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.
- package/.opencode/skills/swarm-pr-feedback/SKILL.md +41 -1
- package/.opencode/skills/swarm-pr-review/SKILL.md +83 -24
- package/README.md +18 -0
- package/dist/background/candidate-contract.d.ts +13 -0
- package/dist/background/workspace-snapshot.d.ts +54 -0
- package/dist/cli/{config-doctor-q0krbxwv.js → config-doctor-b67nb84b.js} +2 -2
- package/dist/cli/{curation-policy-v6ywe8qd.js → curation-policy-kvzhfaj1.js} +2 -2
- package/dist/cli/{curator-llm-factory-mhwje8rf.js → curator-llm-factory-x7kvry9x.js} +17 -14
- package/dist/cli/{curator-tqedvpy1.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-m0m74hvr.js → guardrail-explain-5kd44rcj.js} +18 -15
- package/dist/cli/{guardrail-log-5g8x6qc6.js → guardrail-log-aksrzqdx.js} +3 -3
- package/dist/cli/hashing-mjwn6j4y.js +34 -0
- package/dist/cli/{hive-promoter-s804newd.js → hive-promoter-cy21rhnd.js} +17 -14
- package/dist/cli/{index-n8j0cnp1.js → index-0755132s.js} +4 -4
- package/dist/cli/{index-1kjg590p.js → index-1kn24ja0.js} +1 -1
- package/dist/cli/{index-j08trvny.js → index-21115szq.js} +5 -5
- package/dist/cli/{index-2cv1j3rf.js → index-37v2wxqe.js} +1 -1
- package/dist/cli/{index-cfz5750k.js → index-7ayaq27q.js} +5 -1
- package/dist/cli/{index-9bk16zkv.js → index-84nz7bhe.js} +1 -1
- package/dist/cli/{index-bwfzq91q.js → index-ad6j0m7k.js} +12 -6
- package/dist/cli/{index-qh9466j3.js → index-azghvnja.js} +2 -2
- package/dist/cli/{index-cxq1t7j1.js → index-ckybsn6p.js} +19 -16
- package/dist/cli/{index-cggqh2dz.js → index-kwwxevne.js} +53 -996
- package/dist/cli/{index-nw9cn84t.js → index-m5fw4mer.js} +19 -2
- package/dist/cli/{index-2w4tsmva.js → index-rw3f73aq.js} +1 -1
- package/dist/cli/{index-wpqypdge.js → index-s0rbdp61.js} +2 -2
- package/dist/cli/{index-bbejqagj.js → index-s0vahtdm.js} +1 -1
- package/dist/cli/{index-81dkzncr.js → index-sr2cynyw.js} +4535 -2325
- package/dist/cli/index-wjm896ey.js +1100 -0
- package/dist/cli/{index-w58bmwyq.js → index-wnz282j3.js} +2 -2
- package/dist/cli/{index-y6a7gjtj.js → index-wsdnttf4.js} +228 -81
- package/dist/cli/{index-m1hew17b.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-f1m2xp5x.js → knowledge-escalator-ta3xjrdj.js} +3 -3
- package/dist/cli/{knowledge-events-rksgem6f.js → knowledge-events-dk6banmz.js} +1 -1
- package/dist/cli/{knowledge-store-t6d6fr8b.js → knowledge-store-h3jz10bv.js} +1 -1
- package/dist/cli/{knowledge-validator-5ggzb3j1.js → knowledge-validator-jsz1dxkr.js} +4 -4
- package/dist/cli/runner-2v413r74.js +21 -0
- package/dist/cli/{scan-cursor-18fnwr58.js → scan-cursor-48gwzh9c.js} +2 -2
- package/dist/cli/{schema-jw15d8bt.js → schema-f937b9cs.js} +5 -1
- package/dist/cli/{skill-generator-3n5y902z.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 +355 -320
- 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
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
EvaluationRunV1Schema,
|
|
4
|
+
GateAuditResultV1Schema,
|
|
5
|
+
getProtectedEvaluationRunIds
|
|
6
|
+
} from "./index-z1tm47nj.js";
|
|
2
7
|
import {
|
|
3
8
|
readEffectiveSpecSync
|
|
4
9
|
} from "./index-9ss2m4rs.js";
|
|
@@ -8,9 +13,6 @@ import {
|
|
|
8
13
|
readTaskEvidenceRaw,
|
|
9
14
|
sanitizeTaskId
|
|
10
15
|
} from "./index-mrtms113.js";
|
|
11
|
-
import {
|
|
12
|
-
atomicWriteFile
|
|
13
|
-
} from "./index-ey29aap6.js";
|
|
14
16
|
import {
|
|
15
17
|
EVIDENCE_MAX_JSON_BYTES,
|
|
16
18
|
EvidenceBundleSchema,
|
|
@@ -57,7 +59,6 @@ import {
|
|
|
57
59
|
} from "./index-09b9zncg.js";
|
|
58
60
|
import {
|
|
59
61
|
ZodError,
|
|
60
|
-
exports_external,
|
|
61
62
|
init_zod
|
|
62
63
|
} from "./index-bpmtbmy9.js";
|
|
63
64
|
import {
|
|
@@ -3382,961 +3383,17 @@ init_zod();
|
|
|
3382
3383
|
import {
|
|
3383
3384
|
mkdirSync as mkdirSync4,
|
|
3384
3385
|
readdirSync as readdirSync2,
|
|
3385
|
-
realpathSync
|
|
3386
|
+
realpathSync,
|
|
3386
3387
|
rmSync,
|
|
3387
3388
|
statSync as statSync2
|
|
3388
3389
|
} from "fs";
|
|
3389
3390
|
import * as fs6 from "fs/promises";
|
|
3390
3391
|
import * as os4 from "os";
|
|
3391
|
-
import * as
|
|
3392
|
+
import * as path9 from "path";
|
|
3392
3393
|
|
|
3393
3394
|
// src/evaluation/retention.ts
|
|
3394
3395
|
import * as fs4 from "fs/promises";
|
|
3395
|
-
import * as path11 from "path";
|
|
3396
|
-
|
|
3397
|
-
// src/evaluation/contracts.ts
|
|
3398
|
-
init_zod();
|
|
3399
|
-
var IdentifierSchema = exports_external.string().min(1).max(160).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
|
|
3400
|
-
var Sha256Schema = exports_external.string().regex(/^[a-f0-9]{64}$/);
|
|
3401
|
-
var IsoDateSchema = exports_external.iso.datetime({ offset: true });
|
|
3402
|
-
var RelativePathSchema = exports_external.string().min(1).max(1024).refine((value) => !value.includes("\x00"), "path contains a NUL byte").refine((value) => !/^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(value), {
|
|
3403
|
-
message: "path must be project-relative"
|
|
3404
|
-
}).refine((value) => !value.replace(/\\/g, "/").split("/").some((segment) => segment === ".."), "path must not traverse outside its root");
|
|
3405
|
-
var EvaluationSplitSchema = exports_external.enum(["train", "validation", "test"]);
|
|
3406
|
-
var CostRecordSchema = exports_external.object({
|
|
3407
|
-
source: exports_external.enum(["reported", "estimated", "unavailable"]),
|
|
3408
|
-
usd: exports_external.number().finite().nonnegative().optional()
|
|
3409
|
-
}).strict().superRefine((value, ctx) => {
|
|
3410
|
-
if (value.source === "unavailable" && value.usd !== undefined) {
|
|
3411
|
-
ctx.addIssue({
|
|
3412
|
-
code: "custom",
|
|
3413
|
-
path: ["usd"],
|
|
3414
|
-
message: "unavailable costs cannot declare a USD value"
|
|
3415
|
-
});
|
|
3416
|
-
}
|
|
3417
|
-
if (value.source !== "unavailable" && value.usd === undefined) {
|
|
3418
|
-
ctx.addIssue({
|
|
3419
|
-
code: "custom",
|
|
3420
|
-
path: ["usd"],
|
|
3421
|
-
message: "reported and estimated costs require a USD value"
|
|
3422
|
-
});
|
|
3423
|
-
}
|
|
3424
|
-
});
|
|
3425
|
-
var HumanReviewReceiptSchema = exports_external.object({
|
|
3426
|
-
reviewer: exports_external.string().min(1).max(200),
|
|
3427
|
-
reviewedAt: IsoDateSchema,
|
|
3428
|
-
instruction: exports_external.literal(true),
|
|
3429
|
-
fixture: exports_external.literal(true),
|
|
3430
|
-
scorer: exports_external.literal(true),
|
|
3431
|
-
secretsPrivacy: exports_external.literal(true),
|
|
3432
|
-
license: exports_external.literal(true),
|
|
3433
|
-
split: exports_external.literal(true)
|
|
3434
|
-
}).strict();
|
|
3435
|
-
var EvaluationTaskV1Schema = exports_external.object({
|
|
3436
|
-
v: exports_external.literal(1),
|
|
3437
|
-
id: IdentifierSchema,
|
|
3438
|
-
derivedFromTaskId: IdentifierSchema.optional(),
|
|
3439
|
-
source: exports_external.enum(["curated", "trace-proposal"]),
|
|
3440
|
-
split: EvaluationSplitSchema,
|
|
3441
|
-
category: IdentifierSchema,
|
|
3442
|
-
protected: exports_external.boolean().default(false),
|
|
3443
|
-
instructionPath: RelativePathSchema,
|
|
3444
|
-
environment: exports_external.discriminatedUnion("kind", [
|
|
3445
|
-
exports_external.object({ kind: exports_external.literal("fixture"), path: RelativePathSchema }).strict(),
|
|
3446
|
-
exports_external.object({ kind: exports_external.literal("container"), path: RelativePathSchema }).strict()
|
|
3447
|
-
]),
|
|
3448
|
-
scorer: exports_external.object({
|
|
3449
|
-
kind: exports_external.enum(["builtin", "project"]),
|
|
3450
|
-
argv: exports_external.array(exports_external.string().min(1).max(4096)).min(1).max(64),
|
|
3451
|
-
timeoutMs: exports_external.number().int().min(100).max(600000),
|
|
3452
|
-
scoreRange: exports_external.tuple([exports_external.number().finite(), exports_external.number().finite()]).refine(([minimum, maximum]) => maximum > minimum, {
|
|
3453
|
-
message: "score range maximum must be greater than minimum"
|
|
3454
|
-
})
|
|
3455
|
-
}).strict(),
|
|
3456
|
-
provenance: exports_external.object({
|
|
3457
|
-
origin: exports_external.string().min(1).max(2048),
|
|
3458
|
-
license: exports_external.string().min(1).max(200),
|
|
3459
|
-
collectedAt: IsoDateSchema.optional(),
|
|
3460
|
-
review: HumanReviewReceiptSchema.optional()
|
|
3461
|
-
}).strict(),
|
|
3462
|
-
contentHash: Sha256Schema
|
|
3463
|
-
}).strict().superRefine((task, ctx) => {
|
|
3464
|
-
if (task.derivedFromTaskId === task.id) {
|
|
3465
|
-
ctx.addIssue({
|
|
3466
|
-
code: "custom",
|
|
3467
|
-
path: ["derivedFromTaskId"],
|
|
3468
|
-
message: "a derived task must reference a different parent task"
|
|
3469
|
-
});
|
|
3470
|
-
}
|
|
3471
|
-
if (task.source === "trace-proposal" && !task.provenance.review) {
|
|
3472
|
-
ctx.addIssue({
|
|
3473
|
-
code: "custom",
|
|
3474
|
-
path: ["provenance", "review"],
|
|
3475
|
-
message: "trace-derived tasks require an explicit human review receipt"
|
|
3476
|
-
});
|
|
3477
|
-
}
|
|
3478
|
-
if (task.scorer.kind === "project" && !RelativePathSchema.safeParse(task.scorer.argv[0]).success) {
|
|
3479
|
-
ctx.addIssue({
|
|
3480
|
-
code: "custom",
|
|
3481
|
-
path: ["scorer", "argv", 0],
|
|
3482
|
-
message: "project scorer executable must be project-relative"
|
|
3483
|
-
});
|
|
3484
|
-
}
|
|
3485
|
-
});
|
|
3486
|
-
var EvaluationCandidateV1Schema = exports_external.object({
|
|
3487
|
-
v: exports_external.literal(1),
|
|
3488
|
-
id: IdentifierSchema,
|
|
3489
|
-
kind: exports_external.enum(["baseline", "skill", "harness"]),
|
|
3490
|
-
payloadPath: RelativePathSchema,
|
|
3491
|
-
model: exports_external.string().min(1).max(300),
|
|
3492
|
-
agent: exports_external.string().min(1).max(160).optional(),
|
|
3493
|
-
contentHash: Sha256Schema
|
|
3494
|
-
}).strict();
|
|
3495
|
-
var EvaluationOutcomeSchema = exports_external.enum([
|
|
3496
|
-
"scored",
|
|
3497
|
-
"infrastructure_failure",
|
|
3498
|
-
"timeout",
|
|
3499
|
-
"cancelled",
|
|
3500
|
-
"malformed",
|
|
3501
|
-
"unsupported",
|
|
3502
|
-
"integrity_failure"
|
|
3503
|
-
]);
|
|
3504
|
-
var EvaluationResultV1Schema = exports_external.object({
|
|
3505
|
-
v: exports_external.literal(1),
|
|
3506
|
-
taskId: IdentifierSchema,
|
|
3507
|
-
category: IdentifierSchema,
|
|
3508
|
-
protected: exports_external.boolean().default(false),
|
|
3509
|
-
repetition: exports_external.number().int().nonnegative(),
|
|
3510
|
-
candidateId: IdentifierSchema,
|
|
3511
|
-
seed: exports_external.string().min(1).max(500),
|
|
3512
|
-
outcome: EvaluationOutcomeSchema,
|
|
3513
|
-
score: exports_external.number().finite().optional(),
|
|
3514
|
-
scoreRange: exports_external.tuple([exports_external.number().finite(), exports_external.number().finite()]),
|
|
3515
|
-
cost: CostRecordSchema,
|
|
3516
|
-
durationMs: exports_external.number().int().nonnegative(),
|
|
3517
|
-
retries: exports_external.number().int().nonnegative().default(0),
|
|
3518
|
-
failureCode: exports_external.string().min(1).max(160).optional(),
|
|
3519
|
-
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
3520
|
-
}).strict().superRefine((result, ctx) => {
|
|
3521
|
-
const [minimum, maximum] = result.scoreRange;
|
|
3522
|
-
if (maximum <= minimum) {
|
|
3523
|
-
ctx.addIssue({
|
|
3524
|
-
code: "custom",
|
|
3525
|
-
path: ["scoreRange"],
|
|
3526
|
-
message: "score range maximum must be greater than minimum"
|
|
3527
|
-
});
|
|
3528
|
-
}
|
|
3529
|
-
if (result.outcome === "scored") {
|
|
3530
|
-
if (result.score === undefined || result.score < minimum || result.score > maximum) {
|
|
3531
|
-
ctx.addIssue({
|
|
3532
|
-
code: "custom",
|
|
3533
|
-
path: ["score"],
|
|
3534
|
-
message: "scored outcomes require an in-range score"
|
|
3535
|
-
});
|
|
3536
|
-
}
|
|
3537
|
-
} else if (result.score !== undefined) {
|
|
3538
|
-
ctx.addIssue({
|
|
3539
|
-
code: "custom",
|
|
3540
|
-
path: ["score"],
|
|
3541
|
-
message: "non-score outcomes must not be imputed with a score"
|
|
3542
|
-
});
|
|
3543
|
-
}
|
|
3544
|
-
});
|
|
3545
|
-
var EvaluationBudgetsV1Schema = exports_external.object({
|
|
3546
|
-
maxTasks: exports_external.number().int().positive(),
|
|
3547
|
-
maxRepetitions: exports_external.number().int().positive(),
|
|
3548
|
-
maxConcurrency: exports_external.number().int().positive(),
|
|
3549
|
-
maxTaskTimeMs: exports_external.number().int().positive(),
|
|
3550
|
-
maxRetries: exports_external.number().int().nonnegative(),
|
|
3551
|
-
maxOutputBytes: exports_external.number().int().positive(),
|
|
3552
|
-
maxSpendUsd: exports_external.number().finite().nonnegative().optional()
|
|
3553
|
-
}).strict();
|
|
3554
|
-
var EnvironmentFingerprintV1Schema = exports_external.object({
|
|
3555
|
-
platform: exports_external.string().min(1),
|
|
3556
|
-
arch: exports_external.string().min(1),
|
|
3557
|
-
runtime: exports_external.string().min(1),
|
|
3558
|
-
baseSha: exports_external.string().regex(/^[a-f0-9]{40,64}$/),
|
|
3559
|
-
activeTreeHash: Sha256Schema,
|
|
3560
|
-
taskSetHash: Sha256Schema
|
|
3561
|
-
}).strict();
|
|
3562
|
-
var EvaluationRunV1Schema = exports_external.object({
|
|
3563
|
-
v: exports_external.literal(1),
|
|
3564
|
-
runId: IdentifierSchema,
|
|
3565
|
-
createdAt: IsoDateSchema,
|
|
3566
|
-
status: exports_external.enum(["complete", "inconclusive", "cancelled"]),
|
|
3567
|
-
baseline: EvaluationCandidateV1Schema,
|
|
3568
|
-
candidate: EvaluationCandidateV1Schema,
|
|
3569
|
-
taskSet: exports_external.object({
|
|
3570
|
-
id: IdentifierSchema,
|
|
3571
|
-
contentHash: Sha256Schema,
|
|
3572
|
-
taskIds: exports_external.array(IdentifierSchema).min(1)
|
|
3573
|
-
}).strict(),
|
|
3574
|
-
split: EvaluationSplitSchema,
|
|
3575
|
-
seed: exports_external.string().min(1).max(500),
|
|
3576
|
-
models: exports_external.array(exports_external.string().min(1).max(300)).min(1),
|
|
3577
|
-
environment: EnvironmentFingerprintV1Schema,
|
|
3578
|
-
budgets: EvaluationBudgetsV1Schema,
|
|
3579
|
-
results: exports_external.array(EvaluationResultV1Schema),
|
|
3580
|
-
cost: CostRecordSchema,
|
|
3581
|
-
integrityHash: Sha256Schema
|
|
3582
|
-
}).strict().superRefine((run, ctx) => {
|
|
3583
|
-
if (run.environment.taskSetHash !== run.taskSet.contentHash) {
|
|
3584
|
-
ctx.addIssue({
|
|
3585
|
-
code: "custom",
|
|
3586
|
-
path: ["environment", "taskSetHash"],
|
|
3587
|
-
message: "environment and task-set hashes must agree"
|
|
3588
|
-
});
|
|
3589
|
-
}
|
|
3590
|
-
const allowed = new Set([run.baseline.id, run.candidate.id]);
|
|
3591
|
-
if (allowed.size !== 2) {
|
|
3592
|
-
ctx.addIssue({
|
|
3593
|
-
code: "custom",
|
|
3594
|
-
path: ["candidate", "id"],
|
|
3595
|
-
message: "baseline and candidate ids must differ"
|
|
3596
|
-
});
|
|
3597
|
-
}
|
|
3598
|
-
if (run.results.some((result) => !allowed.has(result.candidateId))) {
|
|
3599
|
-
ctx.addIssue({
|
|
3600
|
-
code: "custom",
|
|
3601
|
-
path: ["results"],
|
|
3602
|
-
message: "results may only reference the declared baseline and candidate"
|
|
3603
|
-
});
|
|
3604
|
-
}
|
|
3605
|
-
const taskIds = new Set(run.taskSet.taskIds);
|
|
3606
|
-
if (run.results.some((result) => !taskIds.has(result.taskId))) {
|
|
3607
|
-
ctx.addIssue({
|
|
3608
|
-
code: "custom",
|
|
3609
|
-
path: ["results"],
|
|
3610
|
-
message: "results may only reference tasks in the frozen task set"
|
|
3611
|
-
});
|
|
3612
|
-
}
|
|
3613
|
-
const resultKeys = run.results.map((result) => `${result.candidateId}\x00${result.taskId}\x00${result.repetition}`);
|
|
3614
|
-
if (new Set(resultKeys).size !== resultKeys.length) {
|
|
3615
|
-
ctx.addIssue({
|
|
3616
|
-
code: "custom",
|
|
3617
|
-
path: ["results"],
|
|
3618
|
-
message: "run contains duplicate candidate/task/repetition results"
|
|
3619
|
-
});
|
|
3620
|
-
}
|
|
3621
|
-
if (run.results.some((result) => result.repetition >= run.budgets.maxRepetitions)) {
|
|
3622
|
-
ctx.addIssue({
|
|
3623
|
-
code: "custom",
|
|
3624
|
-
path: ["results"],
|
|
3625
|
-
message: "result repetition exceeds the declared run budget"
|
|
3626
|
-
});
|
|
3627
|
-
}
|
|
3628
|
-
});
|
|
3629
|
-
var PromotionPolicyV1Schema = exports_external.object({
|
|
3630
|
-
v: exports_external.literal(1),
|
|
3631
|
-
minValidPairs: exports_external.number().int().positive().default(6),
|
|
3632
|
-
minCoverage: exports_external.number().min(0).max(1).default(0.8),
|
|
3633
|
-
deadband: exports_external.number().min(0).max(1).default(0),
|
|
3634
|
-
confidence: exports_external.literal(0.95).default(0.95),
|
|
3635
|
-
bootstrapResamples: exports_external.literal(1e4).default(1e4),
|
|
3636
|
-
requireAvailableCosts: exports_external.boolean().default(true),
|
|
3637
|
-
protectedCategoryTolerances: exports_external.record(exports_external.string(), exports_external.number().min(0).max(1)).default({})
|
|
3638
|
-
}).strict();
|
|
3639
|
-
var PromotionComparisonSchema = exports_external.object({
|
|
3640
|
-
baselineRunId: IdentifierSchema,
|
|
3641
|
-
baselineCandidateId: IdentifierSchema,
|
|
3642
|
-
baselineMean: exports_external.number().min(0).max(1),
|
|
3643
|
-
candidateMean: exports_external.number().min(0).max(1),
|
|
3644
|
-
pairedDelta: exports_external.number().min(-1).max(1),
|
|
3645
|
-
confidenceInterval: exports_external.tuple([
|
|
3646
|
-
exports_external.number().min(-1).max(1),
|
|
3647
|
-
exports_external.number().min(-1).max(1)
|
|
3648
|
-
]),
|
|
3649
|
-
validPairs: exports_external.number().int().nonnegative(),
|
|
3650
|
-
missingPairs: exports_external.number().int().nonnegative(),
|
|
3651
|
-
coverage: exports_external.number().min(0).max(1)
|
|
3652
|
-
}).strict();
|
|
3653
|
-
var PromotionDecisionV1Schema = exports_external.object({
|
|
3654
|
-
v: exports_external.literal(1),
|
|
3655
|
-
decisionId: IdentifierSchema,
|
|
3656
|
-
runId: IdentifierSchema,
|
|
3657
|
-
decidedAt: IsoDateSchema,
|
|
3658
|
-
status: exports_external.enum(["accept", "reject", "inconclusive"]),
|
|
3659
|
-
reasons: exports_external.array(exports_external.string().min(1).max(300)),
|
|
3660
|
-
baseline: PromotionComparisonSchema,
|
|
3661
|
-
historicalBest: PromotionComparisonSchema,
|
|
3662
|
-
deadband: exports_external.number().min(0).max(1),
|
|
3663
|
-
bootstrap: exports_external.object({
|
|
3664
|
-
resamples: exports_external.literal(1e4),
|
|
3665
|
-
confidence: exports_external.literal(0.95),
|
|
3666
|
-
seedHash: Sha256Schema
|
|
3667
|
-
}).strict(),
|
|
3668
|
-
categories: exports_external.array(exports_external.object({
|
|
3669
|
-
category: IdentifierSchema,
|
|
3670
|
-
protected: exports_external.boolean(),
|
|
3671
|
-
tolerance: exports_external.number().min(0).max(1),
|
|
3672
|
-
baselineMean: exports_external.number().min(0).max(1),
|
|
3673
|
-
candidateMean: exports_external.number().min(0).max(1),
|
|
3674
|
-
pairedDelta: exports_external.number().min(-1).max(1),
|
|
3675
|
-
regression: exports_external.boolean()
|
|
3676
|
-
}).strict()),
|
|
3677
|
-
lineage: exports_external.object({
|
|
3678
|
-
baselineRunId: IdentifierSchema,
|
|
3679
|
-
historicalBestRunId: IdentifierSchema,
|
|
3680
|
-
taskSetHash: Sha256Schema,
|
|
3681
|
-
baselineHash: Sha256Schema,
|
|
3682
|
-
candidateHash: Sha256Schema,
|
|
3683
|
-
historicalBestHash: Sha256Schema
|
|
3684
|
-
}).strict(),
|
|
3685
|
-
policyHash: Sha256Schema,
|
|
3686
|
-
unavailableQualityMetrics: exports_external.array(exports_external.enum(["complexity_delta", "public_api_delta"])).default(["complexity_delta", "public_api_delta"])
|
|
3687
|
-
}).strict();
|
|
3688
|
-
var GateNameSchema = exports_external.enum([
|
|
3689
|
-
"reviewer",
|
|
3690
|
-
"test-engineer",
|
|
3691
|
-
"sast",
|
|
3692
|
-
"mutation",
|
|
3693
|
-
"quality"
|
|
3694
|
-
]);
|
|
3695
|
-
var GateAuditManifestV1Schema = exports_external.object({
|
|
3696
|
-
v: exports_external.literal(1),
|
|
3697
|
-
id: IdentifierSchema,
|
|
3698
|
-
createdAt: IsoDateSchema,
|
|
3699
|
-
taskIds: exports_external.array(IdentifierSchema).min(1),
|
|
3700
|
-
gates: exports_external.array(GateNameSchema).min(1),
|
|
3701
|
-
models: exports_external.array(exports_external.string().min(1).max(300)).min(1),
|
|
3702
|
-
preferredSwarm: IdentifierSchema.optional(),
|
|
3703
|
-
repetitions: exports_external.number().int().positive().max(100),
|
|
3704
|
-
seed: exports_external.string().min(1).max(500),
|
|
3705
|
-
maxConcurrency: exports_external.number().int().positive().max(64),
|
|
3706
|
-
maxRetries: exports_external.number().int().nonnegative().max(20),
|
|
3707
|
-
maxTimeMs: exports_external.number().int().positive(),
|
|
3708
|
-
maxCostUsd: exports_external.number().finite().nonnegative().optional(),
|
|
3709
|
-
contentHash: Sha256Schema
|
|
3710
|
-
}).strict();
|
|
3711
|
-
var GateAuditCellV1Schema = exports_external.object({
|
|
3712
|
-
v: exports_external.literal(1),
|
|
3713
|
-
taskId: IdentifierSchema,
|
|
3714
|
-
candidateId: IdentifierSchema.optional(),
|
|
3715
|
-
defectType: IdentifierSchema,
|
|
3716
|
-
gate: GateNameSchema,
|
|
3717
|
-
model: exports_external.string().min(1).max(300),
|
|
3718
|
-
repetition: exports_external.number().int().nonnegative(),
|
|
3719
|
-
outcome: exports_external.enum([
|
|
3720
|
-
"caught",
|
|
3721
|
-
"missed",
|
|
3722
|
-
"false_rejection",
|
|
3723
|
-
"unsupported",
|
|
3724
|
-
"infrastructure_failure"
|
|
3725
|
-
]),
|
|
3726
|
-
retries: exports_external.number().int().nonnegative(),
|
|
3727
|
-
cost: CostRecordSchema,
|
|
3728
|
-
durationMs: exports_external.number().int().nonnegative(),
|
|
3729
|
-
failureClassification: exports_external.enum([
|
|
3730
|
-
"clean",
|
|
3731
|
-
"pre_existing_failure",
|
|
3732
|
-
"new_regression",
|
|
3733
|
-
"flaky_failure",
|
|
3734
|
-
"infrastructure_failure",
|
|
3735
|
-
"unknown_failure"
|
|
3736
|
-
]).optional(),
|
|
3737
|
-
failureCode: exports_external.string().min(1).max(160).optional()
|
|
3738
|
-
}).strict();
|
|
3739
|
-
var GateAuditResultV1Schema = exports_external.object({
|
|
3740
|
-
v: exports_external.literal(1),
|
|
3741
|
-
runId: IdentifierSchema,
|
|
3742
|
-
manifestHash: Sha256Schema,
|
|
3743
|
-
createdAt: IsoDateSchema,
|
|
3744
|
-
status: exports_external.enum(["complete", "inconclusive", "cancelled"]),
|
|
3745
|
-
cells: exports_external.array(GateAuditCellV1Schema),
|
|
3746
|
-
cost: CostRecordSchema,
|
|
3747
|
-
qualityMetricAvailability: exports_external.object({
|
|
3748
|
-
complexity_delta: exports_external.literal("unavailable"),
|
|
3749
|
-
public_api_delta: exports_external.literal("unavailable")
|
|
3750
|
-
}).strict()
|
|
3751
|
-
}).strict();
|
|
3752
|
-
var TestConsumptionClaimV1Schema = exports_external.object({
|
|
3753
|
-
v: exports_external.literal(1),
|
|
3754
|
-
runId: IdentifierSchema,
|
|
3755
|
-
taskSetHash: Sha256Schema,
|
|
3756
|
-
baselineHash: Sha256Schema,
|
|
3757
|
-
candidateHash: Sha256Schema,
|
|
3758
|
-
claimedAt: IsoDateSchema
|
|
3759
|
-
}).strict();
|
|
3760
|
-
var TaskSetSnapshotV1Schema = exports_external.object({
|
|
3761
|
-
v: exports_external.literal(1),
|
|
3762
|
-
id: IdentifierSchema,
|
|
3763
|
-
split: EvaluationSplitSchema,
|
|
3764
|
-
tasks: exports_external.array(EvaluationTaskV1Schema).min(1),
|
|
3765
|
-
contentHash: Sha256Schema,
|
|
3766
|
-
createdAt: IsoDateSchema
|
|
3767
|
-
}).strict();
|
|
3768
|
-
|
|
3769
|
-
// src/evaluation/store.ts
|
|
3770
|
-
import { mkdir as mkdir2, readdir, readFile as readFile3 } from "fs/promises";
|
|
3771
|
-
import * as path10 from "path";
|
|
3772
|
-
|
|
3773
|
-
// src/evidence/immutable-store.ts
|
|
3774
|
-
import { mkdir, readFile } from "fs/promises";
|
|
3775
3396
|
import * as path8 from "path";
|
|
3776
|
-
async function readOptionalFile(filePath) {
|
|
3777
|
-
try {
|
|
3778
|
-
return await readFile(filePath, "utf8");
|
|
3779
|
-
} catch (error2) {
|
|
3780
|
-
if (error2.code === "ENOENT")
|
|
3781
|
-
return;
|
|
3782
|
-
throw error2;
|
|
3783
|
-
}
|
|
3784
|
-
}
|
|
3785
|
-
async function writeImmutableArtifact(options) {
|
|
3786
|
-
await mkdir(path8.dirname(options.filePath), { recursive: true });
|
|
3787
|
-
return withEvidenceLock(options.directory, options.relativeLockPath, options.agent, options.taskId, async () => {
|
|
3788
|
-
const desired = options.serialize(options.value);
|
|
3789
|
-
const existing = await readOptionalFile(options.filePath);
|
|
3790
|
-
if (existing !== undefined) {
|
|
3791
|
-
let parsed;
|
|
3792
|
-
try {
|
|
3793
|
-
parsed = options.parse(JSON.parse(existing));
|
|
3794
|
-
} catch (error2) {
|
|
3795
|
-
throw options.conflictError({
|
|
3796
|
-
kind: "corrupt",
|
|
3797
|
-
filePath: options.filePath,
|
|
3798
|
-
cause: error2
|
|
3799
|
-
});
|
|
3800
|
-
}
|
|
3801
|
-
if (options.serialize(parsed) === desired || options.isEquivalent?.(parsed, options.value)) {
|
|
3802
|
-
return parsed;
|
|
3803
|
-
}
|
|
3804
|
-
throw options.conflictError({
|
|
3805
|
-
kind: "divergent",
|
|
3806
|
-
filePath: options.filePath
|
|
3807
|
-
});
|
|
3808
|
-
}
|
|
3809
|
-
await atomicWriteFile(options.filePath, desired);
|
|
3810
|
-
return options.value;
|
|
3811
|
-
});
|
|
3812
|
-
}
|
|
3813
|
-
|
|
3814
|
-
// src/evaluation/hashing.ts
|
|
3815
|
-
import { createHash as createHash2 } from "crypto";
|
|
3816
|
-
import { lstatSync, realpathSync } from "fs";
|
|
3817
|
-
import { lstat, opendir, readFile as readFile2, realpath } from "fs/promises";
|
|
3818
|
-
import * as path9 from "path";
|
|
3819
|
-
function canonicalize(value, ancestors) {
|
|
3820
|
-
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
3821
|
-
return value;
|
|
3822
|
-
}
|
|
3823
|
-
if (typeof value === "number") {
|
|
3824
|
-
if (!Number.isFinite(value))
|
|
3825
|
-
throw new TypeError("canonical JSON rejects non-finite numbers");
|
|
3826
|
-
return Object.is(value, -0) ? 0 : value;
|
|
3827
|
-
}
|
|
3828
|
-
if (value === undefined)
|
|
3829
|
-
return;
|
|
3830
|
-
if (typeof value !== "object") {
|
|
3831
|
-
throw new TypeError(`canonical JSON cannot encode ${typeof value}`);
|
|
3832
|
-
}
|
|
3833
|
-
if (ancestors.has(value))
|
|
3834
|
-
throw new TypeError("canonical JSON rejects cyclic values");
|
|
3835
|
-
ancestors.add(value);
|
|
3836
|
-
try {
|
|
3837
|
-
if (Array.isArray(value)) {
|
|
3838
|
-
return value.map((entry) => {
|
|
3839
|
-
const encoded = canonicalize(entry, ancestors);
|
|
3840
|
-
return encoded === undefined ? null : encoded;
|
|
3841
|
-
});
|
|
3842
|
-
}
|
|
3843
|
-
const record = value;
|
|
3844
|
-
const result = {};
|
|
3845
|
-
for (const key of Object.keys(record).sort()) {
|
|
3846
|
-
const encoded = canonicalize(record[key], ancestors);
|
|
3847
|
-
if (encoded !== undefined)
|
|
3848
|
-
result[key] = encoded;
|
|
3849
|
-
}
|
|
3850
|
-
return result;
|
|
3851
|
-
} finally {
|
|
3852
|
-
ancestors.delete(value);
|
|
3853
|
-
}
|
|
3854
|
-
}
|
|
3855
|
-
function canonicalJson(value) {
|
|
3856
|
-
return JSON.stringify(canonicalize(value, new Set));
|
|
3857
|
-
}
|
|
3858
|
-
function sha256(value) {
|
|
3859
|
-
return createHash2("sha256").update(value).digest("hex");
|
|
3860
|
-
}
|
|
3861
|
-
function canonicalHash(value) {
|
|
3862
|
-
return sha256(canonicalJson(value));
|
|
3863
|
-
}
|
|
3864
|
-
function contentHashWithout(value, keys) {
|
|
3865
|
-
const copy = { ...value };
|
|
3866
|
-
for (const key of keys)
|
|
3867
|
-
delete copy[key];
|
|
3868
|
-
return canonicalHash(copy);
|
|
3869
|
-
}
|
|
3870
|
-
function createHashBudget(limits) {
|
|
3871
|
-
return { entries: 0, bytes: 0, limits };
|
|
3872
|
-
}
|
|
3873
|
-
function chargeHashEntry(budget) {
|
|
3874
|
-
budget.entries++;
|
|
3875
|
-
if (budget.entries > budget.limits.maxFiles) {
|
|
3876
|
-
throw new Error("evaluation input exceeds the canonical hashing budget");
|
|
3877
|
-
}
|
|
3878
|
-
}
|
|
3879
|
-
async function closeDirectory(directory) {
|
|
3880
|
-
try {
|
|
3881
|
-
await directory.close();
|
|
3882
|
-
} catch (error2) {
|
|
3883
|
-
if (typeof error2 !== "object" || error2 === null || !("code" in error2) || error2.code !== "ERR_DIR_CLOSED") {
|
|
3884
|
-
throw error2;
|
|
3885
|
-
}
|
|
3886
|
-
}
|
|
3887
|
-
}
|
|
3888
|
-
function resolveTaskInputHashLimits(limits) {
|
|
3889
|
-
const bounded = {
|
|
3890
|
-
maxFiles: limits.maxFiles ?? 5000,
|
|
3891
|
-
maxBytes: limits.maxBytes ?? 50 * 1024 * 1024
|
|
3892
|
-
};
|
|
3893
|
-
if (!Number.isSafeInteger(bounded.maxFiles) || bounded.maxFiles <= 0 || !Number.isSafeInteger(bounded.maxBytes) || bounded.maxBytes <= 0) {
|
|
3894
|
-
throw new Error("task input hash limits must be positive safe integers");
|
|
3895
|
-
}
|
|
3896
|
-
return bounded;
|
|
3897
|
-
}
|
|
3898
|
-
async function hashTree(projectRoot, relativePath, budget) {
|
|
3899
|
-
const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
|
|
3900
|
-
const canonicalRoot = await _internals9.realpath(projectRoot);
|
|
3901
|
-
const entries = [];
|
|
3902
|
-
const visit = async (absolutePath, entryAlreadyCharged = false) => {
|
|
3903
|
-
const stat = await _internals9.lstat(absolutePath);
|
|
3904
|
-
if (stat.isSymbolicLink()) {
|
|
3905
|
-
throw new Error(`evaluation input contains a symlink or reparse point: ${relativePath}`);
|
|
3906
|
-
}
|
|
3907
|
-
if (!entryAlreadyCharged)
|
|
3908
|
-
chargeHashEntry(budget);
|
|
3909
|
-
const relative2 = path9.relative(canonicalRoot, absolutePath).replace(/\\/g, "/");
|
|
3910
|
-
if (stat.isDirectory()) {
|
|
3911
|
-
entries.push({ path: relative2, type: "directory" });
|
|
3912
|
-
const children = [];
|
|
3913
|
-
const directory = await _internals9.opendir(absolutePath);
|
|
3914
|
-
try {
|
|
3915
|
-
while (true) {
|
|
3916
|
-
const child = await directory.read();
|
|
3917
|
-
if (!child)
|
|
3918
|
-
break;
|
|
3919
|
-
chargeHashEntry(budget);
|
|
3920
|
-
children.push(child.name);
|
|
3921
|
-
}
|
|
3922
|
-
} finally {
|
|
3923
|
-
await closeDirectory(directory);
|
|
3924
|
-
}
|
|
3925
|
-
for (const child of children.sort()) {
|
|
3926
|
-
await visit(path9.join(absolutePath, child), true);
|
|
3927
|
-
}
|
|
3928
|
-
return;
|
|
3929
|
-
}
|
|
3930
|
-
if (!stat.isFile()) {
|
|
3931
|
-
throw new Error(`evaluation input must contain only regular files: ${relative2}`);
|
|
3932
|
-
}
|
|
3933
|
-
budget.bytes += stat.size;
|
|
3934
|
-
if (budget.bytes > budget.limits.maxBytes) {
|
|
3935
|
-
throw new Error("evaluation input exceeds the canonical hashing budget");
|
|
3936
|
-
}
|
|
3937
|
-
const content = await _internals9.readFile(absolutePath);
|
|
3938
|
-
entries.push({
|
|
3939
|
-
path: relative2,
|
|
3940
|
-
type: "file",
|
|
3941
|
-
bytes: content.byteLength,
|
|
3942
|
-
sha256: sha256(content)
|
|
3943
|
-
});
|
|
3944
|
-
};
|
|
3945
|
-
await visit(target);
|
|
3946
|
-
return entries;
|
|
3947
|
-
}
|
|
3948
|
-
async function computeTaskInputContentHash(projectRoot, task, limits = {}) {
|
|
3949
|
-
const bounded = resolveTaskInputHashLimits(limits);
|
|
3950
|
-
const budget = createHashBudget(bounded);
|
|
3951
|
-
const inputTrees = [
|
|
3952
|
-
...await hashTree(projectRoot, task.instructionPath, budget),
|
|
3953
|
-
...await hashTree(projectRoot, task.environment.path, budget)
|
|
3954
|
-
];
|
|
3955
|
-
if (task.scorer.kind === "project" && task.scorer.argv[0]) {
|
|
3956
|
-
inputTrees.push(...await hashTree(projectRoot, task.scorer.argv[0], budget));
|
|
3957
|
-
}
|
|
3958
|
-
return canonicalHash({
|
|
3959
|
-
task: JSON.parse(canonicalJson({ ...task, contentHash: undefined })),
|
|
3960
|
-
inputs: inputTrees.sort((left, right) => `${left.path}\x00${left.type}`.localeCompare(`${right.path}\x00${right.type}`))
|
|
3961
|
-
});
|
|
3962
|
-
}
|
|
3963
|
-
async function computeTaskLineageInputHash(projectRoot, task, limits = {}) {
|
|
3964
|
-
const bounded = resolveTaskInputHashLimits(limits);
|
|
3965
|
-
const budget = createHashBudget(bounded);
|
|
3966
|
-
const normalizedTree = async (relativePath) => {
|
|
3967
|
-
const canonicalRoot = await _internals9.realpath(projectRoot);
|
|
3968
|
-
const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
|
|
3969
|
-
const targetRelative = path9.relative(canonicalRoot, target).replace(/\\/g, "/");
|
|
3970
|
-
return (await hashTree(projectRoot, relativePath, budget)).map((entry) => ({
|
|
3971
|
-
...entry,
|
|
3972
|
-
path: path9.posix.relative(targetRelative, entry.path) || "."
|
|
3973
|
-
}));
|
|
3974
|
-
};
|
|
3975
|
-
const scorerArgv = [...task.scorer.argv];
|
|
3976
|
-
const projectScorer = task.scorer.kind === "project" && scorerArgv[0] ? await normalizedTree(scorerArgv[0]) : undefined;
|
|
3977
|
-
if (projectScorer)
|
|
3978
|
-
scorerArgv[0] = "<project-scorer>";
|
|
3979
|
-
return canonicalHash({
|
|
3980
|
-
instruction: await normalizedTree(task.instructionPath),
|
|
3981
|
-
environment: {
|
|
3982
|
-
kind: task.environment.kind,
|
|
3983
|
-
tree: await normalizedTree(task.environment.path)
|
|
3984
|
-
},
|
|
3985
|
-
scorer: {
|
|
3986
|
-
kind: task.scorer.kind,
|
|
3987
|
-
argv: scorerArgv,
|
|
3988
|
-
timeoutMs: task.scorer.timeoutMs,
|
|
3989
|
-
scoreRange: task.scorer.scoreRange,
|
|
3990
|
-
projectScorer
|
|
3991
|
-
}
|
|
3992
|
-
});
|
|
3993
|
-
}
|
|
3994
|
-
function computeTaskSetContentHash(snapshot) {
|
|
3995
|
-
return contentHashWithout(snapshot, ["contentHash", "createdAt"]);
|
|
3996
|
-
}
|
|
3997
|
-
function computeManifestContentHash(manifest) {
|
|
3998
|
-
return contentHashWithout(manifest, ["contentHash"]);
|
|
3999
|
-
}
|
|
4000
|
-
function isWithin(root, candidate) {
|
|
4001
|
-
const relative2 = path9.relative(root, candidate);
|
|
4002
|
-
return relative2 === "" || !relative2.startsWith("..") && !path9.isAbsolute(relative2);
|
|
4003
|
-
}
|
|
4004
|
-
function resolveContainedExistingPath(root, relativePath) {
|
|
4005
|
-
if (path9.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
|
|
4006
|
-
throw new Error("evaluation path must be a non-NUL relative path");
|
|
4007
|
-
}
|
|
4008
|
-
const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4009
|
-
if (segments.length === 0 || segments.some((segment) => segment === "..")) {
|
|
4010
|
-
throw new Error("evaluation path traversal is not allowed");
|
|
4011
|
-
}
|
|
4012
|
-
const canonicalRoot = realpathSync(root);
|
|
4013
|
-
let cursor = canonicalRoot;
|
|
4014
|
-
for (const segment of segments) {
|
|
4015
|
-
if (segment === ".")
|
|
4016
|
-
continue;
|
|
4017
|
-
cursor = path9.join(cursor, segment);
|
|
4018
|
-
const stat = lstatSync(cursor);
|
|
4019
|
-
if (stat.isSymbolicLink()) {
|
|
4020
|
-
throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
|
|
4021
|
-
}
|
|
4022
|
-
}
|
|
4023
|
-
const canonicalCandidate = realpathSync(cursor);
|
|
4024
|
-
if (!isWithin(canonicalRoot, canonicalCandidate)) {
|
|
4025
|
-
throw new Error("evaluation path escapes its admitted root");
|
|
4026
|
-
}
|
|
4027
|
-
return canonicalCandidate;
|
|
4028
|
-
}
|
|
4029
|
-
async function resolveContainedExistingPathAsync(root, relativePath) {
|
|
4030
|
-
if (path9.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
|
|
4031
|
-
throw new Error("evaluation path must be a non-NUL relative path");
|
|
4032
|
-
}
|
|
4033
|
-
const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
|
|
4034
|
-
if (segments.length === 0 || segments.some((segment) => segment === "..")) {
|
|
4035
|
-
throw new Error("evaluation path traversal is not allowed");
|
|
4036
|
-
}
|
|
4037
|
-
const canonicalRoot = await _internals9.realpath(root);
|
|
4038
|
-
let cursor = canonicalRoot;
|
|
4039
|
-
for (const segment of segments) {
|
|
4040
|
-
if (segment === ".")
|
|
4041
|
-
continue;
|
|
4042
|
-
cursor = path9.join(cursor, segment);
|
|
4043
|
-
const stat = await _internals9.lstat(cursor);
|
|
4044
|
-
if (stat.isSymbolicLink()) {
|
|
4045
|
-
throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
|
|
4046
|
-
}
|
|
4047
|
-
}
|
|
4048
|
-
const canonicalCandidate = await _internals9.realpath(cursor);
|
|
4049
|
-
if (!isWithin(canonicalRoot, canonicalCandidate)) {
|
|
4050
|
-
throw new Error("evaluation path escapes its admitted root");
|
|
4051
|
-
}
|
|
4052
|
-
return canonicalCandidate;
|
|
4053
|
-
}
|
|
4054
|
-
var _internals9 = {
|
|
4055
|
-
lstat,
|
|
4056
|
-
opendir,
|
|
4057
|
-
readFile: readFile2,
|
|
4058
|
-
realpath
|
|
4059
|
-
};
|
|
4060
|
-
|
|
4061
|
-
// src/evaluation/store.ts
|
|
4062
|
-
var AGENT = "evaluation-store";
|
|
4063
|
-
var TEST_LEDGER_RELATIVE = path10.join("evolution", "test-consumption.jsonl");
|
|
4064
|
-
var TASK_SPLIT_REGISTRY_RELATIVE = path10.join("evolution", "tasks", "split-registry.json");
|
|
4065
|
-
|
|
4066
|
-
class EvaluationConflictError extends Error {
|
|
4067
|
-
constructor(message) {
|
|
4068
|
-
super(message);
|
|
4069
|
-
this.name = "EvaluationConflictError";
|
|
4070
|
-
}
|
|
4071
|
-
}
|
|
4072
|
-
function swarmPath(directory, ...segments) {
|
|
4073
|
-
return path10.join(directory, ".swarm", ...segments);
|
|
4074
|
-
}
|
|
4075
|
-
function serialized(value) {
|
|
4076
|
-
return `${canonicalJson(value)}
|
|
4077
|
-
`;
|
|
4078
|
-
}
|
|
4079
|
-
function parseTaskSplitRegistry(content) {
|
|
4080
|
-
if (content === undefined)
|
|
4081
|
-
return { v: 1, entries: [] };
|
|
4082
|
-
let value;
|
|
4083
|
-
try {
|
|
4084
|
-
value = JSON.parse(content);
|
|
4085
|
-
} catch (error2) {
|
|
4086
|
-
throw new EvaluationConflictError(`task split registry is corrupt: ${String(error2)}`);
|
|
4087
|
-
}
|
|
4088
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
4089
|
-
throw new EvaluationConflictError("task split registry is corrupt");
|
|
4090
|
-
}
|
|
4091
|
-
const record = value;
|
|
4092
|
-
if (record.v !== 1 || !Array.isArray(record.entries)) {
|
|
4093
|
-
throw new EvaluationConflictError("task split registry is corrupt");
|
|
4094
|
-
}
|
|
4095
|
-
const entries = record.entries.map((candidate) => {
|
|
4096
|
-
if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
|
|
4097
|
-
throw new EvaluationConflictError("task split registry is corrupt");
|
|
4098
|
-
}
|
|
4099
|
-
const entry = candidate;
|
|
4100
|
-
if (typeof entry.lineageHash !== "string" || !["train", "validation", "test"].includes(String(entry.split)) || !Array.isArray(entry.taskIds) || !entry.taskIds.every((id) => typeof id === "string") || !Array.isArray(entry.inputHashes) || !entry.inputHashes.every((hash) => typeof hash === "string")) {
|
|
4101
|
-
throw new EvaluationConflictError("task split registry is corrupt");
|
|
4102
|
-
}
|
|
4103
|
-
return {
|
|
4104
|
-
lineageHash: entry.lineageHash,
|
|
4105
|
-
split: entry.split,
|
|
4106
|
-
taskIds: [...new Set(entry.taskIds)].sort(),
|
|
4107
|
-
inputHashes: [...new Set(entry.inputHashes)].sort()
|
|
4108
|
-
};
|
|
4109
|
-
});
|
|
4110
|
-
return { v: 1, entries };
|
|
4111
|
-
}
|
|
4112
|
-
function evaluationConflictError(conflict) {
|
|
4113
|
-
return conflict.kind === "corrupt" ? new EvaluationConflictError(`existing evaluation artifact is corrupt: ${conflict.filePath}: ${String(conflict.cause)}`) : new EvaluationConflictError(`immutable evaluation artifact conflicts with existing content: ${conflict.filePath}`);
|
|
4114
|
-
}
|
|
4115
|
-
function writeImmutable(options) {
|
|
4116
|
-
return writeImmutableArtifact({
|
|
4117
|
-
...options,
|
|
4118
|
-
agent: AGENT,
|
|
4119
|
-
serialize: serialized,
|
|
4120
|
-
conflictError: evaluationConflictError
|
|
4121
|
-
});
|
|
4122
|
-
}
|
|
4123
|
-
function validateTaskInputs(directory, task) {
|
|
4124
|
-
resolveContainedExistingPath(directory, task.instructionPath);
|
|
4125
|
-
const environmentRoot = resolveContainedExistingPath(directory, task.environment.path);
|
|
4126
|
-
if (task.scorer.kind === "project") {
|
|
4127
|
-
const executable = task.scorer.argv[0];
|
|
4128
|
-
if (!executable)
|
|
4129
|
-
throw new Error("project scorer requires an executable");
|
|
4130
|
-
const scorerPath = resolveContainedExistingPath(directory, executable);
|
|
4131
|
-
const relative3 = path10.relative(environmentRoot, scorerPath);
|
|
4132
|
-
if (!relative3 || relative3 === ".." || relative3.startsWith(`..${path10.sep}`) || path10.isAbsolute(relative3)) {
|
|
4133
|
-
throw new Error("project scorer executable must be inside the admitted task environment");
|
|
4134
|
-
}
|
|
4135
|
-
}
|
|
4136
|
-
}
|
|
4137
|
-
async function admitEvaluationTask(directory, input, inputRoot = directory) {
|
|
4138
|
-
const task = EvaluationTaskV1Schema.parse(input);
|
|
4139
|
-
if (await computeTaskInputContentHash(inputRoot, task) !== task.contentHash) {
|
|
4140
|
-
throw new Error(`task ${task.id} content hash does not match its canonical inputs`);
|
|
4141
|
-
}
|
|
4142
|
-
validateTaskInputs(inputRoot, task);
|
|
4143
|
-
const lineageInputHash = await computeTaskLineageInputHash(inputRoot, task);
|
|
4144
|
-
const relative3 = path10.join("evolution", "tasks", task.split, `${task.id}.json`);
|
|
4145
|
-
const admissionLock = TASK_SPLIT_REGISTRY_RELATIVE;
|
|
4146
|
-
const filePath = swarmPath(directory, relative3);
|
|
4147
|
-
await mkdir2(path10.dirname(filePath), { recursive: true });
|
|
4148
|
-
return withEvidenceLock(directory, admissionLock, AGENT, task.id, async () => {
|
|
4149
|
-
const registryPath = swarmPath(directory, TASK_SPLIT_REGISTRY_RELATIVE);
|
|
4150
|
-
const registry = parseTaskSplitRegistry(await readOptionalFile(registryPath));
|
|
4151
|
-
const existingTaskEntry = registry.entries.find((entry) => entry.taskIds.includes(task.id));
|
|
4152
|
-
if (existingTaskEntry && existingTaskEntry.split !== task.split) {
|
|
4153
|
-
throw new EvaluationConflictError(`task ${task.id} is already admitted to a different split`);
|
|
4154
|
-
}
|
|
4155
|
-
const parentEntry = task.derivedFromTaskId ? registry.entries.find((entry) => entry.taskIds.includes(task.derivedFromTaskId)) : undefined;
|
|
4156
|
-
if (task.derivedFromTaskId && !parentEntry) {
|
|
4157
|
-
throw new EvaluationConflictError(`derived task ${task.id} references an unadmitted parent ${task.derivedFromTaskId}`);
|
|
4158
|
-
}
|
|
4159
|
-
const lineageEntry = parentEntry ?? registry.entries.find((entry) => entry.lineageHash === lineageInputHash || entry.inputHashes.includes(lineageInputHash));
|
|
4160
|
-
if (lineageEntry && lineageEntry.split !== task.split) {
|
|
4161
|
-
throw new EvaluationConflictError(`task ${task.id} lineage is already admitted to ${lineageEntry.split}; derived and aliased tasks must inherit that split`);
|
|
4162
|
-
}
|
|
4163
|
-
for (const split of ["train", "validation", "test"]) {
|
|
4164
|
-
if (split === task.split)
|
|
4165
|
-
continue;
|
|
4166
|
-
if (await readOptionalFile(swarmPath(directory, "evolution", "tasks", split, `${task.id}.json`))) {
|
|
4167
|
-
throw new EvaluationConflictError(`task ${task.id} is already admitted to a different split`);
|
|
4168
|
-
}
|
|
4169
|
-
}
|
|
4170
|
-
const desired = serialized(task);
|
|
4171
|
-
const existing = await readOptionalFile(filePath);
|
|
4172
|
-
if (existing !== undefined) {
|
|
4173
|
-
const parsed = EvaluationTaskV1Schema.parse(JSON.parse(existing));
|
|
4174
|
-
if (serialized(parsed) === desired) {
|
|
4175
|
-
if (lineageEntry) {
|
|
4176
|
-
lineageEntry.taskIds = [
|
|
4177
|
-
...new Set([...lineageEntry.taskIds, task.id])
|
|
4178
|
-
].sort();
|
|
4179
|
-
lineageEntry.inputHashes = [
|
|
4180
|
-
...new Set([...lineageEntry.inputHashes, lineageInputHash])
|
|
4181
|
-
].sort();
|
|
4182
|
-
} else {
|
|
4183
|
-
registry.entries.push({
|
|
4184
|
-
lineageHash: lineageInputHash,
|
|
4185
|
-
split: task.split,
|
|
4186
|
-
taskIds: [task.id],
|
|
4187
|
-
inputHashes: [lineageInputHash]
|
|
4188
|
-
});
|
|
4189
|
-
}
|
|
4190
|
-
registry.entries.sort((left, right) => left.lineageHash.localeCompare(right.lineageHash));
|
|
4191
|
-
await atomicWriteFile(registryPath, serialized(registry));
|
|
4192
|
-
return parsed;
|
|
4193
|
-
}
|
|
4194
|
-
if (task.split !== "train") {
|
|
4195
|
-
throw new EvaluationConflictError(`${task.split} task ${task.id} is immutable after admission`);
|
|
4196
|
-
}
|
|
4197
|
-
}
|
|
4198
|
-
await atomicWriteFile(filePath, desired);
|
|
4199
|
-
if (lineageEntry) {
|
|
4200
|
-
lineageEntry.taskIds = [
|
|
4201
|
-
...new Set([...lineageEntry.taskIds, task.id])
|
|
4202
|
-
].sort();
|
|
4203
|
-
lineageEntry.inputHashes = [
|
|
4204
|
-
...new Set([...lineageEntry.inputHashes, lineageInputHash])
|
|
4205
|
-
].sort();
|
|
4206
|
-
} else {
|
|
4207
|
-
registry.entries.push({
|
|
4208
|
-
lineageHash: lineageInputHash,
|
|
4209
|
-
split: task.split,
|
|
4210
|
-
taskIds: [task.id],
|
|
4211
|
-
inputHashes: [lineageInputHash]
|
|
4212
|
-
});
|
|
4213
|
-
}
|
|
4214
|
-
registry.entries.sort((left, right) => left.lineageHash.localeCompare(right.lineageHash));
|
|
4215
|
-
await atomicWriteFile(registryPath, serialized(registry));
|
|
4216
|
-
return task;
|
|
4217
|
-
});
|
|
4218
|
-
}
|
|
4219
|
-
async function saveTaskSetSnapshot(directory, input, inputRoot = directory) {
|
|
4220
|
-
const snapshot = TaskSetSnapshotV1Schema.parse(input);
|
|
4221
|
-
const ids = snapshot.tasks.map((task) => task.id);
|
|
4222
|
-
if (new Set(ids).size !== ids.length)
|
|
4223
|
-
throw new Error("task-set contains duplicate task ids");
|
|
4224
|
-
if (snapshot.tasks.some((task) => task.split !== snapshot.split)) {
|
|
4225
|
-
throw new Error("task-set tasks must all belong to the declared split");
|
|
4226
|
-
}
|
|
4227
|
-
for (const task of snapshot.tasks) {
|
|
4228
|
-
if (await computeTaskInputContentHash(inputRoot, task) !== task.contentHash) {
|
|
4229
|
-
throw new Error(`task ${task.id} content hash is invalid`);
|
|
4230
|
-
}
|
|
4231
|
-
}
|
|
4232
|
-
if (computeTaskSetContentHash(snapshot) !== snapshot.contentHash) {
|
|
4233
|
-
throw new Error("task-set content hash does not match its canonical inputs");
|
|
4234
|
-
}
|
|
4235
|
-
const relative3 = path10.join("evolution", "task-sets", `${snapshot.contentHash}.json`);
|
|
4236
|
-
return writeImmutable({
|
|
4237
|
-
directory,
|
|
4238
|
-
relativeLockPath: relative3,
|
|
4239
|
-
filePath: swarmPath(directory, relative3),
|
|
4240
|
-
taskId: snapshot.id,
|
|
4241
|
-
value: snapshot,
|
|
4242
|
-
parse: (value) => TaskSetSnapshotV1Schema.parse(value),
|
|
4243
|
-
isEquivalent: (existing, desired) => existing.contentHash === desired.contentHash
|
|
4244
|
-
});
|
|
4245
|
-
}
|
|
4246
|
-
async function saveGateAuditResult(directory, input) {
|
|
4247
|
-
const result = GateAuditResultV1Schema.parse(input);
|
|
4248
|
-
const relative3 = path10.join("evidence", "gate-audit", result.runId, "results.json");
|
|
4249
|
-
return writeImmutable({
|
|
4250
|
-
directory,
|
|
4251
|
-
relativeLockPath: relative3,
|
|
4252
|
-
filePath: swarmPath(directory, relative3),
|
|
4253
|
-
taskId: result.runId,
|
|
4254
|
-
value: result,
|
|
4255
|
-
parse: (value) => GateAuditResultV1Schema.parse(value)
|
|
4256
|
-
});
|
|
4257
|
-
}
|
|
4258
|
-
async function saveGateAuditManifest(directory, input) {
|
|
4259
|
-
const manifest = GateAuditManifestV1Schema.parse(input);
|
|
4260
|
-
const relative3 = path10.join("evolution", "gate-audit-manifests", `${manifest.id}.json`);
|
|
4261
|
-
return writeImmutable({
|
|
4262
|
-
directory,
|
|
4263
|
-
relativeLockPath: relative3,
|
|
4264
|
-
filePath: swarmPath(directory, relative3),
|
|
4265
|
-
taskId: manifest.id,
|
|
4266
|
-
value: manifest,
|
|
4267
|
-
parse: (value) => GateAuditManifestV1Schema.parse(value)
|
|
4268
|
-
});
|
|
4269
|
-
}
|
|
4270
|
-
async function readGateAuditManifest(directory, runId) {
|
|
4271
|
-
const content = await readOptionalFile(swarmPath(directory, "evolution", "gate-audit-manifests", `${runId}.json`));
|
|
4272
|
-
return content === undefined ? undefined : GateAuditManifestV1Schema.parse(JSON.parse(content));
|
|
4273
|
-
}
|
|
4274
|
-
async function readGateAuditResult(directory, runId) {
|
|
4275
|
-
const content = await readOptionalFile(swarmPath(directory, "evidence", "gate-audit", runId, "results.json"));
|
|
4276
|
-
return content === undefined ? undefined : GateAuditResultV1Schema.parse(JSON.parse(content));
|
|
4277
|
-
}
|
|
4278
|
-
async function listGateAuditResults(directory) {
|
|
4279
|
-
const root = swarmPath(directory, "evidence", "gate-audit");
|
|
4280
|
-
let entries = [];
|
|
4281
|
-
try {
|
|
4282
|
-
entries = await readdir(root, { withFileTypes: true });
|
|
4283
|
-
} catch (error2) {
|
|
4284
|
-
if (error2.code === "ENOENT") {
|
|
4285
|
-
return { results: [], corruptRunIds: [] };
|
|
4286
|
-
}
|
|
4287
|
-
throw error2;
|
|
4288
|
-
}
|
|
4289
|
-
const results = [];
|
|
4290
|
-
const corruptRunIds = [];
|
|
4291
|
-
for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
|
|
4292
|
-
if (!entry.isDirectory() || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(entry.name)) {
|
|
4293
|
-
continue;
|
|
4294
|
-
}
|
|
4295
|
-
try {
|
|
4296
|
-
const parsed = GateAuditResultV1Schema.parse(JSON.parse(await readFile3(path10.join(root, entry.name, "results.json"), "utf8")));
|
|
4297
|
-
if (parsed.runId !== entry.name)
|
|
4298
|
-
throw new Error("run id/path mismatch");
|
|
4299
|
-
results.push(parsed);
|
|
4300
|
-
} catch {
|
|
4301
|
-
corruptRunIds.push(entry.name);
|
|
4302
|
-
}
|
|
4303
|
-
}
|
|
4304
|
-
return { results, corruptRunIds };
|
|
4305
|
-
}
|
|
4306
|
-
function parseTestLedger(content) {
|
|
4307
|
-
return content.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line, index) => {
|
|
4308
|
-
try {
|
|
4309
|
-
return TestConsumptionClaimV1Schema.parse(JSON.parse(line));
|
|
4310
|
-
} catch (error2) {
|
|
4311
|
-
throw new EvaluationConflictError(`test-consumption ledger is corrupt at line ${index + 1}: ${String(error2)}`);
|
|
4312
|
-
}
|
|
4313
|
-
});
|
|
4314
|
-
}
|
|
4315
|
-
async function getProtectedEvaluationRunIds(directory) {
|
|
4316
|
-
const protectedIds = new Set;
|
|
4317
|
-
const ledger = await readOptionalFile(swarmPath(directory, TEST_LEDGER_RELATIVE));
|
|
4318
|
-
for (const claim of parseTestLedger(ledger ?? ""))
|
|
4319
|
-
protectedIds.add(claim.runId);
|
|
4320
|
-
const decisionsDirectory = swarmPath(directory, "evolution", "decisions");
|
|
4321
|
-
let files = [];
|
|
4322
|
-
try {
|
|
4323
|
-
files = await readdir(decisionsDirectory);
|
|
4324
|
-
} catch (error2) {
|
|
4325
|
-
if (error2.code !== "ENOENT")
|
|
4326
|
-
throw error2;
|
|
4327
|
-
}
|
|
4328
|
-
for (const filename of files.filter((value) => value.endsWith(".json")).sort()) {
|
|
4329
|
-
const decision = PromotionDecisionV1Schema.parse(JSON.parse(await readFile3(path10.join(decisionsDirectory, filename), "utf8")));
|
|
4330
|
-
protectedIds.add(decision.runId);
|
|
4331
|
-
protectedIds.add(decision.lineage.baselineRunId);
|
|
4332
|
-
if (decision.lineage.historicalBestRunId !== "unavailable") {
|
|
4333
|
-
protectedIds.add(decision.lineage.historicalBestRunId);
|
|
4334
|
-
}
|
|
4335
|
-
}
|
|
4336
|
-
return protectedIds;
|
|
4337
|
-
}
|
|
4338
|
-
|
|
4339
|
-
// src/evaluation/retention.ts
|
|
4340
3397
|
function artifactKey(artifact) {
|
|
4341
3398
|
return `${artifact.namespace}/${artifact.id}`;
|
|
4342
3399
|
}
|
|
@@ -4344,7 +3401,7 @@ function validId(value) {
|
|
|
4344
3401
|
return /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(value);
|
|
4345
3402
|
}
|
|
4346
3403
|
async function evaluationRunCandidates(directory) {
|
|
4347
|
-
const root =
|
|
3404
|
+
const root = path8.join(directory, ".swarm", "evolution", "runs");
|
|
4348
3405
|
let entries = [];
|
|
4349
3406
|
try {
|
|
4350
3407
|
entries = await fs4.readdir(root, { withFileTypes: true });
|
|
@@ -4363,7 +3420,7 @@ async function evaluationRunCandidates(directory) {
|
|
|
4363
3420
|
if (!validId(id))
|
|
4364
3421
|
continue;
|
|
4365
3422
|
const artifact = { namespace: "evaluation-run", id };
|
|
4366
|
-
const target =
|
|
3423
|
+
const target = path8.join(root, entry.name);
|
|
4367
3424
|
try {
|
|
4368
3425
|
const run = EvaluationRunV1Schema.parse(JSON.parse(await fs4.readFile(target, "utf8")));
|
|
4369
3426
|
if (run.runId !== id)
|
|
@@ -4381,7 +3438,7 @@ async function evaluationRunCandidates(directory) {
|
|
|
4381
3438
|
return { candidates, corrupt };
|
|
4382
3439
|
}
|
|
4383
3440
|
async function gateAuditCandidates(directory) {
|
|
4384
|
-
const root =
|
|
3441
|
+
const root = path8.join(directory, ".swarm", "evidence", "gate-audit");
|
|
4385
3442
|
let entries = [];
|
|
4386
3443
|
try {
|
|
4387
3444
|
entries = await fs4.readdir(root, { withFileTypes: true });
|
|
@@ -4397,9 +3454,9 @@ async function gateAuditCandidates(directory) {
|
|
|
4397
3454
|
if (!entry.isDirectory() || !validId(entry.name))
|
|
4398
3455
|
continue;
|
|
4399
3456
|
const artifact = { namespace: "gate-audit", id: entry.name };
|
|
4400
|
-
const target =
|
|
3457
|
+
const target = path8.join(root, entry.name);
|
|
4401
3458
|
try {
|
|
4402
|
-
const result = GateAuditResultV1Schema.parse(JSON.parse(await fs4.readFile(
|
|
3459
|
+
const result = GateAuditResultV1Schema.parse(JSON.parse(await fs4.readFile(path8.join(target, "results.json"), "utf8")));
|
|
4403
3460
|
if (result.runId !== entry.name)
|
|
4404
3461
|
throw new Error("run id/path mismatch");
|
|
4405
3462
|
candidates.push({
|
|
@@ -4440,7 +3497,7 @@ var _retentionInternals = {
|
|
|
4440
3497
|
removeCandidate
|
|
4441
3498
|
};
|
|
4442
3499
|
async function archiveEvaluationArtifacts(args) {
|
|
4443
|
-
return withEvidenceLock(args.directory,
|
|
3500
|
+
return withEvidenceLock(args.directory, path8.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
|
|
4444
3501
|
const inventory = await retentionCandidates(args.directory);
|
|
4445
3502
|
let protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
|
|
4446
3503
|
const protectedRefs = inventory.candidates.filter((candidate) => protectedKeys.has(artifactKey(candidate.artifact))).map((candidate) => candidate.artifact);
|
|
@@ -4497,7 +3554,7 @@ var MAX_READ_BYTES = 100 * 1024 * 1024;
|
|
|
4497
3554
|
var RENAME_MAX_ATTEMPTS = 5;
|
|
4498
3555
|
var RENAME_BACKOFF_MS = 10;
|
|
4499
3556
|
var RENAME_RETRY_CODES = new Set(["EPERM", "EBUSY", "ENOTEMPTY", "EACCES"]);
|
|
4500
|
-
var
|
|
3557
|
+
var _internals9 = {
|
|
4501
3558
|
stat: (p) => fsp.stat(p),
|
|
4502
3559
|
createReadStream: (p, options) => fs5.createReadStream(p, options),
|
|
4503
3560
|
openSync: (p, flags) => fs5.openSync(p, flags),
|
|
@@ -4539,9 +3596,9 @@ function emptyResult(dryRun) {
|
|
|
4539
3596
|
};
|
|
4540
3597
|
}
|
|
4541
3598
|
async function readCacheRows(filePath) {
|
|
4542
|
-
const stat2 = await
|
|
3599
|
+
const stat2 = await _internals9.stat(filePath);
|
|
4543
3600
|
const bytesBefore = stat2.size;
|
|
4544
|
-
const stream =
|
|
3601
|
+
const stream = _internals9.createReadStream(filePath, {
|
|
4545
3602
|
encoding: "utf8",
|
|
4546
3603
|
highWaterMark: 64 * 1024
|
|
4547
3604
|
});
|
|
@@ -4617,23 +3674,23 @@ async function rewriteAtomic(filePath, survivors) {
|
|
|
4617
3674
|
`)}
|
|
4618
3675
|
` : "";
|
|
4619
3676
|
let succeeded = false;
|
|
4620
|
-
const fd =
|
|
3677
|
+
const fd = _internals9.openSync(tmpPath, "w");
|
|
4621
3678
|
try {
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4625
|
-
await
|
|
3679
|
+
_internals9.writeSync(fd, payload, 0);
|
|
3680
|
+
_internals9.fsyncSync(fd);
|
|
3681
|
+
_internals9.closeSync(fd);
|
|
3682
|
+
await _internals9.renameWithRetry(tmpPath, filePath);
|
|
4626
3683
|
succeeded = true;
|
|
4627
3684
|
} catch (err) {
|
|
4628
3685
|
try {
|
|
4629
|
-
|
|
3686
|
+
_internals9.closeSync(fd);
|
|
4630
3687
|
} catch {}
|
|
4631
3688
|
const msg = err instanceof Error ? err.message : String(err);
|
|
4632
3689
|
throw new Error(`documents-cache rewrite failed: ${msg}`);
|
|
4633
3690
|
} finally {
|
|
4634
3691
|
if (!succeeded) {
|
|
4635
3692
|
try {
|
|
4636
|
-
await
|
|
3693
|
+
await _internals9.unlink(tmpPath);
|
|
4637
3694
|
} catch {}
|
|
4638
3695
|
}
|
|
4639
3696
|
}
|
|
@@ -4645,7 +3702,7 @@ async function pruneEvidenceDocuments(args) {
|
|
|
4645
3702
|
const filePath = validateSwarmPath(args.directory, EVIDENCE_CACHE_FILE);
|
|
4646
3703
|
let stat2;
|
|
4647
3704
|
try {
|
|
4648
|
-
stat2 = await
|
|
3705
|
+
stat2 = await _internals9.stat(filePath);
|
|
4649
3706
|
} catch (err) {
|
|
4650
3707
|
const code = err?.code;
|
|
4651
3708
|
if (code === "ENOENT")
|
|
@@ -4760,11 +3817,11 @@ var PROJECT_INDICATORS = [
|
|
|
4760
3817
|
];
|
|
4761
3818
|
function isWeakConfigContainerRoot(directory) {
|
|
4762
3819
|
try {
|
|
4763
|
-
const resolved =
|
|
4764
|
-
return resolved ===
|
|
3820
|
+
const resolved = realpathSync(directory);
|
|
3821
|
+
return resolved === realpathSync(os4.tmpdir()) || resolved === realpathSync(os4.homedir());
|
|
4765
3822
|
} catch {
|
|
4766
|
-
const resolved =
|
|
4767
|
-
return resolved ===
|
|
3823
|
+
const resolved = path9.resolve(directory);
|
|
3824
|
+
return resolved === path9.resolve(os4.tmpdir()) || resolved === path9.resolve(os4.homedir());
|
|
4768
3825
|
}
|
|
4769
3826
|
}
|
|
4770
3827
|
function hasAccessibleProjectIndicator(directory, options = {}) {
|
|
@@ -4774,7 +3831,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
|
|
|
4774
3831
|
continue;
|
|
4775
3832
|
}
|
|
4776
3833
|
try {
|
|
4777
|
-
const indicatorStat = statSync2(
|
|
3834
|
+
const indicatorStat = statSync2(path9.join(directory, indicator));
|
|
4778
3835
|
if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
|
|
4779
3836
|
return true;
|
|
4780
3837
|
}
|
|
@@ -4785,7 +3842,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
|
|
|
4785
3842
|
function validateProjectRoot(directory) {
|
|
4786
3843
|
let resolved;
|
|
4787
3844
|
try {
|
|
4788
|
-
resolved =
|
|
3845
|
+
resolved = realpathSync(directory);
|
|
4789
3846
|
} catch {
|
|
4790
3847
|
warn(`[evidence] Cannot canonicalize directory "${directory}" \u2014 failing closed`);
|
|
4791
3848
|
throw new Error(`Cannot verify project root for "${directory}" \u2014 directory may not exist or is inaccessible`);
|
|
@@ -4796,14 +3853,14 @@ function validateProjectRoot(directory) {
|
|
|
4796
3853
|
if (depth >= MAX_DEPTH)
|
|
4797
3854
|
break;
|
|
4798
3855
|
depth++;
|
|
4799
|
-
const parent =
|
|
3856
|
+
const parent = path9.dirname(current);
|
|
4800
3857
|
if (parent === current)
|
|
4801
3858
|
break;
|
|
4802
|
-
if (
|
|
3859
|
+
if (path9.dirname(parent) === parent) {
|
|
4803
3860
|
current = parent;
|
|
4804
3861
|
continue;
|
|
4805
3862
|
}
|
|
4806
|
-
const parentSwarm =
|
|
3863
|
+
const parentSwarm = path9.join(parent, ".swarm");
|
|
4807
3864
|
try {
|
|
4808
3865
|
if (statSync2(parentSwarm).isDirectory()) {
|
|
4809
3866
|
if (hasAccessibleProjectIndicator(parent, {
|
|
@@ -4822,14 +3879,14 @@ function validateProjectRoot(directory) {
|
|
|
4822
3879
|
}
|
|
4823
3880
|
}
|
|
4824
3881
|
async function saveEvidence(directory, taskId, evidence) {
|
|
4825
|
-
|
|
3882
|
+
_internals10.validateProjectRoot(directory);
|
|
4826
3883
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
4827
|
-
const relativePath =
|
|
3884
|
+
const relativePath = path9.join("evidence", sanitizedTaskId, "evidence.json");
|
|
4828
3885
|
validateSwarmPath(directory, relativePath);
|
|
4829
|
-
|
|
3886
|
+
_internals10.validateEvidence(evidence);
|
|
4830
3887
|
return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
|
|
4831
3888
|
const evidencePath = validateSwarmPath(directory, relativePath);
|
|
4832
|
-
const evidenceDir =
|
|
3889
|
+
const evidenceDir = path9.dirname(evidencePath);
|
|
4833
3890
|
let bundle;
|
|
4834
3891
|
const existingContent = await readSwarmFileAsync(directory, relativePath);
|
|
4835
3892
|
if (existingContent !== null) {
|
|
@@ -4872,7 +3929,7 @@ async function saveEvidence(directory, taskId, evidence) {
|
|
|
4872
3929
|
throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
|
|
4873
3930
|
}
|
|
4874
3931
|
mkdirSync4(evidenceDir, { recursive: true });
|
|
4875
|
-
const tempPath =
|
|
3932
|
+
const tempPath = path9.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
4876
3933
|
try {
|
|
4877
3934
|
await bunWrite(tempPath, bundleJson);
|
|
4878
3935
|
await fs6.rename(tempPath, evidencePath);
|
|
@@ -4916,7 +3973,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
|
|
|
4916
3973
|
}
|
|
4917
3974
|
async function loadEvidence(directory, taskId, options) {
|
|
4918
3975
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
4919
|
-
const relativePath =
|
|
3976
|
+
const relativePath = path9.join("evidence", sanitizedTaskId, "evidence.json");
|
|
4920
3977
|
if (relativePath.length > 4096) {
|
|
4921
3978
|
return { status: "not_found" };
|
|
4922
3979
|
}
|
|
@@ -4932,7 +3989,7 @@ async function loadEvidence(directory, taskId, options) {
|
|
|
4932
3989
|
return { status: "invalid_schema", errors: ["Invalid JSON"] };
|
|
4933
3990
|
}
|
|
4934
3991
|
if (isFlatRetrospective(parsed)) {
|
|
4935
|
-
const wrappedBundle =
|
|
3992
|
+
const wrappedBundle = _internals10.wrapFlatRetrospective(parsed, sanitizedTaskId);
|
|
4936
3993
|
try {
|
|
4937
3994
|
const validated = EvidenceBundleSchema.parse(wrappedBundle);
|
|
4938
3995
|
if (options?.migrate === false) {
|
|
@@ -4940,9 +3997,9 @@ async function loadEvidence(directory, taskId, options) {
|
|
|
4940
3997
|
}
|
|
4941
3998
|
try {
|
|
4942
3999
|
await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
|
|
4943
|
-
const evidenceDir =
|
|
4000
|
+
const evidenceDir = path9.dirname(evidencePath);
|
|
4944
4001
|
const bundleJson = JSON.stringify(validated);
|
|
4945
|
-
const tempPath =
|
|
4002
|
+
const tempPath = path9.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
4946
4003
|
try {
|
|
4947
4004
|
await bunWrite(tempPath, bundleJson);
|
|
4948
4005
|
await fs6.rename(tempPath, evidencePath);
|
|
@@ -4987,7 +4044,7 @@ async function listEvidenceTaskIds(directory) {
|
|
|
4987
4044
|
}
|
|
4988
4045
|
const taskIds = [];
|
|
4989
4046
|
for (const entry of entries) {
|
|
4990
|
-
const entryPath =
|
|
4047
|
+
const entryPath = path9.join(evidenceBasePath, entry);
|
|
4991
4048
|
try {
|
|
4992
4049
|
const stats = statSync2(entryPath);
|
|
4993
4050
|
if (!stats.isDirectory()) {
|
|
@@ -5005,7 +4062,7 @@ async function listEvidenceTaskIds(directory) {
|
|
|
5005
4062
|
}
|
|
5006
4063
|
async function deleteEvidence(directory, taskId) {
|
|
5007
4064
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
5008
|
-
const relativePath =
|
|
4065
|
+
const relativePath = path9.join("evidence", sanitizedTaskId);
|
|
5009
4066
|
const evidenceDir = validateSwarmPath(directory, relativePath);
|
|
5010
4067
|
try {
|
|
5011
4068
|
statSync2(evidenceDir);
|
|
@@ -5021,8 +4078,8 @@ async function deleteEvidence(directory, taskId) {
|
|
|
5021
4078
|
}
|
|
5022
4079
|
}
|
|
5023
4080
|
async function checkRequirementCoverage(phase, directory) {
|
|
5024
|
-
const relativePath =
|
|
5025
|
-
const absolutePath =
|
|
4081
|
+
const relativePath = path9.join("evidence", `req-coverage-phase-${phase}.json`);
|
|
4082
|
+
const absolutePath = path9.resolve(directory, ".swarm", relativePath);
|
|
5026
4083
|
try {
|
|
5027
4084
|
await fs6.access(absolutePath);
|
|
5028
4085
|
return { exists: true, path: absolutePath };
|
|
@@ -5031,8 +4088,8 @@ async function checkRequirementCoverage(phase, directory) {
|
|
|
5031
4088
|
}
|
|
5032
4089
|
}
|
|
5033
4090
|
async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
5034
|
-
const taskIds = await
|
|
5035
|
-
const cutoffDate = options?.now ? new Date(options.now) :
|
|
4091
|
+
const taskIds = await _internals10.listEvidenceTaskIds(directory);
|
|
4092
|
+
const cutoffDate = options?.now ? new Date(options.now) : _internals10.now();
|
|
5036
4093
|
cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
|
|
5037
4094
|
const cutoffIso = cutoffDate.toISOString();
|
|
5038
4095
|
const selected = new Set;
|
|
@@ -5044,7 +4101,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
5044
4101
|
for (const taskId of taskIds) {
|
|
5045
4102
|
let result;
|
|
5046
4103
|
try {
|
|
5047
|
-
result = await
|
|
4104
|
+
result = await _internals10.loadEvidence(directory, taskId);
|
|
5048
4105
|
} catch {
|
|
5049
4106
|
warn("archive: skipping corrupt or unreadable evidence for task", taskId);
|
|
5050
4107
|
continue;
|
|
@@ -5072,7 +4129,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
5072
4129
|
}
|
|
5073
4130
|
if (!options?.dryRun) {
|
|
5074
4131
|
for (const taskId of [...selected].sort()) {
|
|
5075
|
-
const deleted = await
|
|
4132
|
+
const deleted = await _internals10.deleteEvidence(directory, taskId);
|
|
5076
4133
|
if (deleted)
|
|
5077
4134
|
archived.push(taskId);
|
|
5078
4135
|
else
|
|
@@ -5098,7 +4155,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
5098
4155
|
};
|
|
5099
4156
|
if (typeof options?.cacheMaxBytes === "number" && options.cacheMaxBytes > 0 || typeof options?.cacheMaxRecords === "number" && options.cacheMaxRecords > 0) {
|
|
5100
4157
|
try {
|
|
5101
|
-
documentsCache = await
|
|
4158
|
+
documentsCache = await _internals10.pruneEvidenceDocuments({
|
|
5102
4159
|
directory,
|
|
5103
4160
|
maxBytes: options?.cacheMaxBytes,
|
|
5104
4161
|
maxRecords: options?.cacheMaxRecords,
|
|
@@ -5126,7 +4183,7 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
|
|
|
5126
4183
|
function validateEvidence(evidence) {
|
|
5127
4184
|
return EvidenceSchema.parse(evidence);
|
|
5128
4185
|
}
|
|
5129
|
-
var
|
|
4186
|
+
var _internals10 = {
|
|
5130
4187
|
wrapFlatRetrospective,
|
|
5131
4188
|
loadEvidence,
|
|
5132
4189
|
listEvidenceTaskIds,
|
|
@@ -5248,4 +4305,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
|
|
|
5248
4305
|
return merged;
|
|
5249
4306
|
}
|
|
5250
4307
|
|
|
5251
|
-
export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown,
|
|
4308
|
+
export { getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
|