@tangle-network/agent-eval 0.112.0 → 0.114.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/belief-state/index.js +1 -1
- package/dist/benchmarks/index.d.ts +2 -2
- package/dist/benchmarks/index.js +6 -6
- package/dist/builder-eval/index.js +1 -1
- package/dist/campaign/index.d.ts +55 -22
- package/dist/campaign/index.js +13 -7
- package/dist/{chunk-VLNGPUJJ.js → chunk-3LXTCTWL.js} +2 -2
- package/dist/{chunk-7QPK6W7R.js → chunk-ARU2PZFM.js} +2 -2
- package/dist/{chunk-EMKORATZ.js → chunk-DPZAEKA6.js} +2 -2
- package/dist/{chunk-XNCF3JU3.js → chunk-FAOEFFRT.js} +2 -2
- package/dist/{chunk-WZOIFFG2.js → chunk-LOBMT6SB.js} +2 -2
- package/dist/{chunk-RT2AFUXM.js → chunk-MOXWMGPC.js} +2 -2
- package/dist/{chunk-RCXMWGRY.js → chunk-N6MTC3GK.js} +69 -25
- package/dist/chunk-N6MTC3GK.js.map +1 -0
- package/dist/{chunk-YWGKQARF.js → chunk-NYFUT3B3.js} +2 -2
- package/dist/{chunk-VC43KQCK.js → chunk-PJQFMIOX.js} +62 -9
- package/dist/chunk-PJQFMIOX.js.map +1 -0
- package/dist/{chunk-QQVYMBCZ.js → chunk-VHPD6AXX.js} +512 -21
- package/dist/{chunk-QQVYMBCZ.js.map → chunk-VHPD6AXX.js.map} +1 -1
- package/dist/{chunk-BOLYPSAE.js → chunk-WBOGKYM4.js} +4 -4
- package/dist/{chunk-3LDPUYAC.js → chunk-X4UCIOTZ.js} +2 -2
- package/dist/contract/index.d.ts +7 -7
- package/dist/contract/index.js +9 -8
- package/dist/contract/index.js.map +1 -1
- package/dist/{gepa-T8T215nw.d.ts → gepa-DolL_Fko.d.ts} +2 -6
- package/dist/hosted/index.d.ts +1 -1
- package/dist/{index-Dc3VLGhp.d.ts → index-CWr5SIG-.d.ts} +1 -1
- package/dist/index.d.ts +8 -8
- package/dist/index.js +13 -11
- package/dist/index.js.map +1 -1
- package/dist/meta-eval/index.d.ts +1 -1
- package/dist/meta-eval/index.js +2 -2
- package/dist/multishot/index.d.ts +1 -1
- package/dist/openapi.json +1 -1
- package/dist/pipelines/index.js +2 -2
- package/dist/{pre-registration-BepVVa6P.d.ts → pre-registration-CTQbZbpX.d.ts} +1 -1
- package/dist/{provenance-KhY8ESVM.d.ts → provenance-BZmpWmn4.d.ts} +5 -11
- package/dist/reporting.d.ts +1 -1
- package/dist/reporting.js +4 -4
- package/dist/rl.d.ts +1 -1
- package/dist/rl.js +5 -5
- package/dist/{run-campaign-HJQEDCJQ.js → run-campaign-UADIM77S.js} +3 -3
- package/dist/{statistics-CDfpwIdX.d.ts → statistics-oUbOJe-S.d.ts} +34 -1
- package/dist/{types-v--ctu-b.d.ts → types-CgSlO6wT.d.ts} +26 -11
- package/docs/design/loop-taxonomy.md +7 -1
- package/docs/improvement-glossary.md +1 -1
- package/package.json +1 -1
- package/dist/chunk-RCXMWGRY.js.map +0 -1
- package/dist/chunk-VC43KQCK.js.map +0 -1
- /package/dist/{chunk-VLNGPUJJ.js.map → chunk-3LXTCTWL.js.map} +0 -0
- /package/dist/{chunk-7QPK6W7R.js.map → chunk-ARU2PZFM.js.map} +0 -0
- /package/dist/{chunk-EMKORATZ.js.map → chunk-DPZAEKA6.js.map} +0 -0
- /package/dist/{chunk-XNCF3JU3.js.map → chunk-FAOEFFRT.js.map} +0 -0
- /package/dist/{chunk-WZOIFFG2.js.map → chunk-LOBMT6SB.js.map} +0 -0
- /package/dist/{chunk-RT2AFUXM.js.map → chunk-MOXWMGPC.js.map} +0 -0
- /package/dist/{chunk-YWGKQARF.js.map → chunk-NYFUT3B3.js.map} +0 -0
- /package/dist/{chunk-BOLYPSAE.js.map → chunk-WBOGKYM4.js.map} +0 -0
- /package/dist/{chunk-3LDPUYAC.js.map → chunk-X4UCIOTZ.js.map} +0 -0
- /package/dist/{run-campaign-HJQEDCJQ.js.map → run-campaign-UADIM77S.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
assertCodeSurfaceIdentity,
|
|
2
3
|
campaignBreakdown,
|
|
3
4
|
campaignMeanComposite,
|
|
4
5
|
defaultProductionGate,
|
|
@@ -9,8 +10,9 @@ import {
|
|
|
9
10
|
recoverTruncatedJson,
|
|
10
11
|
renderAnalystEvidence,
|
|
11
12
|
runImprovementLoop,
|
|
13
|
+
surfaceContentHash,
|
|
12
14
|
surfaceHash
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-N6MTC3GK.js";
|
|
14
16
|
import {
|
|
15
17
|
estimateCost,
|
|
16
18
|
isModelPriced
|
|
@@ -21,7 +23,7 @@ import {
|
|
|
21
23
|
planCampaignRun,
|
|
22
24
|
runCampaign,
|
|
23
25
|
summarizeBackendIntegrity
|
|
24
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-FAOEFFRT.js";
|
|
25
27
|
import {
|
|
26
28
|
admitPolicyEdit,
|
|
27
29
|
applyPolicyEditToSurface,
|
|
@@ -39,7 +41,7 @@ import {
|
|
|
39
41
|
mulberry32,
|
|
40
42
|
pairedBootstrap,
|
|
41
43
|
weightedComposite
|
|
42
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-PJQFMIOX.js";
|
|
43
45
|
import {
|
|
44
46
|
analyzeTraces
|
|
45
47
|
} from "./chunk-RPDDVKI7.js";
|
|
@@ -3906,7 +3908,7 @@ function compositeProposer(opts) {
|
|
|
3906
3908
|
const surface = isCandidate ? proposal.surface : proposal;
|
|
3907
3909
|
const label = isCandidate ? proposal.label : "candidate";
|
|
3908
3910
|
const rationale = isCandidate ? proposal.rationale : "";
|
|
3909
|
-
const key =
|
|
3911
|
+
const key = surfaceContentHash(surface);
|
|
3910
3912
|
if (seen.has(key)) continue;
|
|
3911
3913
|
seen.add(key);
|
|
3912
3914
|
pool.push({ surface, label: `${member.kind}:${label}`, rationale });
|
|
@@ -4172,6 +4174,7 @@ function coerceCandidateSurface(surface) {
|
|
|
4172
4174
|
if (surface && typeof surface === "object") {
|
|
4173
4175
|
const obj = surface;
|
|
4174
4176
|
if (obj.kind === "code" && typeof obj.worktreeRef === "string") {
|
|
4177
|
+
assertCodeSurfaceIdentity(surface);
|
|
4175
4178
|
return surface;
|
|
4176
4179
|
}
|
|
4177
4180
|
return JSON.stringify(surface, null, 2);
|
|
@@ -4179,7 +4182,7 @@ function coerceCandidateSurface(surface) {
|
|
|
4179
4182
|
throw new Error("policyEditProposer: policy edit produced an unsupported surface");
|
|
4180
4183
|
}
|
|
4181
4184
|
function sameSurface(a, b) {
|
|
4182
|
-
return
|
|
4185
|
+
return surfaceContentHash(a) === surfaceContentHash(b);
|
|
4183
4186
|
}
|
|
4184
4187
|
|
|
4185
4188
|
// src/campaign/proposers/trace-analyst.ts
|
|
@@ -4338,8 +4341,36 @@ function isTransientTransportFailure(message, opts = {}) {
|
|
|
4338
4341
|
|
|
4339
4342
|
// src/campaign/worktree/index.ts
|
|
4340
4343
|
import { execFileSync } from "child_process";
|
|
4341
|
-
import {
|
|
4342
|
-
import {
|
|
4344
|
+
import { createHash as createHash7 } from "crypto";
|
|
4345
|
+
import {
|
|
4346
|
+
closeSync,
|
|
4347
|
+
existsSync as existsSync4,
|
|
4348
|
+
constants as fsConstants,
|
|
4349
|
+
fstatSync,
|
|
4350
|
+
lstatSync,
|
|
4351
|
+
mkdirSync as mkdirSync2,
|
|
4352
|
+
mkdtempSync as mkdtempSync2,
|
|
4353
|
+
openSync,
|
|
4354
|
+
readlinkSync,
|
|
4355
|
+
readSync,
|
|
4356
|
+
realpathSync,
|
|
4357
|
+
rmSync
|
|
4358
|
+
} from "fs";
|
|
4359
|
+
import { devNull, tmpdir as tmpdir2 } from "os";
|
|
4360
|
+
import { basename, dirname as dirname2, isAbsolute as isAbsolute2, join as join5, relative as relative2, resolve as resolve2, sep } from "path";
|
|
4361
|
+
var MAX_GIT_OUTPUT_BYTES = 256 * 1024 * 1024;
|
|
4362
|
+
var FILE_HASH_CHUNK_BYTES = 1024 * 1024;
|
|
4363
|
+
var GIT_REPOSITORY_ENV = /* @__PURE__ */ new Set([
|
|
4364
|
+
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
4365
|
+
"GIT_COMMON_DIR",
|
|
4366
|
+
"GIT_DIR",
|
|
4367
|
+
"GIT_INDEX_FILE",
|
|
4368
|
+
"GIT_NAMESPACE",
|
|
4369
|
+
"GIT_OBJECT_DIRECTORY",
|
|
4370
|
+
"GIT_PREFIX",
|
|
4371
|
+
"GIT_QUARANTINE_PATH",
|
|
4372
|
+
"GIT_WORK_TREE"
|
|
4373
|
+
]);
|
|
4343
4374
|
var WorktreeAdapterError = class extends Error {
|
|
4344
4375
|
constructor(message, cause) {
|
|
4345
4376
|
super(message);
|
|
@@ -4348,14 +4379,443 @@ var WorktreeAdapterError = class extends Error {
|
|
|
4348
4379
|
}
|
|
4349
4380
|
cause;
|
|
4350
4381
|
};
|
|
4351
|
-
function defaultGit(args, cwd) {
|
|
4382
|
+
function defaultGit(args, cwd, overrides) {
|
|
4352
4383
|
try {
|
|
4353
|
-
|
|
4384
|
+
const env = { ...process.env };
|
|
4385
|
+
for (const key of Object.keys(env)) {
|
|
4386
|
+
if (GIT_REPOSITORY_ENV.has(key) || key === "GIT_CONFIG" || key.startsWith("GIT_CONFIG_") || key.startsWith("GIT_ATTR_") || key === "GIT_DIFF_OPTS" || key === "GIT_EXTERNAL_DIFF") {
|
|
4387
|
+
delete env[key];
|
|
4388
|
+
}
|
|
4389
|
+
}
|
|
4390
|
+
Object.assign(env, overrides);
|
|
4391
|
+
env.GIT_NO_REPLACE_OBJECTS = "1";
|
|
4392
|
+
env.LC_ALL = "C";
|
|
4393
|
+
env.LANG = "C";
|
|
4394
|
+
return execFileSync("git", args, { cwd, env, maxBuffer: MAX_GIT_OUTPUT_BYTES });
|
|
4354
4395
|
} catch (err) {
|
|
4355
4396
|
const stderr = err && typeof err === "object" && "stderr" in err ? String(err.stderr) : "";
|
|
4356
4397
|
throw new WorktreeAdapterError(`git ${args.join(" ")} failed: ${stderr || String(err)}`, err);
|
|
4357
4398
|
}
|
|
4358
4399
|
}
|
|
4400
|
+
function gitBytes(git, args, cwd, env) {
|
|
4401
|
+
try {
|
|
4402
|
+
const output = git(args, cwd, env);
|
|
4403
|
+
return typeof output === "string" ? Buffer.from(output, "utf8") : Buffer.from(output);
|
|
4404
|
+
} catch (err) {
|
|
4405
|
+
if (err instanceof WorktreeAdapterError) throw err;
|
|
4406
|
+
throw new WorktreeAdapterError(`git ${args.join(" ")} failed: ${String(err)}`, err);
|
|
4407
|
+
}
|
|
4408
|
+
}
|
|
4409
|
+
function gitText(git, args, cwd, env) {
|
|
4410
|
+
return gitBytes(git, args, cwd, env).toString("utf8").trim();
|
|
4411
|
+
}
|
|
4412
|
+
function sha2562(bytes) {
|
|
4413
|
+
return `sha256:${createHash7("sha256").update(bytes).digest("hex")}`;
|
|
4414
|
+
}
|
|
4415
|
+
var PATCH_ARGS = [
|
|
4416
|
+
// Git binary patches embed a zlib stream. Pin no-compression at command
|
|
4417
|
+
// scope so repository/global config and compressor heuristics cannot change
|
|
4418
|
+
// the candidate identity for the same base and final tree.
|
|
4419
|
+
"-c",
|
|
4420
|
+
"core.compression=0",
|
|
4421
|
+
"-c",
|
|
4422
|
+
`core.attributesFile=${devNull}`,
|
|
4423
|
+
"-c",
|
|
4424
|
+
"core.quotePath=true",
|
|
4425
|
+
"-c",
|
|
4426
|
+
"diff.suppressBlankEmpty=false",
|
|
4427
|
+
"diff",
|
|
4428
|
+
"--unified=3",
|
|
4429
|
+
"--inter-hunk-context=0",
|
|
4430
|
+
`-O${devNull}`,
|
|
4431
|
+
"--binary",
|
|
4432
|
+
"--full-index",
|
|
4433
|
+
"--no-color",
|
|
4434
|
+
"--no-ext-diff",
|
|
4435
|
+
"--no-textconv",
|
|
4436
|
+
"--no-renames",
|
|
4437
|
+
"--diff-algorithm=myers",
|
|
4438
|
+
"--no-indent-heuristic",
|
|
4439
|
+
"--src-prefix=a/",
|
|
4440
|
+
"--dst-prefix=b/"
|
|
4441
|
+
];
|
|
4442
|
+
var CANONICAL_GIT_ENV = {
|
|
4443
|
+
GIT_ATTR_GLOBAL: devNull,
|
|
4444
|
+
GIT_ATTR_NOSYSTEM: "1",
|
|
4445
|
+
GIT_CONFIG_GLOBAL: devNull,
|
|
4446
|
+
GIT_CONFIG_NOSYSTEM: "1",
|
|
4447
|
+
GIT_CONFIG_SYSTEM: devNull
|
|
4448
|
+
};
|
|
4449
|
+
function patchBytes(git, cwd, baseCommit, candidateCommit) {
|
|
4450
|
+
const scratch = mkdtempSync2(join5(tmpdir2(), "agent-eval-patch-"));
|
|
4451
|
+
const bareRepo = join5(scratch, "repo.git");
|
|
4452
|
+
const emptyTemplate = join5(scratch, "empty-template");
|
|
4453
|
+
mkdirSync2(emptyTemplate);
|
|
4454
|
+
try {
|
|
4455
|
+
const objectFormat = gitObjectHashAlgorithm(candidateCommit);
|
|
4456
|
+
const sourceObjects = realpathSync(gitText(git, ["rev-parse", "--git-path", "objects"], cwd));
|
|
4457
|
+
gitText(
|
|
4458
|
+
git,
|
|
4459
|
+
[
|
|
4460
|
+
"init",
|
|
4461
|
+
"--bare",
|
|
4462
|
+
"--quiet",
|
|
4463
|
+
...objectFormat === "sha256" ? ["--object-format=sha256"] : [],
|
|
4464
|
+
`--template=${emptyTemplate}`,
|
|
4465
|
+
bareRepo
|
|
4466
|
+
],
|
|
4467
|
+
scratch,
|
|
4468
|
+
CANONICAL_GIT_ENV
|
|
4469
|
+
);
|
|
4470
|
+
return gitBytes(
|
|
4471
|
+
git,
|
|
4472
|
+
[`--git-dir=${bareRepo}`, ...PATCH_ARGS, baseCommit, candidateCommit, "--"],
|
|
4473
|
+
scratch,
|
|
4474
|
+
{
|
|
4475
|
+
...CANONICAL_GIT_ENV,
|
|
4476
|
+
GIT_ALTERNATE_OBJECT_DIRECTORIES: sourceObjects
|
|
4477
|
+
}
|
|
4478
|
+
);
|
|
4479
|
+
} finally {
|
|
4480
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
function resolveCommit(git, cwd, ref) {
|
|
4484
|
+
return gitText(git, ["rev-parse", "--verify", `${ref}^{commit}`], cwd);
|
|
4485
|
+
}
|
|
4486
|
+
function unresolvedWorktreePath(surface, worktreeDir) {
|
|
4487
|
+
if (isAbsolute2(surface.worktreeRef)) return surface.worktreeRef;
|
|
4488
|
+
if (worktreeDir) return join5(worktreeDir, basename(surface.worktreeRef));
|
|
4489
|
+
return surface.worktreeRef;
|
|
4490
|
+
}
|
|
4491
|
+
function displayGitPath(path) {
|
|
4492
|
+
return JSON.stringify(path);
|
|
4493
|
+
}
|
|
4494
|
+
function parseGitTreeEntries(bytes) {
|
|
4495
|
+
const input = Buffer.from(bytes);
|
|
4496
|
+
const entries = [];
|
|
4497
|
+
let start = 0;
|
|
4498
|
+
while (start < input.length) {
|
|
4499
|
+
const end = input.indexOf(0, start);
|
|
4500
|
+
if (end < 0) throw new WorktreeAdapterError("Git tree output was not NUL-terminated");
|
|
4501
|
+
const record = input.subarray(start, end);
|
|
4502
|
+
start = end + 1;
|
|
4503
|
+
if (record.length === 0) continue;
|
|
4504
|
+
const tab = record.indexOf(9);
|
|
4505
|
+
if (tab < 0) throw new WorktreeAdapterError("Git tree entry did not contain a path");
|
|
4506
|
+
const metadata = record.subarray(0, tab).toString("ascii").split(" ");
|
|
4507
|
+
if (metadata.length !== 3) throw new WorktreeAdapterError("Git tree entry was malformed");
|
|
4508
|
+
const mode = metadata[0];
|
|
4509
|
+
const type = metadata[1];
|
|
4510
|
+
const objectId = metadata[2];
|
|
4511
|
+
if (!mode || !type || !objectId) {
|
|
4512
|
+
throw new WorktreeAdapterError("Git tree entry was malformed");
|
|
4513
|
+
}
|
|
4514
|
+
const pathBytes = record.subarray(tab + 1);
|
|
4515
|
+
const path = pathBytes.toString("utf8");
|
|
4516
|
+
if (!Buffer.from(path, "utf8").equals(pathBytes)) {
|
|
4517
|
+
throw new WorktreeAdapterError("CodeSurface paths must be valid UTF-8");
|
|
4518
|
+
}
|
|
4519
|
+
if (type !== "blob" && type !== "commit") {
|
|
4520
|
+
throw new WorktreeAdapterError(
|
|
4521
|
+
`CodeSurface contains unsupported Git object type ${type} at ${displayGitPath(path)}`
|
|
4522
|
+
);
|
|
4523
|
+
}
|
|
4524
|
+
entries.push({ mode, objectId, path });
|
|
4525
|
+
}
|
|
4526
|
+
return entries;
|
|
4527
|
+
}
|
|
4528
|
+
function parseGitPathList(bytes) {
|
|
4529
|
+
const input = Buffer.from(bytes);
|
|
4530
|
+
const paths = [];
|
|
4531
|
+
let start = 0;
|
|
4532
|
+
while (start < input.length) {
|
|
4533
|
+
const end = input.indexOf(0, start);
|
|
4534
|
+
if (end < 0) throw new WorktreeAdapterError("Git path output was not NUL-terminated");
|
|
4535
|
+
const pathBytes = input.subarray(start, end);
|
|
4536
|
+
start = end + 1;
|
|
4537
|
+
if (pathBytes.length === 0) continue;
|
|
4538
|
+
const path = pathBytes.toString("utf8");
|
|
4539
|
+
if (!Buffer.from(path, "utf8").equals(pathBytes)) {
|
|
4540
|
+
throw new WorktreeAdapterError("CodeSurface paths must be valid UTF-8");
|
|
4541
|
+
}
|
|
4542
|
+
paths.push(path);
|
|
4543
|
+
}
|
|
4544
|
+
return paths;
|
|
4545
|
+
}
|
|
4546
|
+
function isWithinRoot(root, candidate) {
|
|
4547
|
+
const rel = relative2(root, candidate);
|
|
4548
|
+
return rel === "" || !isAbsolute2(rel) && rel !== ".." && !rel.startsWith(`..${sep}`);
|
|
4549
|
+
}
|
|
4550
|
+
function assertSafeRelativePath(root, path) {
|
|
4551
|
+
if (path.length === 0 || isAbsolute2(path)) {
|
|
4552
|
+
throw new WorktreeAdapterError(`CodeSurface contains unsafe path ${displayGitPath(path)}`);
|
|
4553
|
+
}
|
|
4554
|
+
const absolutePath = resolve2(root, path);
|
|
4555
|
+
if (!isWithinRoot(root, absolutePath) || absolutePath === root) {
|
|
4556
|
+
throw new WorktreeAdapterError(`CodeSurface path escapes its worktree: ${displayGitPath(path)}`);
|
|
4557
|
+
}
|
|
4558
|
+
const segments = path.split(/[\\/]/u);
|
|
4559
|
+
let parent = root;
|
|
4560
|
+
for (const segment of segments.slice(0, -1)) {
|
|
4561
|
+
if (segment.length === 0 || segment === "." || segment === "..") {
|
|
4562
|
+
throw new WorktreeAdapterError(`CodeSurface contains unsafe path ${displayGitPath(path)}`);
|
|
4563
|
+
}
|
|
4564
|
+
parent = join5(parent, segment);
|
|
4565
|
+
const stat = lstatSync(parent);
|
|
4566
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
4567
|
+
throw new WorktreeAdapterError(
|
|
4568
|
+
`CodeSurface path traverses a non-directory or symbolic link: ${displayGitPath(path)}`
|
|
4569
|
+
);
|
|
4570
|
+
}
|
|
4571
|
+
}
|
|
4572
|
+
return absolutePath;
|
|
4573
|
+
}
|
|
4574
|
+
function gitObjectHashAlgorithm(objectId) {
|
|
4575
|
+
if (/^[a-f0-9]{40}$/.test(objectId)) return "sha1";
|
|
4576
|
+
if (/^[a-f0-9]{64}$/.test(objectId)) return "sha256";
|
|
4577
|
+
throw new WorktreeAdapterError(`Unsupported Git object id: ${objectId}`);
|
|
4578
|
+
}
|
|
4579
|
+
function hashGitBlobBytes(bytes, objectId) {
|
|
4580
|
+
const hash = createHash7(gitObjectHashAlgorithm(objectId));
|
|
4581
|
+
hash.update(`blob ${bytes.byteLength}\0`);
|
|
4582
|
+
hash.update(bytes);
|
|
4583
|
+
return hash.digest("hex");
|
|
4584
|
+
}
|
|
4585
|
+
function hashGitBlobFile(path, objectId) {
|
|
4586
|
+
const before = lstatSync(path);
|
|
4587
|
+
if (!before.isFile() || before.isSymbolicLink()) {
|
|
4588
|
+
throw new WorktreeAdapterError(`CodeSurface expected a regular file at ${displayGitPath(path)}`);
|
|
4589
|
+
}
|
|
4590
|
+
const noFollow = process.platform === "win32" ? 0 : fsConstants.O_NOFOLLOW;
|
|
4591
|
+
const fd = openSync(path, fsConstants.O_RDONLY | noFollow);
|
|
4592
|
+
try {
|
|
4593
|
+
const opened = fstatSync(fd);
|
|
4594
|
+
if (!opened.isFile() || opened.dev !== before.dev || opened.ino !== before.ino || opened.size !== before.size) {
|
|
4595
|
+
throw new WorktreeAdapterError(
|
|
4596
|
+
`CodeSurface file changed while it was being verified: ${displayGitPath(path)}`
|
|
4597
|
+
);
|
|
4598
|
+
}
|
|
4599
|
+
const hash = createHash7(gitObjectHashAlgorithm(objectId));
|
|
4600
|
+
hash.update(`blob ${opened.size}\0`);
|
|
4601
|
+
const chunk = Buffer.allocUnsafe(FILE_HASH_CHUNK_BYTES);
|
|
4602
|
+
let total = 0;
|
|
4603
|
+
while (true) {
|
|
4604
|
+
const count = readSync(fd, chunk, 0, chunk.length, null);
|
|
4605
|
+
if (count === 0) break;
|
|
4606
|
+
total += count;
|
|
4607
|
+
hash.update(chunk.subarray(0, count));
|
|
4608
|
+
}
|
|
4609
|
+
if (total !== opened.size) {
|
|
4610
|
+
throw new WorktreeAdapterError(
|
|
4611
|
+
`CodeSurface file changed while it was being verified: ${displayGitPath(path)}`
|
|
4612
|
+
);
|
|
4613
|
+
}
|
|
4614
|
+
return { hash: hash.digest("hex"), executable: (opened.mode & 73) !== 0 };
|
|
4615
|
+
} finally {
|
|
4616
|
+
closeSync(fd);
|
|
4617
|
+
}
|
|
4618
|
+
}
|
|
4619
|
+
function assertSymlinkTargetIsBound(root, linkPath, trackedPaths) {
|
|
4620
|
+
const targetBytes = readlinkSync(linkPath, { encoding: "buffer" });
|
|
4621
|
+
const target = targetBytes.toString("utf8");
|
|
4622
|
+
if (!Buffer.from(target, "utf8").equals(targetBytes)) {
|
|
4623
|
+
throw new WorktreeAdapterError(
|
|
4624
|
+
`CodeSurface symbolic link has an unsafe target at ${displayGitPath(linkPath)}`
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
if (isAbsolute2(target)) {
|
|
4628
|
+
throw new WorktreeAdapterError(
|
|
4629
|
+
`CodeSurface symbolic link escapes its worktree at ${displayGitPath(linkPath)}`
|
|
4630
|
+
);
|
|
4631
|
+
}
|
|
4632
|
+
const lexicalTarget = resolve2(dirname2(linkPath), target);
|
|
4633
|
+
if (!isWithinRoot(root, lexicalTarget)) {
|
|
4634
|
+
throw new WorktreeAdapterError(
|
|
4635
|
+
`CodeSurface symbolic link escapes its worktree at ${displayGitPath(linkPath)}`
|
|
4636
|
+
);
|
|
4637
|
+
}
|
|
4638
|
+
let resolvedTarget;
|
|
4639
|
+
try {
|
|
4640
|
+
resolvedTarget = realpathSync(linkPath);
|
|
4641
|
+
} catch (err) {
|
|
4642
|
+
throw new WorktreeAdapterError(
|
|
4643
|
+
`CodeSurface symbolic link target is missing or cyclic at ${displayGitPath(linkPath)}`,
|
|
4644
|
+
err
|
|
4645
|
+
);
|
|
4646
|
+
}
|
|
4647
|
+
if (!isWithinRoot(root, resolvedTarget)) {
|
|
4648
|
+
throw new WorktreeAdapterError(
|
|
4649
|
+
`CodeSurface symbolic link escapes its worktree at ${displayGitPath(linkPath)}`
|
|
4650
|
+
);
|
|
4651
|
+
}
|
|
4652
|
+
const targetRelative = relative2(root, resolvedTarget).split(sep).join("/");
|
|
4653
|
+
const targetIsTracked = trackedPaths.has(targetRelative) || [...trackedPaths].some((trackedPath) => trackedPath.startsWith(`${targetRelative}/`));
|
|
4654
|
+
if (!targetIsTracked) {
|
|
4655
|
+
throw new WorktreeAdapterError(
|
|
4656
|
+
`CodeSurface symbolic link resolves to untracked content at ${displayGitPath(linkPath)}`
|
|
4657
|
+
);
|
|
4658
|
+
}
|
|
4659
|
+
}
|
|
4660
|
+
function assertRawTreeMatchesWorktree(git, root, candidateCommit) {
|
|
4661
|
+
const entries = parseGitTreeEntries(
|
|
4662
|
+
gitBytes(git, ["ls-tree", "-r", "-z", "--full-tree", candidateCommit], root)
|
|
4663
|
+
);
|
|
4664
|
+
const trackedPaths = new Set(entries.map((entry) => entry.path));
|
|
4665
|
+
for (const entry of entries) {
|
|
4666
|
+
const absolutePath = assertSafeRelativePath(root, entry.path);
|
|
4667
|
+
if (entry.mode === "160000" || entry.mode === "040000") {
|
|
4668
|
+
throw new WorktreeAdapterError(
|
|
4669
|
+
`CodeSurface contains a Git submodule whose executable bytes are not bound: ${displayGitPath(entry.path)}`
|
|
4670
|
+
);
|
|
4671
|
+
}
|
|
4672
|
+
if (entry.mode === "120000") {
|
|
4673
|
+
const stat = lstatSync(absolutePath);
|
|
4674
|
+
if (!stat.isSymbolicLink()) {
|
|
4675
|
+
throw new WorktreeAdapterError(
|
|
4676
|
+
`CodeSurface expected a symbolic link at ${displayGitPath(entry.path)}`
|
|
4677
|
+
);
|
|
4678
|
+
}
|
|
4679
|
+
const targetBytes = readlinkSync(absolutePath, { encoding: "buffer" });
|
|
4680
|
+
const actualObjectId = hashGitBlobBytes(targetBytes, entry.objectId);
|
|
4681
|
+
if (actualObjectId !== entry.objectId) {
|
|
4682
|
+
throw new WorktreeAdapterError(
|
|
4683
|
+
`CodeSurface raw symbolic-link bytes differ from the candidate tree at ${displayGitPath(entry.path)}`
|
|
4684
|
+
);
|
|
4685
|
+
}
|
|
4686
|
+
assertSymlinkTargetIsBound(root, absolutePath, trackedPaths);
|
|
4687
|
+
continue;
|
|
4688
|
+
}
|
|
4689
|
+
if (entry.mode !== "100644" && entry.mode !== "100755") {
|
|
4690
|
+
throw new WorktreeAdapterError(
|
|
4691
|
+
`CodeSurface contains unsupported Git mode ${entry.mode} at ${displayGitPath(entry.path)}`
|
|
4692
|
+
);
|
|
4693
|
+
}
|
|
4694
|
+
const actual = hashGitBlobFile(absolutePath, entry.objectId);
|
|
4695
|
+
if (actual.hash !== entry.objectId) {
|
|
4696
|
+
throw new WorktreeAdapterError(
|
|
4697
|
+
`CodeSurface raw tracked bytes differ from the candidate tree because the worktree changed after finalization at ${displayGitPath(entry.path)}`
|
|
4698
|
+
);
|
|
4699
|
+
}
|
|
4700
|
+
if (process.platform !== "win32" && actual.executable !== (entry.mode === "100755")) {
|
|
4701
|
+
throw new WorktreeAdapterError(
|
|
4702
|
+
`CodeSurface executable mode differs from the candidate tree at ${displayGitPath(entry.path)}`
|
|
4703
|
+
);
|
|
4704
|
+
}
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
function verifyCodeSurfaceWithGit(surface, path, git) {
|
|
4708
|
+
assertCodeSurfaceIdentity(surface);
|
|
4709
|
+
if (!existsSync4(path)) {
|
|
4710
|
+
throw new WorktreeAdapterError(`CodeSurface worktree does not exist: ${path}`);
|
|
4711
|
+
}
|
|
4712
|
+
const lexicalRoot = resolve2(path);
|
|
4713
|
+
const canonicalRoot = realpathSync(path);
|
|
4714
|
+
if (lstatSync(lexicalRoot).isSymbolicLink() || process.platform !== "win32" && lexicalRoot !== canonicalRoot) {
|
|
4715
|
+
throw new WorktreeAdapterError(
|
|
4716
|
+
`CodeSurface worktree locator must not contain a symbolic link: ${path}`
|
|
4717
|
+
);
|
|
4718
|
+
}
|
|
4719
|
+
const repoRoot = gitText(git, ["rev-parse", "--show-toplevel"], path);
|
|
4720
|
+
const canonicalRepoRoot = realpathSync(repoRoot);
|
|
4721
|
+
if (canonicalRepoRoot !== canonicalRoot) {
|
|
4722
|
+
throw new WorktreeAdapterError(
|
|
4723
|
+
`CodeSurface worktree locator is not the repository root: expected ${repoRoot}, got ${path}`
|
|
4724
|
+
);
|
|
4725
|
+
}
|
|
4726
|
+
const indexFlags = gitText(git, ["ls-files", "-v"], path).split("\n").filter((line) => line.length > 0 && (line.startsWith("S ") || /^[a-z]/.test(line)));
|
|
4727
|
+
if (indexFlags.length > 0) {
|
|
4728
|
+
throw new WorktreeAdapterError(
|
|
4729
|
+
`CodeSurface worktree uses hidden index entries that cannot be verified: ${path}
|
|
4730
|
+
${indexFlags.join("\n")}`
|
|
4731
|
+
);
|
|
4732
|
+
}
|
|
4733
|
+
const extraPaths = [
|
|
4734
|
+
...parseGitPathList(gitBytes(git, ["ls-files", "--others", "--exclude-standard", "-z"], path)),
|
|
4735
|
+
...parseGitPathList(
|
|
4736
|
+
gitBytes(git, ["ls-files", "--others", "--ignored", "--exclude-standard", "-z"], path)
|
|
4737
|
+
)
|
|
4738
|
+
];
|
|
4739
|
+
if (extraPaths.length > 0) {
|
|
4740
|
+
throw new WorktreeAdapterError(
|
|
4741
|
+
`CodeSurface worktree changed after finalization: ${path}
|
|
4742
|
+
${extraPaths.join("\n")}`
|
|
4743
|
+
);
|
|
4744
|
+
}
|
|
4745
|
+
const candidateCommit = resolveCommit(git, path, "HEAD");
|
|
4746
|
+
if (candidateCommit !== surface.candidateCommit) {
|
|
4747
|
+
throw new WorktreeAdapterError(
|
|
4748
|
+
`CodeSurface candidate commit mismatch: expected ${surface.candidateCommit}, got ${candidateCommit}`
|
|
4749
|
+
);
|
|
4750
|
+
}
|
|
4751
|
+
const baseCommit = resolveCommit(git, path, surface.baseCommit);
|
|
4752
|
+
if (baseCommit !== surface.baseCommit) {
|
|
4753
|
+
throw new WorktreeAdapterError(
|
|
4754
|
+
`CodeSurface base commit mismatch: expected ${surface.baseCommit}, got ${baseCommit}`
|
|
4755
|
+
);
|
|
4756
|
+
}
|
|
4757
|
+
const baseTree = gitText(git, ["rev-parse", "--verify", `${surface.baseCommit}^{tree}`], path);
|
|
4758
|
+
if (baseTree !== surface.baseTree) {
|
|
4759
|
+
throw new WorktreeAdapterError(
|
|
4760
|
+
`CodeSurface base tree mismatch: expected ${surface.baseTree}, got ${baseTree}`
|
|
4761
|
+
);
|
|
4762
|
+
}
|
|
4763
|
+
const candidateTree = gitText(
|
|
4764
|
+
git,
|
|
4765
|
+
["rev-parse", "--verify", `${surface.candidateCommit}^{tree}`],
|
|
4766
|
+
path
|
|
4767
|
+
);
|
|
4768
|
+
if (candidateTree !== surface.candidateTree) {
|
|
4769
|
+
throw new WorktreeAdapterError(
|
|
4770
|
+
`CodeSurface tree mismatch: expected ${surface.candidateTree}, got ${candidateTree}`
|
|
4771
|
+
);
|
|
4772
|
+
}
|
|
4773
|
+
try {
|
|
4774
|
+
gitText(
|
|
4775
|
+
git,
|
|
4776
|
+
[
|
|
4777
|
+
"diff-index",
|
|
4778
|
+
"--cached",
|
|
4779
|
+
"--quiet",
|
|
4780
|
+
"--no-ext-diff",
|
|
4781
|
+
"--no-textconv",
|
|
4782
|
+
surface.candidateCommit,
|
|
4783
|
+
"--"
|
|
4784
|
+
],
|
|
4785
|
+
path
|
|
4786
|
+
);
|
|
4787
|
+
} catch (err) {
|
|
4788
|
+
throw new WorktreeAdapterError(
|
|
4789
|
+
`CodeSurface index differs from finalized candidate: ${path}`,
|
|
4790
|
+
err
|
|
4791
|
+
);
|
|
4792
|
+
}
|
|
4793
|
+
try {
|
|
4794
|
+
assertRawTreeMatchesWorktree(git, canonicalRepoRoot, surface.candidateCommit);
|
|
4795
|
+
} catch (err) {
|
|
4796
|
+
if (err instanceof WorktreeAdapterError) throw err;
|
|
4797
|
+
throw new WorktreeAdapterError(`CodeSurface raw tree verification failed: ${path}`, err);
|
|
4798
|
+
}
|
|
4799
|
+
const mergeBase = gitText(git, ["merge-base", surface.baseCommit, surface.candidateCommit], path);
|
|
4800
|
+
if (mergeBase !== surface.baseCommit) {
|
|
4801
|
+
throw new WorktreeAdapterError(
|
|
4802
|
+
`CodeSurface candidate ${surface.candidateCommit} does not descend from base ${surface.baseCommit}`
|
|
4803
|
+
);
|
|
4804
|
+
}
|
|
4805
|
+
const patch = patchBytes(git, path, surface.baseCommit, surface.candidateCommit);
|
|
4806
|
+
const actualPatchHash = sha2562(patch);
|
|
4807
|
+
if (actualPatchHash !== surface.patch.sha256 || patch.byteLength !== surface.patch.byteLength) {
|
|
4808
|
+
throw new WorktreeAdapterError(
|
|
4809
|
+
`CodeSurface patch mismatch: expected ${surface.patch.sha256}/${surface.patch.byteLength} bytes, got ${actualPatchHash}/${patch.byteLength} bytes`
|
|
4810
|
+
);
|
|
4811
|
+
}
|
|
4812
|
+
return {
|
|
4813
|
+
path,
|
|
4814
|
+
repoRoot: canonicalRepoRoot,
|
|
4815
|
+
contentHash: surfaceContentHash(surface),
|
|
4816
|
+
patchBytes: new Uint8Array(patch)
|
|
4817
|
+
};
|
|
4818
|
+
}
|
|
4359
4819
|
function slug2(label) {
|
|
4360
4820
|
return label.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 48) || "candidate";
|
|
4361
4821
|
}
|
|
@@ -4368,32 +4828,62 @@ function gitWorktreeAdapter(opts) {
|
|
|
4368
4828
|
const id = `${slug2(label)}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
4369
4829
|
const branch = `${branchPrefix}/${id}`;
|
|
4370
4830
|
const path = join5(worktreeDir, id);
|
|
4371
|
-
|
|
4372
|
-
|
|
4831
|
+
const baseCommit = resolveCommit(git, opts.repoRoot, baseRef);
|
|
4832
|
+
const baseTree = gitText(
|
|
4833
|
+
git,
|
|
4834
|
+
["rev-parse", "--verify", `${baseCommit}^{tree}`],
|
|
4835
|
+
opts.repoRoot
|
|
4836
|
+
);
|
|
4837
|
+
gitText(git, ["worktree", "add", "-b", branch, path, baseCommit], opts.repoRoot);
|
|
4838
|
+
return { path, branch, baseRef, baseCommit, baseTree };
|
|
4373
4839
|
},
|
|
4374
4840
|
async finalize(worktree, summary) {
|
|
4375
|
-
const status =
|
|
4841
|
+
const status = gitText(
|
|
4842
|
+
git,
|
|
4843
|
+
["status", "--porcelain=v1", "--untracked-files=all"],
|
|
4844
|
+
worktree.path
|
|
4845
|
+
);
|
|
4376
4846
|
if (status.length > 0) {
|
|
4377
|
-
git
|
|
4378
|
-
git
|
|
4847
|
+
gitText(git, ["add", "-A"], worktree.path);
|
|
4848
|
+
gitText(git, ["commit", "-m", summary], worktree.path);
|
|
4379
4849
|
}
|
|
4380
|
-
|
|
4850
|
+
const candidateCommit = resolveCommit(git, worktree.path, "HEAD");
|
|
4851
|
+
const candidateTree = gitText(
|
|
4852
|
+
git,
|
|
4853
|
+
["rev-parse", "--verify", `${candidateCommit}^{tree}`],
|
|
4854
|
+
worktree.path
|
|
4855
|
+
);
|
|
4856
|
+
const patch = patchBytes(git, worktree.path, worktree.baseCommit, candidateCommit);
|
|
4857
|
+
const surface = {
|
|
4381
4858
|
kind: "code",
|
|
4382
4859
|
worktreeRef: worktree.path,
|
|
4383
4860
|
baseRef: worktree.baseRef,
|
|
4861
|
+
baseCommit: worktree.baseCommit,
|
|
4862
|
+
baseTree: worktree.baseTree,
|
|
4863
|
+
candidateCommit,
|
|
4864
|
+
candidateTree,
|
|
4865
|
+
patch: {
|
|
4866
|
+
format: "git-diff-binary",
|
|
4867
|
+
sha256: sha2562(patch),
|
|
4868
|
+
byteLength: patch.byteLength
|
|
4869
|
+
},
|
|
4384
4870
|
summary
|
|
4385
4871
|
};
|
|
4872
|
+
verifyCodeSurfaceWithGit(surface, worktree.path, git);
|
|
4873
|
+
return surface;
|
|
4386
4874
|
},
|
|
4387
4875
|
async discard(worktree) {
|
|
4388
|
-
git
|
|
4389
|
-
git
|
|
4876
|
+
gitText(git, ["worktree", "remove", "--force", worktree.path], opts.repoRoot);
|
|
4877
|
+
gitText(git, ["branch", "-D", worktree.branch], opts.repoRoot);
|
|
4390
4878
|
}
|
|
4391
4879
|
};
|
|
4392
4880
|
}
|
|
4881
|
+
function verifyCodeSurface(surface, worktreeDir) {
|
|
4882
|
+
const path = unresolvedWorktreePath(surface, worktreeDir);
|
|
4883
|
+
return verifyCodeSurfaceWithGit(surface, path, defaultGit);
|
|
4884
|
+
}
|
|
4393
4885
|
function resolveWorktreePath(surface, worktreeDir) {
|
|
4394
|
-
|
|
4395
|
-
if (worktreeDir) return join5(worktreeDir, basename(surface.worktreeRef));
|
|
4396
|
-
return surface.worktreeRef;
|
|
4886
|
+
return verifyCodeSurface(surface, worktreeDir).path;
|
|
4397
4887
|
}
|
|
4398
4888
|
|
|
4399
4889
|
export {
|
|
@@ -4473,6 +4963,7 @@ export {
|
|
|
4473
4963
|
isTransientTransportFailure,
|
|
4474
4964
|
WorktreeAdapterError,
|
|
4475
4965
|
gitWorktreeAdapter,
|
|
4966
|
+
verifyCodeSurface,
|
|
4476
4967
|
resolveWorktreePath
|
|
4477
4968
|
};
|
|
4478
|
-
//# sourceMappingURL=chunk-
|
|
4969
|
+
//# sourceMappingURL=chunk-VHPD6AXX.js.map
|