@fro.bot/systematic 3.5.5 → 3.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +892 -150
- package/dist/lib/opencode-operation-observer.d.ts +22 -0
- package/dist/lib/opencode-workflow-guard.d.ts +18 -1
- package/dist/lib/receipt-ledger.d.ts +13 -6
- package/dist/lib/receipt-readback.d.ts +23 -11
- package/dist/lib/workflow-guard.d.ts +2 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -19,8 +19,8 @@ import {
|
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import { createHash as createHash5 } from "crypto";
|
|
22
|
-
import
|
|
23
|
-
import
|
|
22
|
+
import fs12 from "fs";
|
|
23
|
+
import path11 from "path";
|
|
24
24
|
import { fileURLToPath as fileURLToPath3, pathToFileURL as pathToFileURL3 } from "url";
|
|
25
25
|
|
|
26
26
|
// src/lib/bootstrap.ts
|
|
@@ -1333,10 +1333,20 @@ function digest(domain, facts) {
|
|
|
1333
1333
|
function isCommandTimeout(error) {
|
|
1334
1334
|
return error !== null && typeof error === "object" && "code" in error && error.code === "ETIMEDOUT";
|
|
1335
1335
|
}
|
|
1336
|
+
function sanitizedEnvironment() {
|
|
1337
|
+
const env = { ...process.env };
|
|
1338
|
+
for (const key of Object.keys(env)) {
|
|
1339
|
+
if (key.startsWith("GIT_")) {
|
|
1340
|
+
delete env[key];
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return env;
|
|
1344
|
+
}
|
|
1336
1345
|
function defaultCommandRunner(args2, cwd, maxOutputBytes, timeoutMs) {
|
|
1337
1346
|
try {
|
|
1338
1347
|
const result = spawnSync("git", [...args2], {
|
|
1339
1348
|
cwd,
|
|
1349
|
+
env: sanitizedEnvironment(),
|
|
1340
1350
|
encoding: "utf8",
|
|
1341
1351
|
maxBuffer: maxOutputBytes,
|
|
1342
1352
|
timeout: timeoutMs
|
|
@@ -1355,6 +1365,7 @@ function defaultRemoteCommandRunner(executable, args2, cwd, maxOutputBytes, time
|
|
|
1355
1365
|
try {
|
|
1356
1366
|
const result = spawnSync(executable, [...args2], {
|
|
1357
1367
|
cwd,
|
|
1368
|
+
env: sanitizedEnvironment(),
|
|
1358
1369
|
encoding: "utf8",
|
|
1359
1370
|
maxBuffer: maxOutputBytes,
|
|
1360
1371
|
timeout: timeoutMs
|
|
@@ -1429,6 +1440,140 @@ function runCommand(runner, args2, cwd, limits) {
|
|
|
1429
1440
|
}
|
|
1430
1441
|
return { status: "ok", output: { stdout: result.stdout } };
|
|
1431
1442
|
}
|
|
1443
|
+
function canonicalPath(filePath, realPath = fs8.realpathSync) {
|
|
1444
|
+
try {
|
|
1445
|
+
return realPath(filePath);
|
|
1446
|
+
} catch {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
function requiredGitPath(runner, args2, cwd, limits, realPath = fs8.realpathSync) {
|
|
1451
|
+
const result = runCommand(runner, args2, cwd, limits);
|
|
1452
|
+
if (result.status === "error")
|
|
1453
|
+
return result;
|
|
1454
|
+
const rawValue = result.output.stdout.trim();
|
|
1455
|
+
if (!path8.isAbsolute(rawValue)) {
|
|
1456
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1457
|
+
}
|
|
1458
|
+
const value = canonicalPath(rawValue, realPath);
|
|
1459
|
+
return value === undefined ? { status: "error", reasonCode: "target-unavailable" } : { status: "ok", value };
|
|
1460
|
+
}
|
|
1461
|
+
function captureParentIdentity(targetDirectory, runner, limits, realPath = fs8.realpathSync) {
|
|
1462
|
+
const targetRoot = requiredGitPath(runner, ["rev-parse", "--show-toplevel"], targetDirectory, limits, realPath);
|
|
1463
|
+
if (targetRoot.status === "error")
|
|
1464
|
+
return targetRoot;
|
|
1465
|
+
const gitDir = requiredGitPath(runner, ["rev-parse", "--absolute-git-dir"], targetDirectory, limits, realPath);
|
|
1466
|
+
if (gitDir.status === "error")
|
|
1467
|
+
return gitDir;
|
|
1468
|
+
const commonDir = requiredGitPath(runner, ["rev-parse", "--path-format=absolute", "--git-common-dir"], targetDirectory, limits, realPath);
|
|
1469
|
+
if (commonDir.status === "error")
|
|
1470
|
+
return commonDir;
|
|
1471
|
+
const worktreeList = runCommand(runner, ["worktree", "list", "--porcelain", "-z"], targetDirectory, limits);
|
|
1472
|
+
if (worktreeList.status === "error")
|
|
1473
|
+
return worktreeList;
|
|
1474
|
+
const registeredWorktreeRoots = worktreeList.output.stdout.split("\x00").filter((record) => record.startsWith("worktree ")).map((record) => canonicalPath(record.slice("worktree ".length), realPath));
|
|
1475
|
+
if (registeredWorktreeRoots.some((root) => root === undefined)) {
|
|
1476
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1477
|
+
}
|
|
1478
|
+
const roots = registeredWorktreeRoots.filter((root) => root !== undefined);
|
|
1479
|
+
return roots.length > 0 ? {
|
|
1480
|
+
status: "ok",
|
|
1481
|
+
identity: {
|
|
1482
|
+
targetRoot: targetRoot.value,
|
|
1483
|
+
gitDir: gitDir.value,
|
|
1484
|
+
commonDir: commonDir.value,
|
|
1485
|
+
registeredWorktreeRoots: roots
|
|
1486
|
+
}
|
|
1487
|
+
} : { status: "error", reasonCode: "target-unavailable" };
|
|
1488
|
+
}
|
|
1489
|
+
function readGitfileTarget(gitfilePath, realPath = fs8.realpathSync) {
|
|
1490
|
+
let contents;
|
|
1491
|
+
try {
|
|
1492
|
+
contents = fs8.readFileSync(gitfilePath, "utf8");
|
|
1493
|
+
} catch {
|
|
1494
|
+
return;
|
|
1495
|
+
}
|
|
1496
|
+
const match = /^gitdir:\s*(.+?)\s*$/im.exec(contents);
|
|
1497
|
+
if (!match)
|
|
1498
|
+
return;
|
|
1499
|
+
const target = path8.isAbsolute(match[1]) ? match[1] : path8.resolve(path8.dirname(gitfilePath), match[1]);
|
|
1500
|
+
return canonicalPath(target, realPath);
|
|
1501
|
+
}
|
|
1502
|
+
function readGitdirBacklink(backlinkPath, realPath) {
|
|
1503
|
+
let contents;
|
|
1504
|
+
try {
|
|
1505
|
+
contents = fs8.readFileSync(backlinkPath, "utf8").trim();
|
|
1506
|
+
} catch {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
if (contents.length === 0)
|
|
1510
|
+
return;
|
|
1511
|
+
const target = path8.isAbsolute(contents) ? contents : path8.resolve(path8.dirname(backlinkPath), contents);
|
|
1512
|
+
return canonicalPath(target, realPath);
|
|
1513
|
+
}
|
|
1514
|
+
function isPathWithin(root, candidate) {
|
|
1515
|
+
const relative = path8.relative(root, candidate);
|
|
1516
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path8.sep}`) && !path8.isAbsolute(relative);
|
|
1517
|
+
}
|
|
1518
|
+
function validateDotGitLinkage(targetRoot, gitDir, commonDir, realPath) {
|
|
1519
|
+
const dotGit = path8.join(targetRoot, ".git");
|
|
1520
|
+
let dotGitStat;
|
|
1521
|
+
try {
|
|
1522
|
+
dotGitStat = fs8.lstatSync(dotGit);
|
|
1523
|
+
} catch {
|
|
1524
|
+
return false;
|
|
1525
|
+
}
|
|
1526
|
+
if (dotGitStat.isDirectory()) {
|
|
1527
|
+
return gitDir === commonDir && canonicalPath(dotGit, realPath) === commonDir;
|
|
1528
|
+
}
|
|
1529
|
+
if (!dotGitStat.isFile() || dotGitStat.isSymbolicLink() || !isPathWithin(path8.join(commonDir, "worktrees"), gitDir) || readGitfileTarget(dotGit, realPath) !== gitDir) {
|
|
1530
|
+
return false;
|
|
1531
|
+
}
|
|
1532
|
+
const backlinkPath = path8.join(gitDir, "gitdir");
|
|
1533
|
+
let backlinkStat;
|
|
1534
|
+
try {
|
|
1535
|
+
backlinkStat = fs8.lstatSync(backlinkPath);
|
|
1536
|
+
} catch {
|
|
1537
|
+
return false;
|
|
1538
|
+
}
|
|
1539
|
+
return backlinkStat.isFile() && !backlinkStat.isSymbolicLink() && readGitdirBacklink(backlinkPath, realPath) === canonicalPath(dotGit, realPath);
|
|
1540
|
+
}
|
|
1541
|
+
function validateRegisteredWorktree(candidateDirectory, parentIdentity, options = {}) {
|
|
1542
|
+
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
1543
|
+
const limits = mergeLimits(options.limits);
|
|
1544
|
+
const realPath = options.realPath ?? fs8.realpathSync;
|
|
1545
|
+
const candidateRoot = canonicalPath(candidateDirectory, realPath);
|
|
1546
|
+
if (candidateRoot === undefined) {
|
|
1547
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1548
|
+
}
|
|
1549
|
+
const inside = runCommand(runner, ["rev-parse", "--is-inside-work-tree"], candidateRoot, limits);
|
|
1550
|
+
if (inside.status === "error")
|
|
1551
|
+
return inside;
|
|
1552
|
+
if (inside.output.stdout.trim() !== "true") {
|
|
1553
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1554
|
+
}
|
|
1555
|
+
const targetRoot = requiredGitPath(runner, ["rev-parse", "--show-toplevel"], candidateRoot, limits, realPath);
|
|
1556
|
+
if (targetRoot.status === "error")
|
|
1557
|
+
return targetRoot;
|
|
1558
|
+
const gitDir = requiredGitPath(runner, ["rev-parse", "--absolute-git-dir"], candidateRoot, limits, realPath);
|
|
1559
|
+
if (gitDir.status === "error")
|
|
1560
|
+
return gitDir;
|
|
1561
|
+
const commonDir = requiredGitPath(runner, ["rev-parse", "--path-format=absolute", "--git-common-dir"], candidateRoot, limits, realPath);
|
|
1562
|
+
if (commonDir.status === "error")
|
|
1563
|
+
return commonDir;
|
|
1564
|
+
if (commonDir.value !== parentIdentity.commonDir || !parentIdentity.registeredWorktreeRoots.includes(targetRoot.value)) {
|
|
1565
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1566
|
+
}
|
|
1567
|
+
if (!validateDotGitLinkage(targetRoot.value, gitDir.value, commonDir.value, realPath)) {
|
|
1568
|
+
return { status: "error", reasonCode: "target-unavailable" };
|
|
1569
|
+
}
|
|
1570
|
+
return {
|
|
1571
|
+
status: "ok",
|
|
1572
|
+
targetRoot: targetRoot.value,
|
|
1573
|
+
gitDir: gitDir.value,
|
|
1574
|
+
commonDir: commonDir.value
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1432
1577
|
function readRevision(runner, context, limits) {
|
|
1433
1578
|
const branch = runCommand(runner, ["symbolic-ref", "--short", "HEAD"], context.commandDirectory, limits);
|
|
1434
1579
|
if (branch.status === "error" && branch.reasonCode !== "command-failed") {
|
|
@@ -1785,9 +1930,11 @@ function createOpencodeOperationObserver(options) {
|
|
|
1785
1930
|
} catch {
|
|
1786
1931
|
targetDirectory = path8.resolve(options.targetDirectory);
|
|
1787
1932
|
}
|
|
1788
|
-
const targetDigest = digest("target", [targetDirectory]);
|
|
1789
1933
|
const runner = options.commandRunner ?? defaultCommandRunner;
|
|
1790
1934
|
const remoteRunner = options.remoteCommandRunner ?? defaultRemoteCommandRunner;
|
|
1935
|
+
const realPath = options.realPath ?? fs8.realpathSync;
|
|
1936
|
+
const parentIdentityResult = captureParentIdentity(targetDirectory, runner, limits, realPath);
|
|
1937
|
+
const targetDigest = digest("target", [targetDirectory]);
|
|
1791
1938
|
const fileReader = options.fileReader ?? fs8.readFileSync;
|
|
1792
1939
|
const symlinkReader = options.symlinkReader ?? fs8.readlinkSync;
|
|
1793
1940
|
const statReader = options.statReader ?? ((filePath) => {
|
|
@@ -1802,6 +1949,11 @@ function createOpencodeOperationObserver(options) {
|
|
|
1802
1949
|
});
|
|
1803
1950
|
return {
|
|
1804
1951
|
targetDigest,
|
|
1952
|
+
validateRegisteredWorktree(candidateDirectory) {
|
|
1953
|
+
if (parentIdentityResult.status === "error")
|
|
1954
|
+
return parentIdentityResult;
|
|
1955
|
+
return validateRegisteredWorktree(candidateDirectory, parentIdentityResult.identity, { commandRunner: runner, limits, realPath });
|
|
1956
|
+
},
|
|
1805
1957
|
async snapshot() {
|
|
1806
1958
|
const rootResult = runCommand(runner, ["rev-parse", "--show-toplevel"], targetDirectory, limits);
|
|
1807
1959
|
if (rootResult.status === "error")
|
|
@@ -1849,6 +2001,8 @@ function createOpencodeOperationObserver(options) {
|
|
|
1849
2001
|
|
|
1850
2002
|
// src/lib/opencode-workflow-guard.ts
|
|
1851
2003
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
2004
|
+
import fs10 from "fs";
|
|
2005
|
+
import path9 from "path";
|
|
1852
2006
|
|
|
1853
2007
|
// src/lib/question-attestation.ts
|
|
1854
2008
|
import { createHash as createHash3, randomBytes } from "crypto";
|
|
@@ -2369,8 +2523,10 @@ import { randomBytes as randomBytes2 } from "crypto";
|
|
|
2369
2523
|
|
|
2370
2524
|
// src/lib/receipt-readback.ts
|
|
2371
2525
|
import { createHash as createHash4 } from "crypto";
|
|
2372
|
-
var RECEIPT_READBACK_SCHEMA_VERSION =
|
|
2373
|
-
var RECEIPT_READBACK_PROTOCOL_VERSION =
|
|
2526
|
+
var RECEIPT_READBACK_SCHEMA_VERSION = 2;
|
|
2527
|
+
var RECEIPT_READBACK_PROTOCOL_VERSION = 2;
|
|
2528
|
+
var LEGACY_RECEIPT_READBACK_SCHEMA_VERSION = 1;
|
|
2529
|
+
var LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION = 1;
|
|
2374
2530
|
var MAX_CAPABILITIES = 16;
|
|
2375
2531
|
var MAX_CAPABILITY_LENGTH = 128;
|
|
2376
2532
|
var MAX_MARKERS = 128;
|
|
@@ -2412,6 +2568,12 @@ var CANONICAL_REQUIRED_KEYS = [
|
|
|
2412
2568
|
"timestamp"
|
|
2413
2569
|
];
|
|
2414
2570
|
var CANONICAL_OPTIONAL_KEYS = [
|
|
2571
|
+
"repositoryDigest",
|
|
2572
|
+
"worktreeDigest",
|
|
2573
|
+
"operationTargetIdentity",
|
|
2574
|
+
"resourceDigest"
|
|
2575
|
+
];
|
|
2576
|
+
var LEGACY_CANONICAL_OPTIONAL_KEYS = [
|
|
2415
2577
|
"repositoryDigest",
|
|
2416
2578
|
"worktreeDigest",
|
|
2417
2579
|
"resourceDigest"
|
|
@@ -2465,12 +2627,14 @@ var UNIT_PROGRESSION_KEYS = [
|
|
|
2465
2627
|
"family",
|
|
2466
2628
|
"requiredOperations",
|
|
2467
2629
|
"resourceScopes",
|
|
2630
|
+
"pinnedOperationTargetIdentity",
|
|
2468
2631
|
"state",
|
|
2469
2632
|
"transitionDigest",
|
|
2470
2633
|
"timestamp",
|
|
2471
2634
|
"sessionSalt",
|
|
2472
2635
|
"integrity"
|
|
2473
2636
|
];
|
|
2637
|
+
var LEGACY_UNIT_PROGRESSION_KEYS = UNIT_PROGRESSION_KEYS.filter((key) => key !== "pinnedOperationTargetIdentity");
|
|
2474
2638
|
var RESOURCE_SCOPE_KEYS = ["operation", "resourceIdentity"];
|
|
2475
2639
|
var LEDGER_METADATA_KEYS = [
|
|
2476
2640
|
"schemaVersion",
|
|
@@ -2489,6 +2653,12 @@ function hasExactKeys(value, required, optional = []) {
|
|
|
2489
2653
|
const keys = Object.keys(value);
|
|
2490
2654
|
return required.every((key) => Object.hasOwn(value, key)) && keys.every((key) => allowed.has(key));
|
|
2491
2655
|
}
|
|
2656
|
+
function isCurrentVersionPair(schemaVersion, protocolVersion) {
|
|
2657
|
+
return schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION && protocolVersion === RECEIPT_READBACK_PROTOCOL_VERSION;
|
|
2658
|
+
}
|
|
2659
|
+
function isLegacyVersionPair(schemaVersion, protocolVersion) {
|
|
2660
|
+
return schemaVersion === LEGACY_RECEIPT_READBACK_SCHEMA_VERSION && protocolVersion === LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION;
|
|
2661
|
+
}
|
|
2492
2662
|
function isDigest(value) {
|
|
2493
2663
|
return typeof value === "string" && DIGEST_PATTERN.test(value);
|
|
2494
2664
|
}
|
|
@@ -2589,6 +2759,7 @@ function cloneCanonical(canonical) {
|
|
|
2589
2759
|
workspaceDigest: canonical.workspaceDigest,
|
|
2590
2760
|
repositoryDigest: canonical.repositoryDigest,
|
|
2591
2761
|
worktreeDigest: canonical.worktreeDigest,
|
|
2762
|
+
...isLocalOperation(canonical.operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
|
|
2592
2763
|
resourceDigest: canonical.resourceDigest,
|
|
2593
2764
|
operation: canonical.operation,
|
|
2594
2765
|
result: "success",
|
|
@@ -2610,16 +2781,19 @@ function cloneEnvelope(envelope) {
|
|
|
2610
2781
|
function parseEnvelope(value) {
|
|
2611
2782
|
if (!isRecord3(value) || !hasExactKeys(value, ENVELOPE_KEYS))
|
|
2612
2783
|
return;
|
|
2613
|
-
|
|
2784
|
+
const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2785
|
+
const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2786
|
+
if (!currentVersion && !legacyVersion || value.compatibility !== "compatible" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isRecord3(value.canonical)) {
|
|
2614
2787
|
return;
|
|
2615
2788
|
}
|
|
2616
2789
|
const canonical = value.canonical;
|
|
2617
|
-
|
|
2790
|
+
const operation = isOperation(canonical.operation) ? canonical.operation : undefined;
|
|
2791
|
+
if (!hasExactKeys(canonical, CANONICAL_REQUIRED_KEYS, currentVersion ? CANONICAL_OPTIONAL_KEYS : LEGACY_CANONICAL_OPTIONAL_KEYS) || !isReceiptId(canonical.receiptId) || !isDigest(canonical.registrationDigest) || !isDigest(canonical.callDigest) || !isDigest(canonical.epochDigest) || !isDigest(canonical.unitDigest) || !isDigest(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest(canonical.worktreeDigest) || canonical.operationTargetIdentity !== undefined && !isDigest(canonical.operationTargetIdentity) || operation === undefined || currentVersion && isLocalOperation(operation) && !isDigest(canonical.operationTargetIdentity) || currentVersion && !isLocalOperation(operation) && Object.hasOwn(canonical, "operationTargetIdentity") || legacyVersion && Object.hasOwn(canonical, "operationTargetIdentity") || canonical.resourceDigest !== undefined && !isDigest(canonical.resourceDigest) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || !isTimestamp(canonical.timestamp) || canonical.registrationDigest !== value.registrationDigest) {
|
|
2618
2792
|
return;
|
|
2619
2793
|
}
|
|
2620
2794
|
return {
|
|
2621
|
-
schemaVersion:
|
|
2622
|
-
protocolVersion:
|
|
2795
|
+
schemaVersion: currentVersion ? RECEIPT_READBACK_SCHEMA_VERSION : LEGACY_RECEIPT_READBACK_SCHEMA_VERSION,
|
|
2796
|
+
protocolVersion: currentVersion ? RECEIPT_READBACK_PROTOCOL_VERSION : LEGACY_RECEIPT_READBACK_PROTOCOL_VERSION,
|
|
2623
2797
|
registrationDigest: value.registrationDigest,
|
|
2624
2798
|
capabilityFlags: [...value.capabilityFlags],
|
|
2625
2799
|
compatibility: "compatible",
|
|
@@ -2632,8 +2806,9 @@ function parseEnvelope(value) {
|
|
|
2632
2806
|
workspaceDigest: canonical.workspaceDigest,
|
|
2633
2807
|
repositoryDigest: canonical.repositoryDigest,
|
|
2634
2808
|
worktreeDigest: canonical.worktreeDigest,
|
|
2809
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
|
|
2635
2810
|
resourceDigest: canonical.resourceDigest,
|
|
2636
|
-
operation
|
|
2811
|
+
operation,
|
|
2637
2812
|
result: "success",
|
|
2638
2813
|
source: "runtime-verified",
|
|
2639
2814
|
consumption: canonical.consumption,
|
|
@@ -2642,40 +2817,42 @@ function parseEnvelope(value) {
|
|
|
2642
2817
|
};
|
|
2643
2818
|
}
|
|
2644
2819
|
function parseLedgerMetadata(value) {
|
|
2645
|
-
if (!isRecord3(value) || !hasExactKeys(value, LEDGER_METADATA_KEYS) || value.schemaVersion !==
|
|
2820
|
+
if (!isRecord3(value) || !hasExactKeys(value, LEDGER_METADATA_KEYS) || value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION || value.protocolVersion !== RECEIPT_READBACK_PROTOCOL_VERSION || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags)) {
|
|
2646
2821
|
return;
|
|
2647
2822
|
}
|
|
2648
2823
|
return {
|
|
2649
|
-
schemaVersion:
|
|
2650
|
-
protocolVersion:
|
|
2824
|
+
schemaVersion: RECEIPT_READBACK_SCHEMA_VERSION,
|
|
2825
|
+
protocolVersion: RECEIPT_READBACK_PROTOCOL_VERSION,
|
|
2651
2826
|
registrationDigest: value.registrationDigest,
|
|
2652
2827
|
capabilityFlags: [...value.capabilityFlags]
|
|
2653
2828
|
};
|
|
2654
2829
|
}
|
|
2655
2830
|
function serializeEnvelope(envelope) {
|
|
2656
2831
|
const canonical = envelope.canonical;
|
|
2832
|
+
const canonicalFields = {
|
|
2833
|
+
receiptId: canonical.receiptId,
|
|
2834
|
+
registrationDigest: canonical.registrationDigest,
|
|
2835
|
+
callDigest: canonical.callDigest,
|
|
2836
|
+
epochDigest: canonical.epochDigest,
|
|
2837
|
+
unitDigest: canonical.unitDigest,
|
|
2838
|
+
workspaceDigest: canonical.workspaceDigest,
|
|
2839
|
+
repositoryDigest: canonical.repositoryDigest ?? null,
|
|
2840
|
+
worktreeDigest: canonical.worktreeDigest ?? null,
|
|
2841
|
+
...envelope.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? { operationTargetIdentity: canonical.operationTargetIdentity ?? null } : {},
|
|
2842
|
+
resourceDigest: canonical.resourceDigest ?? null,
|
|
2843
|
+
operation: canonical.operation,
|
|
2844
|
+
result: canonical.result,
|
|
2845
|
+
source: canonical.source,
|
|
2846
|
+
consumption: canonical.consumption,
|
|
2847
|
+
timestamp: canonical.timestamp
|
|
2848
|
+
};
|
|
2657
2849
|
return JSON.stringify({
|
|
2658
2850
|
schemaVersion: envelope.schemaVersion,
|
|
2659
2851
|
protocolVersion: envelope.protocolVersion,
|
|
2660
2852
|
registrationDigest: envelope.registrationDigest,
|
|
2661
2853
|
capabilityFlags: [...envelope.capabilityFlags],
|
|
2662
2854
|
compatibility: envelope.compatibility,
|
|
2663
|
-
canonical:
|
|
2664
|
-
receiptId: canonical.receiptId,
|
|
2665
|
-
registrationDigest: canonical.registrationDigest,
|
|
2666
|
-
callDigest: canonical.callDigest,
|
|
2667
|
-
epochDigest: canonical.epochDigest,
|
|
2668
|
-
unitDigest: canonical.unitDigest,
|
|
2669
|
-
workspaceDigest: canonical.workspaceDigest,
|
|
2670
|
-
repositoryDigest: canonical.repositoryDigest ?? null,
|
|
2671
|
-
worktreeDigest: canonical.worktreeDigest ?? null,
|
|
2672
|
-
resourceDigest: canonical.resourceDigest ?? null,
|
|
2673
|
-
operation: canonical.operation,
|
|
2674
|
-
result: canonical.result,
|
|
2675
|
-
source: canonical.source,
|
|
2676
|
-
consumption: canonical.consumption,
|
|
2677
|
-
timestamp: canonical.timestamp
|
|
2678
|
-
}
|
|
2855
|
+
canonical: canonicalFields
|
|
2679
2856
|
});
|
|
2680
2857
|
}
|
|
2681
2858
|
function hashIntegrity(scope, value) {
|
|
@@ -2711,7 +2888,10 @@ function progressionIntegrity(marker) {
|
|
|
2711
2888
|
unitDigest: marker.unitDigest,
|
|
2712
2889
|
family: marker.family,
|
|
2713
2890
|
requiredOperations: [...marker.requiredOperations],
|
|
2714
|
-
resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope }))
|
|
2891
|
+
resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
|
|
2892
|
+
...marker.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? {
|
|
2893
|
+
pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity ?? null
|
|
2894
|
+
} : {}
|
|
2715
2895
|
};
|
|
2716
2896
|
return hashIntegrity("progression", JSON.stringify({
|
|
2717
2897
|
kind: marker.kind,
|
|
@@ -2737,15 +2917,21 @@ function parseMintMarker(value) {
|
|
|
2737
2917
|
if (value.kind !== "mint") {
|
|
2738
2918
|
return markerResult(value.kind === "control" ? "unknown-kind" : "unknown-kind");
|
|
2739
2919
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
if (
|
|
2920
|
+
const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2921
|
+
const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2922
|
+
if (!currentVersion && !legacyVersion) {
|
|
2923
|
+
if (value.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION && value.schemaVersion !== LEGACY_RECEIPT_READBACK_SCHEMA_VERSION)
|
|
2924
|
+
return markerResult("unknown-schema");
|
|
2743
2925
|
return markerResult("unknown-protocol");
|
|
2926
|
+
}
|
|
2744
2927
|
if (!hasExactKeys(value, MINT_KEYS))
|
|
2745
2928
|
return markerResult("forbidden-field");
|
|
2746
2929
|
const envelope = parseEnvelope(value.envelope);
|
|
2747
2930
|
if (!envelope)
|
|
2748
2931
|
return markerResult("malformed");
|
|
2932
|
+
if (currentVersion && envelope.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION || legacyVersion && envelope.schemaVersion !== 1) {
|
|
2933
|
+
return markerResult("malformed");
|
|
2934
|
+
}
|
|
2749
2935
|
if (!isSessionSaltHex(value.sessionSalt))
|
|
2750
2936
|
return markerResult("malformed");
|
|
2751
2937
|
if (!isDigest(value.integrity))
|
|
@@ -2754,8 +2940,8 @@ function parseMintMarker(value) {
|
|
|
2754
2940
|
return markerResult("malformed");
|
|
2755
2941
|
const marker = {
|
|
2756
2942
|
kind: "mint",
|
|
2757
|
-
schemaVersion:
|
|
2758
|
-
protocolVersion:
|
|
2943
|
+
schemaVersion: value.schemaVersion,
|
|
2944
|
+
protocolVersion: value.protocolVersion,
|
|
2759
2945
|
envelope,
|
|
2760
2946
|
sessionSalt: value.sessionSalt,
|
|
2761
2947
|
integrity: value.integrity
|
|
@@ -2767,13 +2953,15 @@ function parseMintMarker(value) {
|
|
|
2767
2953
|
function parseConsumeMarker(value) {
|
|
2768
2954
|
if (!hasExactKeys(value, CONSUME_KEYS))
|
|
2769
2955
|
return markerResult("forbidden-field");
|
|
2770
|
-
|
|
2956
|
+
const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2957
|
+
const legacyVersion = isLegacyVersionPair(value.schemaVersion, value.protocolVersion);
|
|
2958
|
+
if (!currentVersion && !legacyVersion || value.kind !== "control" || value.control !== "consume" || !isDigest(value.registrationDigest) || !isCapabilityList(value.capabilityFlags) || !isReceiptId(value.receiptId) || !isDigest(value.transitionDigest) || !isTimestamp(value.timestamp) || !isDigest(value.integrity)) {
|
|
2771
2959
|
return markerResult("malformed");
|
|
2772
2960
|
}
|
|
2773
2961
|
const marker = {
|
|
2774
2962
|
kind: "control",
|
|
2775
|
-
schemaVersion:
|
|
2776
|
-
protocolVersion:
|
|
2963
|
+
schemaVersion: value.schemaVersion,
|
|
2964
|
+
protocolVersion: value.protocolVersion,
|
|
2777
2965
|
registrationDigest: value.registrationDigest,
|
|
2778
2966
|
capabilityFlags: [...value.capabilityFlags],
|
|
2779
2967
|
control: "consume",
|
|
@@ -2794,8 +2982,10 @@ function parseProgressionMarker(value) {
|
|
|
2794
2982
|
return markerResult("unknown-kind");
|
|
2795
2983
|
}
|
|
2796
2984
|
function parseProgressionBase(value) {
|
|
2797
|
-
if (value.schemaVersion
|
|
2985
|
+
if ((isCurrentVersionPair(value.schemaVersion, value.protocolVersion) || isLegacyVersionPair(value.schemaVersion, value.protocolVersion)) && value.kind === "control" && value.control === "progression" && isDigest(value.registrationDigest) && isCapabilityList(value.capabilityFlags) && (value.state === "started" || value.state === "completed") && isDigest(value.transitionDigest) && isTimestamp(value.timestamp) && isSessionSaltHex(value.sessionSalt) && isDigest(value.integrity)) {
|
|
2798
2986
|
return {
|
|
2987
|
+
schemaVersion: value.schemaVersion,
|
|
2988
|
+
protocolVersion: value.protocolVersion,
|
|
2799
2989
|
registrationDigest: value.registrationDigest,
|
|
2800
2990
|
capabilityFlags: [...value.capabilityFlags],
|
|
2801
2991
|
state: value.state,
|
|
@@ -2817,8 +3007,8 @@ function parseEpochProgressionMarker(value) {
|
|
|
2817
3007
|
}
|
|
2818
3008
|
const marker = {
|
|
2819
3009
|
kind: "control",
|
|
2820
|
-
schemaVersion:
|
|
2821
|
-
protocolVersion:
|
|
3010
|
+
schemaVersion: base.schemaVersion,
|
|
3011
|
+
protocolVersion: base.protocolVersion,
|
|
2822
3012
|
registrationDigest: base.registrationDigest,
|
|
2823
3013
|
capabilityFlags: [...base.capabilityFlags],
|
|
2824
3014
|
control: "progression",
|
|
@@ -2835,19 +3025,21 @@ function parseEpochProgressionMarker(value) {
|
|
|
2835
3025
|
return progressionMarkerValidation(marker);
|
|
2836
3026
|
}
|
|
2837
3027
|
function parseUnitProgressionMarker(value) {
|
|
2838
|
-
|
|
3028
|
+
const currentVersion = isCurrentVersionPair(value.schemaVersion, value.protocolVersion);
|
|
3029
|
+
if (!hasExactKeys(value, LEGACY_UNIT_PROGRESSION_KEYS, currentVersion ? ["pinnedOperationTargetIdentity"] : [])) {
|
|
2839
3030
|
return Object.hasOwn(value, "epochId") && Object.hasOwn(value, "unitId") ? markerResult("forbidden-field") : markerResult("missing-internal-id");
|
|
2840
3031
|
}
|
|
2841
3032
|
const requiredOperations = canonicalRequiredOperations(value.requiredOperations);
|
|
2842
3033
|
const resourceScopes = canonicalResourceScopes(value.resourceScopes);
|
|
3034
|
+
const pinnedOperationTargetIdentity = value.pinnedOperationTargetIdentity === undefined ? undefined : isDigest(value.pinnedOperationTargetIdentity) ? value.pinnedOperationTargetIdentity : undefined;
|
|
2843
3035
|
const base = parseProgressionBase(value);
|
|
2844
|
-
if (!base || !isInternalId(value.epochId) || !isDigest(value.epochDigest) || !isInternalId(value.unitId) || !isDigest(value.unitDigest) || !isFamily(value.family) || !requiredOperations || !resourceScopes) {
|
|
3036
|
+
if (!base || !isInternalId(value.epochId) || !isDigest(value.epochDigest) || !isInternalId(value.unitId) || !isDigest(value.unitDigest) || !isFamily(value.family) || !requiredOperations || !resourceScopes || value.pinnedOperationTargetIdentity !== undefined && pinnedOperationTargetIdentity === undefined) {
|
|
2845
3037
|
return value.epochId === undefined || value.unitId === undefined ? markerResult("missing-internal-id") : markerResult("malformed");
|
|
2846
3038
|
}
|
|
2847
3039
|
const marker = {
|
|
2848
3040
|
kind: "control",
|
|
2849
|
-
schemaVersion:
|
|
2850
|
-
protocolVersion:
|
|
3041
|
+
schemaVersion: base.schemaVersion,
|
|
3042
|
+
protocolVersion: base.protocolVersion,
|
|
2851
3043
|
registrationDigest: base.registrationDigest,
|
|
2852
3044
|
capabilityFlags: [...base.capabilityFlags],
|
|
2853
3045
|
control: "progression",
|
|
@@ -2859,6 +3051,7 @@ function parseUnitProgressionMarker(value) {
|
|
|
2859
3051
|
family: value.family,
|
|
2860
3052
|
requiredOperations: [...requiredOperations],
|
|
2861
3053
|
resourceScopes: resourceScopes.map((scope) => ({ ...scope })),
|
|
3054
|
+
...pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity } : {},
|
|
2862
3055
|
state: base.state,
|
|
2863
3056
|
transitionDigest: base.transitionDigest,
|
|
2864
3057
|
timestamp: base.timestamp,
|
|
@@ -2877,10 +3070,11 @@ function validateReceiptMarker(input) {
|
|
|
2877
3070
|
return parseMintMarker(input);
|
|
2878
3071
|
if (input.kind !== "control")
|
|
2879
3072
|
return markerResult("unknown-kind");
|
|
2880
|
-
if (input.schemaVersion
|
|
2881
|
-
|
|
2882
|
-
|
|
3073
|
+
if (!isCurrentVersionPair(input.schemaVersion, input.protocolVersion) && !isLegacyVersionPair(input.schemaVersion, input.protocolVersion)) {
|
|
3074
|
+
if (input.schemaVersion !== RECEIPT_READBACK_SCHEMA_VERSION && input.schemaVersion !== LEGACY_RECEIPT_READBACK_SCHEMA_VERSION)
|
|
3075
|
+
return markerResult("unknown-schema");
|
|
2883
3076
|
return markerResult("unknown-protocol");
|
|
3077
|
+
}
|
|
2884
3078
|
if (input.control === "consume")
|
|
2885
3079
|
return parseConsumeMarker(input);
|
|
2886
3080
|
if (input.control === "progression")
|
|
@@ -3010,7 +3204,7 @@ function projectEpochMarker(source, input, context) {
|
|
|
3010
3204
|
};
|
|
3011
3205
|
}
|
|
3012
3206
|
function projectUnitMarker(source, input, context) {
|
|
3013
|
-
if (!isInternalId(input.epochId) || !isInternalId(input.unitId) || !isFamily(input.family) || normalizeRequiredOperations(input.requiredOperations) === undefined || normalizeResourceScopes(input.resourceScopes) === undefined) {
|
|
3207
|
+
if (!isInternalId(input.epochId) || !isInternalId(input.unitId) || !isFamily(input.family) || normalizeRequiredOperations(input.requiredOperations) === undefined || normalizeResourceScopes(input.resourceScopes) === undefined || input.pinnedOperationTargetIdentity !== undefined && !isDigest(input.pinnedOperationTargetIdentity)) {
|
|
3014
3208
|
return;
|
|
3015
3209
|
}
|
|
3016
3210
|
const requiredOperations = normalizeRequiredOperations(input.requiredOperations);
|
|
@@ -3032,6 +3226,7 @@ function projectUnitMarker(source, input, context) {
|
|
|
3032
3226
|
family: input.family,
|
|
3033
3227
|
requiredOperations: [...requiredOperations],
|
|
3034
3228
|
resourceScopes: resourceScopes.map((scope) => ({ ...scope })),
|
|
3229
|
+
...input.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: input.pinnedOperationTargetIdentity } : {},
|
|
3035
3230
|
state: input.state,
|
|
3036
3231
|
transitionDigest: input.transitionDigest,
|
|
3037
3232
|
timestamp: context.timestamp,
|
|
@@ -3107,6 +3302,7 @@ function cloneMarker(marker) {
|
|
|
3107
3302
|
family: marker.family,
|
|
3108
3303
|
requiredOperations: [...marker.requiredOperations],
|
|
3109
3304
|
resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
|
|
3305
|
+
...marker.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity } : {},
|
|
3110
3306
|
state: marker.state,
|
|
3111
3307
|
transitionDigest: marker.transitionDigest,
|
|
3112
3308
|
timestamp: marker.timestamp,
|
|
@@ -3173,6 +3369,9 @@ function serializeMarker(marker) {
|
|
|
3173
3369
|
family: marker.family,
|
|
3174
3370
|
requiredOperations: [...marker.requiredOperations],
|
|
3175
3371
|
resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
|
|
3372
|
+
...marker.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION ? {
|
|
3373
|
+
pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity ?? null
|
|
3374
|
+
} : {},
|
|
3176
3375
|
state: marker.state,
|
|
3177
3376
|
transitionDigest: marker.transitionDigest,
|
|
3178
3377
|
timestamp: marker.timestamp,
|
|
@@ -3196,6 +3395,12 @@ function sameEnvelope(first, second) {
|
|
|
3196
3395
|
function foldMintMarker(marker, context) {
|
|
3197
3396
|
if (marker.sessionSalt !== context.expectedSalt)
|
|
3198
3397
|
return "salt-mismatch";
|
|
3398
|
+
if (marker.envelope.schemaVersion === 1 && context.expectation.operationTargetIdentity) {
|
|
3399
|
+
return "identity-digest-mismatch";
|
|
3400
|
+
}
|
|
3401
|
+
if (marker.envelope.schemaVersion === RECEIPT_READBACK_SCHEMA_VERSION && context.expectation.operationTargetIdentity && marker.envelope.canonical.operationTargetIdentity !== context.expectation.operationTargetIdentity) {
|
|
3402
|
+
return "identity-digest-mismatch";
|
|
3403
|
+
}
|
|
3199
3404
|
const registrationMatches = marker.envelope.registrationDigest === context.expectation.registrationDigest && marker.envelope.canonical.registrationDigest === context.expectation.registrationDigest;
|
|
3200
3405
|
if (!registrationMatches)
|
|
3201
3406
|
return "cross-registration";
|
|
@@ -3261,6 +3466,7 @@ function progressionSnapshot(marker) {
|
|
|
3261
3466
|
family: marker.family,
|
|
3262
3467
|
requiredOperations: [...marker.requiredOperations],
|
|
3263
3468
|
resourceScopes: marker.resourceScopes.map((scope) => ({ ...scope })),
|
|
3469
|
+
...marker.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: marker.pinnedOperationTargetIdentity } : {},
|
|
3264
3470
|
transitionDigest: marker.transitionDigest
|
|
3265
3471
|
};
|
|
3266
3472
|
}
|
|
@@ -3338,13 +3544,14 @@ function applyUnitStart(marker, epoch, context) {
|
|
|
3338
3544
|
if (!sameUnitDeclaration(current, marker) && unitHasMintedEvidence(current, context)) {
|
|
3339
3545
|
return "out-of-order";
|
|
3340
3546
|
}
|
|
3341
|
-
if (sameUnitDeclaration(current, marker) && current.transitionDigest !== marker.transitionDigest) {
|
|
3547
|
+
if (sameUnitDeclaration(current, marker) && current.transitionDigest !== marker.transitionDigest && current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity) {
|
|
3342
3548
|
return "conflicting-marker";
|
|
3343
3549
|
}
|
|
3344
3550
|
if (!unitDeclarationExtends(current, marker))
|
|
3345
3551
|
return "conflicting-marker";
|
|
3346
|
-
if (sameUnitDeclaration(current, marker))
|
|
3552
|
+
if (sameUnitDeclaration(current, marker) && current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity) {
|
|
3347
3553
|
return;
|
|
3554
|
+
}
|
|
3348
3555
|
context.progression = { epoch, unit: snapshot };
|
|
3349
3556
|
return;
|
|
3350
3557
|
}
|
|
@@ -3388,7 +3595,10 @@ function unitDeclarationExtends(current, marker) {
|
|
|
3388
3595
|
scope.operation,
|
|
3389
3596
|
scope.resourceIdentity
|
|
3390
3597
|
]));
|
|
3391
|
-
|
|
3598
|
+
if (![...current.resourceScopes].every((scope) => nextScopes.get(scope.operation) === scope.resourceIdentity)) {
|
|
3599
|
+
return false;
|
|
3600
|
+
}
|
|
3601
|
+
return current.pinnedOperationTargetIdentity === undefined || current.pinnedOperationTargetIdentity === marker.pinnedOperationTargetIdentity;
|
|
3392
3602
|
}
|
|
3393
3603
|
function foldProgressionMarker(marker, context) {
|
|
3394
3604
|
return marker.target === "epoch" ? applyEpochProgression(marker, context) : applyUnitProgression(marker, context);
|
|
@@ -3455,18 +3665,21 @@ function foldReceiptReadback(inputs, expectation) {
|
|
|
3455
3665
|
}
|
|
3456
3666
|
};
|
|
3457
3667
|
}
|
|
3458
|
-
function receiptReadbackExpectationFromMetadata(metadata2, sessionSalt) {
|
|
3668
|
+
function receiptReadbackExpectationFromMetadata(metadata2, sessionSalt, operationTargetIdentity) {
|
|
3459
3669
|
return {
|
|
3460
3670
|
registrationDigest: metadata2.registrationDigest,
|
|
3461
3671
|
capabilityFlags: [...metadata2.capabilityFlags],
|
|
3462
3672
|
sessionSalt: new Uint8Array(sessionSalt),
|
|
3463
|
-
source: "runtime-verified"
|
|
3673
|
+
source: "runtime-verified",
|
|
3674
|
+
operationTargetIdentity
|
|
3464
3675
|
};
|
|
3465
3676
|
}
|
|
3466
3677
|
|
|
3467
3678
|
// src/lib/receipt-ledger.ts
|
|
3468
|
-
var RECEIPT_SCHEMA_VERSION =
|
|
3469
|
-
var RECEIPT_PROTOCOL_VERSION =
|
|
3679
|
+
var RECEIPT_SCHEMA_VERSION = 2;
|
|
3680
|
+
var RECEIPT_PROTOCOL_VERSION = 2;
|
|
3681
|
+
var LEGACY_RECEIPT_SCHEMA_VERSION = 1;
|
|
3682
|
+
var LEGACY_RECEIPT_PROTOCOL_VERSION = 1;
|
|
3470
3683
|
var OPERATION_SET = new Set([
|
|
3471
3684
|
"implementation",
|
|
3472
3685
|
"verification",
|
|
@@ -3514,6 +3727,9 @@ function isStringArray(value) {
|
|
|
3514
3727
|
function isOperation2(value) {
|
|
3515
3728
|
return typeof value === "string" && OPERATION_SET.has(value);
|
|
3516
3729
|
}
|
|
3730
|
+
function isLocalOperation(operation) {
|
|
3731
|
+
return operation === "implementation" || operation === "verification" || operation === "commit";
|
|
3732
|
+
}
|
|
3517
3733
|
function isReceiptReasonCode(value) {
|
|
3518
3734
|
return typeof value === "string" && new Set([
|
|
3519
3735
|
"already-consumed",
|
|
@@ -3573,7 +3789,7 @@ function cloneEnvelope2(envelope) {
|
|
|
3573
3789
|
};
|
|
3574
3790
|
}
|
|
3575
3791
|
function envelopesEqual(first, second) {
|
|
3576
|
-
return first.schemaVersion === second.schemaVersion && first.protocolVersion === second.protocolVersion && first.registrationDigest === second.registrationDigest && capabilityListsEqual(first.capabilityFlags, second.capabilityFlags) && first.compatibility === second.compatibility && first.canonical.receiptId === second.canonical.receiptId && first.canonical.registrationDigest === second.canonical.registrationDigest && first.canonical.callDigest === second.canonical.callDigest && first.canonical.epochDigest === second.canonical.epochDigest && first.canonical.unitDigest === second.canonical.unitDigest && first.canonical.workspaceDigest === second.canonical.workspaceDigest && first.canonical.repositoryDigest === second.canonical.repositoryDigest && first.canonical.worktreeDigest === second.canonical.worktreeDigest && first.canonical.resourceDigest === second.canonical.resourceDigest && first.canonical.operation === second.canonical.operation && first.canonical.result === second.canonical.result && first.canonical.source === second.canonical.source && first.canonical.consumption === second.canonical.consumption && first.canonical.timestamp === second.canonical.timestamp;
|
|
3792
|
+
return first.schemaVersion === second.schemaVersion && first.protocolVersion === second.protocolVersion && first.registrationDigest === second.registrationDigest && capabilityListsEqual(first.capabilityFlags, second.capabilityFlags) && first.compatibility === second.compatibility && first.canonical.receiptId === second.canonical.receiptId && first.canonical.registrationDigest === second.canonical.registrationDigest && first.canonical.callDigest === second.canonical.callDigest && first.canonical.epochDigest === second.canonical.epochDigest && first.canonical.unitDigest === second.canonical.unitDigest && first.canonical.workspaceDigest === second.canonical.workspaceDigest && first.canonical.repositoryDigest === second.canonical.repositoryDigest && first.canonical.worktreeDigest === second.canonical.worktreeDigest && first.canonical.operationTargetIdentity === second.canonical.operationTargetIdentity && first.canonical.resourceDigest === second.canonical.resourceDigest && first.canonical.operation === second.canonical.operation && first.canonical.result === second.canonical.result && first.canonical.source === second.canonical.source && first.canonical.consumption === second.canonical.consumption && first.canonical.timestamp === second.canonical.timestamp;
|
|
3577
3793
|
}
|
|
3578
3794
|
function cloneProgressionState(progression) {
|
|
3579
3795
|
return {
|
|
@@ -3621,6 +3837,7 @@ function storedReceiptFromEnvelope(envelope) {
|
|
|
3621
3837
|
workspaceDigest: envelope.canonical.workspaceDigest,
|
|
3622
3838
|
repositoryDigest: envelope.canonical.repositoryDigest,
|
|
3623
3839
|
worktreeDigest: envelope.canonical.worktreeDigest,
|
|
3840
|
+
operationTargetIdentity: envelope.canonical.operationTargetIdentity,
|
|
3624
3841
|
resourceDigest: envelope.canonical.resourceDigest
|
|
3625
3842
|
}
|
|
3626
3843
|
};
|
|
@@ -3759,8 +3976,9 @@ function parseContext(value) {
|
|
|
3759
3976
|
return;
|
|
3760
3977
|
const repositoryIdentity = value.repositoryIdentity === undefined ? undefined : normalizedIdentity(value.repositoryIdentity);
|
|
3761
3978
|
const worktreeIdentity = value.worktreeIdentity === undefined ? undefined : normalizedIdentity(value.worktreeIdentity);
|
|
3979
|
+
const operationTargetIdentity = value.operationTargetIdentity === undefined ? undefined : isDigest2(value.operationTargetIdentity) ? value.operationTargetIdentity : undefined;
|
|
3762
3980
|
const resourceIdentity = value.resourceIdentity === undefined ? undefined : normalizedIdentity(value.resourceIdentity);
|
|
3763
|
-
if (value.repositoryIdentity !== undefined && !repositoryIdentity || value.worktreeIdentity !== undefined && !worktreeIdentity || value.resourceIdentity !== undefined && !resourceIdentity) {
|
|
3981
|
+
if (value.repositoryIdentity !== undefined && !repositoryIdentity || value.worktreeIdentity !== undefined && !worktreeIdentity || value.operationTargetIdentity !== undefined && !isDigest2(operationTargetIdentity) || value.resourceIdentity !== undefined && !resourceIdentity) {
|
|
3764
3982
|
return;
|
|
3765
3983
|
}
|
|
3766
3984
|
return {
|
|
@@ -3769,6 +3987,7 @@ function parseContext(value) {
|
|
|
3769
3987
|
workspaceIdentity,
|
|
3770
3988
|
repositoryIdentity,
|
|
3771
3989
|
worktreeIdentity,
|
|
3990
|
+
operationTargetIdentity,
|
|
3772
3991
|
resourceIdentity
|
|
3773
3992
|
};
|
|
3774
3993
|
}
|
|
@@ -3784,6 +4003,7 @@ function parseAfter(value) {
|
|
|
3784
4003
|
workspaceIdentity: context.workspaceIdentity,
|
|
3785
4004
|
repositoryIdentity: context.repositoryIdentity,
|
|
3786
4005
|
worktreeIdentity: context.worktreeIdentity,
|
|
4006
|
+
operationTargetIdentity: context.operationTargetIdentity,
|
|
3787
4007
|
resourceIdentity: context.resourceIdentity
|
|
3788
4008
|
};
|
|
3789
4009
|
}
|
|
@@ -3857,6 +4077,8 @@ function compareDigestedContexts(expected, actual) {
|
|
|
3857
4077
|
return "workspace-mismatch";
|
|
3858
4078
|
if (expected.worktreeDigest !== actual.worktreeDigest)
|
|
3859
4079
|
return "workspace-mismatch";
|
|
4080
|
+
if (expected.operationTargetIdentity !== actual.operationTargetIdentity)
|
|
4081
|
+
return "workspace-mismatch";
|
|
3860
4082
|
if (expected.resourceDigest !== actual.resourceDigest)
|
|
3861
4083
|
return "workspace-mismatch";
|
|
3862
4084
|
return;
|
|
@@ -3877,7 +4099,9 @@ function validateEnvelopeInput(input) {
|
|
|
3877
4099
|
if (!("schemaVersion" in input) || !("protocolVersion" in input)) {
|
|
3878
4100
|
return { compatibility: "unavailable", reasonCode: "incomplete-envelope" };
|
|
3879
4101
|
}
|
|
3880
|
-
|
|
4102
|
+
const currentVersion = input.schemaVersion === RECEIPT_SCHEMA_VERSION && input.protocolVersion === RECEIPT_PROTOCOL_VERSION;
|
|
4103
|
+
const legacyVersion = input.schemaVersion === LEGACY_RECEIPT_SCHEMA_VERSION && input.protocolVersion === LEGACY_RECEIPT_PROTOCOL_VERSION;
|
|
4104
|
+
if (!currentVersion && !legacyVersion) {
|
|
3881
4105
|
return { compatibility: "unavailable", reasonCode: "unknown-envelope" };
|
|
3882
4106
|
}
|
|
3883
4107
|
if (!("capabilityFlags" in input) || input.capabilityFlags === undefined) {
|
|
@@ -3897,16 +4121,19 @@ function isReceiptId2(value) {
|
|
|
3897
4121
|
function parseEnvelope2(value) {
|
|
3898
4122
|
if (!isRecord4(value))
|
|
3899
4123
|
return;
|
|
3900
|
-
|
|
4124
|
+
const currentVersion = value.schemaVersion === RECEIPT_SCHEMA_VERSION && value.protocolVersion === RECEIPT_PROTOCOL_VERSION;
|
|
4125
|
+
const legacyVersion = value.schemaVersion === LEGACY_RECEIPT_SCHEMA_VERSION && value.protocolVersion === LEGACY_RECEIPT_PROTOCOL_VERSION;
|
|
4126
|
+
if (!currentVersion && !legacyVersion || value.compatibility !== "compatible" || !isDigest2(value.registrationDigest) || !isBoundedCapabilityList(value.capabilityFlags) || !isRecord4(value.canonical)) {
|
|
3901
4127
|
return;
|
|
3902
4128
|
}
|
|
3903
4129
|
const canonical = value.canonical;
|
|
3904
|
-
|
|
4130
|
+
const operation = isOperation2(canonical.operation) ? canonical.operation : undefined;
|
|
4131
|
+
if (!isReceiptId2(canonical.receiptId) || !isDigest2(canonical.registrationDigest) || !isDigest2(canonical.callDigest) || !isDigest2(canonical.epochDigest) || !isDigest2(canonical.unitDigest) || !isDigest2(canonical.workspaceDigest) || canonical.repositoryDigest !== undefined && !isDigest2(canonical.repositoryDigest) || canonical.worktreeDigest !== undefined && !isDigest2(canonical.worktreeDigest) || canonical.operationTargetIdentity !== undefined && !isDigest2(canonical.operationTargetIdentity) || operation === undefined || currentVersion && isLocalOperation(operation) && !isDigest2(canonical.operationTargetIdentity) || currentVersion && !isLocalOperation(operation) && Object.hasOwn(canonical, "operationTargetIdentity") || canonical.resourceDigest !== undefined && !isDigest2(canonical.resourceDigest) || canonical.result !== "success" || canonical.source !== "runtime-verified" || canonical.consumption !== "available" && canonical.consumption !== "consumed" || typeof canonical.timestamp !== "number" || !Number.isFinite(canonical.timestamp)) {
|
|
3905
4132
|
return;
|
|
3906
4133
|
}
|
|
3907
4134
|
return {
|
|
3908
|
-
schemaVersion: RECEIPT_SCHEMA_VERSION,
|
|
3909
|
-
protocolVersion: RECEIPT_PROTOCOL_VERSION,
|
|
4135
|
+
schemaVersion: currentVersion ? RECEIPT_SCHEMA_VERSION : LEGACY_RECEIPT_SCHEMA_VERSION,
|
|
4136
|
+
protocolVersion: currentVersion ? RECEIPT_PROTOCOL_VERSION : LEGACY_RECEIPT_PROTOCOL_VERSION,
|
|
3910
4137
|
registrationDigest: value.registrationDigest,
|
|
3911
4138
|
capabilityFlags: [...value.capabilityFlags],
|
|
3912
4139
|
compatibility: "compatible",
|
|
@@ -3919,8 +4146,9 @@ function parseEnvelope2(value) {
|
|
|
3919
4146
|
workspaceDigest: canonical.workspaceDigest,
|
|
3920
4147
|
repositoryDigest: canonical.repositoryDigest,
|
|
3921
4148
|
worktreeDigest: canonical.worktreeDigest,
|
|
4149
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: canonical.operationTargetIdentity } : {},
|
|
3922
4150
|
resourceDigest: canonical.resourceDigest,
|
|
3923
|
-
operation
|
|
4151
|
+
operation,
|
|
3924
4152
|
result: "success",
|
|
3925
4153
|
source: "runtime-verified",
|
|
3926
4154
|
consumption: canonical.consumption,
|
|
@@ -3974,13 +4202,14 @@ function createReceiptLedger(options = {}) {
|
|
|
3974
4202
|
}
|
|
3975
4203
|
return digestReceiptIdentity(domain, normalized, sessionSaltBytes);
|
|
3976
4204
|
}
|
|
3977
|
-
function digestContext(context) {
|
|
4205
|
+
function digestContext(context, operation) {
|
|
3978
4206
|
return {
|
|
3979
4207
|
epochDigest: digestIdentity("epoch", context.epochId),
|
|
3980
4208
|
unitDigest: digestIdentity("unit", context.unitId),
|
|
3981
4209
|
workspaceDigest: digestIdentity("workspace", context.workspaceIdentity),
|
|
3982
4210
|
repositoryDigest: context.repositoryIdentity ? digestIdentity("repository", context.repositoryIdentity) : undefined,
|
|
3983
4211
|
worktreeDigest: context.worktreeIdentity ? digestIdentity("worktree", context.worktreeIdentity) : undefined,
|
|
4212
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: context.operationTargetIdentity } : {},
|
|
3984
4213
|
resourceDigest: context.resourceIdentity ? digestIdentity("resource", context.resourceIdentity) : undefined
|
|
3985
4214
|
};
|
|
3986
4215
|
}
|
|
@@ -3994,7 +4223,7 @@ function createReceiptLedger(options = {}) {
|
|
|
3994
4223
|
if (!parsed)
|
|
3995
4224
|
return prepareInvalidObservationResult(input);
|
|
3996
4225
|
const callDigest = digestIdentity("call", parsed.callId);
|
|
3997
|
-
const digested = digestContext(parsed.context);
|
|
4226
|
+
const digested = digestContext(parsed.context, parsed.operation);
|
|
3998
4227
|
const existing = lifecycleByCall.get(callDigest);
|
|
3999
4228
|
if (existing) {
|
|
4000
4229
|
return resolveExistingPrepare(existing, parsed.operation, digested);
|
|
@@ -4013,10 +4242,10 @@ function createReceiptLedger(options = {}) {
|
|
|
4013
4242
|
if (!parsed)
|
|
4014
4243
|
return invalidObservationResult(input);
|
|
4015
4244
|
const callDigest = digestIdentity("call", parsed.callId);
|
|
4016
|
-
const actualContext = digestContext(parsed.context);
|
|
4017
4245
|
const matchingEntry = lifecycleByCall.get(callDigest);
|
|
4018
4246
|
if (!matchingEntry)
|
|
4019
4247
|
return invalidObservationResult(input);
|
|
4248
|
+
const actualContext = digestContext(parsed.context, matchingEntry.operation);
|
|
4020
4249
|
const contextMismatch = compareDigestedContexts(matchingEntry.context, actualContext);
|
|
4021
4250
|
if (contextMismatch) {
|
|
4022
4251
|
sealEntry(matchingEntry);
|
|
@@ -4027,7 +4256,7 @@ function createReceiptLedger(options = {}) {
|
|
|
4027
4256
|
sealEntry(matchingEntry);
|
|
4028
4257
|
return { status: "rejected", reasonCode: entryRejection };
|
|
4029
4258
|
}
|
|
4030
|
-
const afterDigests = digestAfter(parsed.after);
|
|
4259
|
+
const afterDigests = digestAfter(parsed.after, matchingEntry.operation);
|
|
4031
4260
|
const noOpReason = getNoOpReason(matchingEntry, afterDigests);
|
|
4032
4261
|
if (noOpReason) {
|
|
4033
4262
|
sealEntry(matchingEntry);
|
|
@@ -4070,13 +4299,14 @@ function createReceiptLedger(options = {}) {
|
|
|
4070
4299
|
return "successful-no-op";
|
|
4071
4300
|
return;
|
|
4072
4301
|
}
|
|
4073
|
-
function digestAfter(after) {
|
|
4302
|
+
function digestAfter(after, operation) {
|
|
4074
4303
|
return {
|
|
4075
4304
|
epochDigest: "",
|
|
4076
4305
|
unitDigest: "",
|
|
4077
4306
|
workspaceDigest: digestIdentity("workspace", after.workspaceIdentity),
|
|
4078
4307
|
repositoryDigest: after.repositoryIdentity ? digestIdentity("repository", after.repositoryIdentity) : undefined,
|
|
4079
4308
|
worktreeDigest: after.worktreeIdentity ? digestIdentity("worktree", after.worktreeIdentity) : undefined,
|
|
4309
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
|
|
4080
4310
|
resourceDigest: after.resourceIdentity ? digestIdentity("resource", after.resourceIdentity) : undefined
|
|
4081
4311
|
};
|
|
4082
4312
|
}
|
|
@@ -4102,6 +4332,10 @@ function createReceiptLedger(options = {}) {
|
|
|
4102
4332
|
return !entry.context.resourceDigest || !after.resourceDigest || after.resourceDigest === entry.context.resourceDigest ? "no-op-resource" : undefined;
|
|
4103
4333
|
}
|
|
4104
4334
|
function mintReceipt(entry, callDigest, after) {
|
|
4335
|
+
if (isLocalOperation(entry.operation) && !after.operationTargetIdentity) {
|
|
4336
|
+
entry.status = "abandoned";
|
|
4337
|
+
return { status: "rejected", reasonCode: "invalid-observation" };
|
|
4338
|
+
}
|
|
4105
4339
|
const receiptId = randomBytes2(16).toString("hex");
|
|
4106
4340
|
const canonical = {
|
|
4107
4341
|
receiptId,
|
|
@@ -4112,6 +4346,7 @@ function createReceiptLedger(options = {}) {
|
|
|
4112
4346
|
workspaceDigest: entry.context.workspaceDigest,
|
|
4113
4347
|
repositoryDigest: after.repositoryDigest,
|
|
4114
4348
|
worktreeDigest: after.worktreeDigest,
|
|
4349
|
+
...isLocalOperation(entry.operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
|
|
4115
4350
|
resourceDigest: after.resourceDigest,
|
|
4116
4351
|
operation: entry.operation,
|
|
4117
4352
|
result: "success",
|
|
@@ -4136,6 +4371,7 @@ function createReceiptLedger(options = {}) {
|
|
|
4136
4371
|
workspaceDigest: entry.context.workspaceDigest,
|
|
4137
4372
|
repositoryDigest: after.repositoryDigest,
|
|
4138
4373
|
worktreeDigest: after.worktreeDigest,
|
|
4374
|
+
operationTargetIdentity: after.operationTargetIdentity,
|
|
4139
4375
|
resourceDigest: after.resourceDigest
|
|
4140
4376
|
}
|
|
4141
4377
|
});
|
|
@@ -4149,10 +4385,10 @@ function createReceiptLedger(options = {}) {
|
|
|
4149
4385
|
if (!callId || !context)
|
|
4150
4386
|
return { status: "rejected", reasonCode: "invalid-observation" };
|
|
4151
4387
|
const callDigest = digestIdentity("call", callId);
|
|
4152
|
-
const digested = digestContext(context);
|
|
4153
4388
|
const matchingEntry = lifecycleByCall.get(callDigest);
|
|
4154
4389
|
if (!matchingEntry)
|
|
4155
4390
|
return { status: "rejected", reasonCode: "unknown-receipt" };
|
|
4391
|
+
const digested = digestContext(context, matchingEntry.operation);
|
|
4156
4392
|
const contextMismatch = compareDigestedContexts(matchingEntry.context, digested);
|
|
4157
4393
|
if (contextMismatch)
|
|
4158
4394
|
return { status: "rejected", reasonCode: contextMismatch };
|
|
@@ -4171,7 +4407,7 @@ function createReceiptLedger(options = {}) {
|
|
|
4171
4407
|
const stored = receipts.get(receiptId);
|
|
4172
4408
|
if (!stored)
|
|
4173
4409
|
return { status: "rejected", reasonCode: "unknown-receipt" };
|
|
4174
|
-
const actual = digestContext(context);
|
|
4410
|
+
const actual = digestContext(context, stored.envelope.canonical.operation);
|
|
4175
4411
|
const contextMismatch = compareDigestedContexts(stored.context, actual);
|
|
4176
4412
|
if (contextMismatch)
|
|
4177
4413
|
return { status: "rejected", reasonCode: contextMismatch };
|
|
@@ -4181,13 +4417,19 @@ function createReceiptLedger(options = {}) {
|
|
|
4181
4417
|
stored.envelope.canonical.consumption = "consumed";
|
|
4182
4418
|
return { status: "consumed", receipt: cloneEnvelope2(stored.envelope) };
|
|
4183
4419
|
}
|
|
4184
|
-
function recoverReceipt(input) {
|
|
4420
|
+
function recoverReceipt(input, operationTargetIdentity) {
|
|
4185
4421
|
const markerResult2 = getMintMarker(input);
|
|
4186
4422
|
if (markerResult2.status !== "valid")
|
|
4187
4423
|
return markerResult2;
|
|
4188
4424
|
const marker = markerResult2.marker;
|
|
4189
4425
|
if (marker.sessionSalt !== sessionSalt)
|
|
4190
4426
|
return { status: "rejected", category: "salt-mismatch" };
|
|
4427
|
+
if (marker.envelope.schemaVersion === 1 && operationTargetIdentity !== undefined) {
|
|
4428
|
+
return { status: "rejected", category: "identity-digest-mismatch" };
|
|
4429
|
+
}
|
|
4430
|
+
if (marker.envelope.schemaVersion === RECEIPT_SCHEMA_VERSION && operationTargetIdentity !== undefined && marker.envelope.canonical.operationTargetIdentity !== operationTargetIdentity) {
|
|
4431
|
+
return { status: "rejected", category: "identity-digest-mismatch" };
|
|
4432
|
+
}
|
|
4191
4433
|
const envelopeValidation = validateEnvelope(marker.envelope);
|
|
4192
4434
|
const envelopeFailure = recoveryEnvelopeCategory(envelopeValidation);
|
|
4193
4435
|
if (envelopeFailure) {
|
|
@@ -4212,8 +4454,8 @@ function createReceiptLedger(options = {}) {
|
|
|
4212
4454
|
receipts.set(receiptId, storedReceiptFromEnvelope(envelope));
|
|
4213
4455
|
return { status: "recovered", receipt: cloneEnvelope2(envelope) };
|
|
4214
4456
|
}
|
|
4215
|
-
function recoverReadback(input) {
|
|
4216
|
-
const folded = foldForLedgerRecovery(input, receiptReadbackExpectationFromMetadata(metadata2, sessionSaltBytes));
|
|
4457
|
+
function recoverReadback(input, operationTargetIdentity) {
|
|
4458
|
+
const folded = foldForLedgerRecovery(input, receiptReadbackExpectationFromMetadata(metadata2, sessionSaltBytes, operationTargetIdentity));
|
|
4217
4459
|
if (folded.status !== "reconstructed") {
|
|
4218
4460
|
return { status: "rejected", category: folded.category };
|
|
4219
4461
|
}
|
|
@@ -7685,6 +7927,7 @@ function parseOperationContext(value) {
|
|
|
7685
7927
|
"workspaceIdentity",
|
|
7686
7928
|
"repositoryIdentity",
|
|
7687
7929
|
"worktreeIdentity",
|
|
7930
|
+
"operationTargetIdentity",
|
|
7688
7931
|
"resourceIdentity",
|
|
7689
7932
|
"resourceRevisionIdentity"
|
|
7690
7933
|
]))) {
|
|
@@ -7708,14 +7951,16 @@ function hasRequiredOperationIdentities(value) {
|
|
|
7708
7951
|
function parseOperationStateFields(value) {
|
|
7709
7952
|
const repositoryIdentity = parseOptionalDigestIdentity(value.repositoryIdentity);
|
|
7710
7953
|
const worktreeIdentity = parseOptionalDigestIdentity(value.worktreeIdentity);
|
|
7954
|
+
const operationTargetIdentity = parseOptionalDigestIdentity(value.operationTargetIdentity);
|
|
7711
7955
|
const resourceIdentity = parseOptionalDigestIdentity(value.resourceIdentity);
|
|
7712
7956
|
const resourceRevisionIdentity = parseOptionalDigestIdentity(value.resourceRevisionIdentity);
|
|
7713
|
-
if (!validOptionalIdentity(value.repositoryIdentity, repositoryIdentity) || !validOptionalIdentity(value.worktreeIdentity, worktreeIdentity) || !validOptionalIdentity(value.resourceIdentity, resourceIdentity) || !validOptionalIdentity(value.resourceRevisionIdentity, resourceRevisionIdentity)) {
|
|
7957
|
+
if (!validOptionalIdentity(value.repositoryIdentity, repositoryIdentity) || !validOptionalIdentity(value.worktreeIdentity, worktreeIdentity) || !validOptionalIdentity(value.operationTargetIdentity, operationTargetIdentity) || !validOptionalIdentity(value.resourceIdentity, resourceIdentity) || !validOptionalIdentity(value.resourceRevisionIdentity, resourceRevisionIdentity)) {
|
|
7714
7958
|
return;
|
|
7715
7959
|
}
|
|
7716
7960
|
return {
|
|
7717
7961
|
repositoryIdentity,
|
|
7718
7962
|
worktreeIdentity,
|
|
7963
|
+
operationTargetIdentity,
|
|
7719
7964
|
resourceIdentity,
|
|
7720
7965
|
resourceRevisionIdentity
|
|
7721
7966
|
};
|
|
@@ -7759,6 +8004,7 @@ var OPERATION_AFTER_FIELDS = new Set([
|
|
|
7759
8004
|
"workspaceIdentity",
|
|
7760
8005
|
"repositoryIdentity",
|
|
7761
8006
|
"worktreeIdentity",
|
|
8007
|
+
"operationTargetIdentity",
|
|
7762
8008
|
"resourceIdentity",
|
|
7763
8009
|
"resourceRevisionIdentity",
|
|
7764
8010
|
"pullRequest",
|
|
@@ -8293,6 +8539,7 @@ var RECOVERY_UNIT_KEYS = new Set([
|
|
|
8293
8539
|
"family",
|
|
8294
8540
|
"requiredOperations",
|
|
8295
8541
|
"resourceScopes",
|
|
8542
|
+
"pinnedOperationTargetIdentity",
|
|
8296
8543
|
"transitionDigest"
|
|
8297
8544
|
]);
|
|
8298
8545
|
var RECOVERY_STATE_KEYS = new Set([
|
|
@@ -8454,7 +8701,8 @@ function parseRecoveryUnit(value) {
|
|
|
8454
8701
|
return;
|
|
8455
8702
|
const requiredOperations = parseRecoveryOperations(value.requiredOperations);
|
|
8456
8703
|
const resourceScopes = parseRecoveryResourceScopes(value.resourceScopes);
|
|
8457
|
-
|
|
8704
|
+
const pinnedOperationTargetIdentity = value.pinnedOperationTargetIdentity === undefined ? undefined : isRecoveryDigest(value.pinnedOperationTargetIdentity) ? value.pinnedOperationTargetIdentity : undefined;
|
|
8705
|
+
if (value.target !== "unit" || value.state !== "started" && value.state !== "completed" || !isRecoveryId(value.epochId) || !isRecoveryDigest(value.epochDigest) || !isRecoveryId(value.unitId) || !isRecoveryDigest(value.unitDigest) || !isFamily2(value.family) || !requiredOperations || !resourceScopes || value.pinnedOperationTargetIdentity !== undefined && pinnedOperationTargetIdentity === undefined || !isRecoveryDigest(value.transitionDigest)) {
|
|
8458
8706
|
return;
|
|
8459
8707
|
}
|
|
8460
8708
|
return {
|
|
@@ -8467,6 +8715,7 @@ function parseRecoveryUnit(value) {
|
|
|
8467
8715
|
family: value.family,
|
|
8468
8716
|
requiredOperations,
|
|
8469
8717
|
resourceScopes,
|
|
8718
|
+
...pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity } : {},
|
|
8470
8719
|
transitionDigest: value.transitionDigest
|
|
8471
8720
|
};
|
|
8472
8721
|
}
|
|
@@ -8518,7 +8767,8 @@ function cloneUnit(unit) {
|
|
|
8518
8767
|
resourceScopes: Object.freeze([...unit.resourceScopes].map(([operation, resourceIdentity]) => ({
|
|
8519
8768
|
operation,
|
|
8520
8769
|
resourceIdentity
|
|
8521
|
-
})))
|
|
8770
|
+
}))),
|
|
8771
|
+
...unit.pinnedOperationTargetIdentity ? { pinnedOperationTargetIdentity: unit.pinnedOperationTargetIdentity } : {}
|
|
8522
8772
|
});
|
|
8523
8773
|
}
|
|
8524
8774
|
function familyForSkill(skill) {
|
|
@@ -8606,6 +8856,7 @@ function parseReadback(input) {
|
|
|
8606
8856
|
"workspaceIdentity",
|
|
8607
8857
|
"repositoryIdentity",
|
|
8608
8858
|
"worktreeIdentity",
|
|
8859
|
+
"operationTargetIdentity",
|
|
8609
8860
|
"resourceIdentity",
|
|
8610
8861
|
"resourceRevisionIdentity",
|
|
8611
8862
|
"pullRequest",
|
|
@@ -8618,6 +8869,7 @@ function parseReadback(input) {
|
|
|
8618
8869
|
workspaceIdentity: input.workspaceIdentity,
|
|
8619
8870
|
repositoryIdentity: input.repositoryIdentity,
|
|
8620
8871
|
worktreeIdentity: input.worktreeIdentity,
|
|
8872
|
+
operationTargetIdentity: input.operationTargetIdentity,
|
|
8621
8873
|
resourceIdentity: input.resourceIdentity,
|
|
8622
8874
|
resourceRevisionIdentity: input.resourceRevisionIdentity,
|
|
8623
8875
|
pullRequest: input.pullRequest,
|
|
@@ -8631,6 +8883,7 @@ function parseReadback(input) {
|
|
|
8631
8883
|
workspaceIdentity: after.workspaceIdentity,
|
|
8632
8884
|
repositoryIdentity: after.repositoryIdentity,
|
|
8633
8885
|
worktreeIdentity: after.worktreeIdentity,
|
|
8886
|
+
operationTargetIdentity: after.operationTargetIdentity,
|
|
8634
8887
|
resourceIdentity: after.resourceIdentity,
|
|
8635
8888
|
resourceRevisionIdentity: after.resourceRevisionIdentity,
|
|
8636
8889
|
pullRequest: after.pullRequest,
|
|
@@ -8644,9 +8897,10 @@ function parseLegacyReadbackAfter(input) {
|
|
|
8644
8897
|
}
|
|
8645
8898
|
const repositoryIdentity = parseOptionalReadbackIdentity(input.repositoryIdentity);
|
|
8646
8899
|
const worktreeIdentity = parseOptionalReadbackIdentity(input.worktreeIdentity);
|
|
8900
|
+
const operationTargetIdentity = parseOptionalReadbackIdentity(input.operationTargetIdentity);
|
|
8647
8901
|
const resourceIdentity = parseOptionalReadbackIdentity(input.resourceIdentity);
|
|
8648
8902
|
const resourceRevisionIdentity = parseOptionalReadbackIdentity(input.resourceRevisionIdentity);
|
|
8649
|
-
if (!isOptionalReadbackIdentity(input.repositoryIdentity, repositoryIdentity) || !isOptionalReadbackIdentity(input.worktreeIdentity, worktreeIdentity) || !isOptionalReadbackIdentity(input.resourceIdentity, resourceIdentity) || !isOptionalReadbackIdentity(input.resourceRevisionIdentity, resourceRevisionIdentity)) {
|
|
8903
|
+
if (!isOptionalReadbackIdentity(input.repositoryIdentity, repositoryIdentity) || !isOptionalReadbackIdentity(input.worktreeIdentity, worktreeIdentity) || !isOptionalReadbackIdentity(input.operationTargetIdentity, operationTargetIdentity) || !isOptionalReadbackIdentity(input.resourceIdentity, resourceIdentity) || !isOptionalReadbackIdentity(input.resourceRevisionIdentity, resourceRevisionIdentity)) {
|
|
8650
8904
|
return;
|
|
8651
8905
|
}
|
|
8652
8906
|
const pullRequest = parseLegacyPullRequest(input.pullRequest);
|
|
@@ -8660,6 +8914,7 @@ function parseLegacyReadbackAfter(input) {
|
|
|
8660
8914
|
workspaceIdentity: input.workspaceIdentity,
|
|
8661
8915
|
...repositoryIdentity ? { repositoryIdentity } : {},
|
|
8662
8916
|
...worktreeIdentity ? { worktreeIdentity } : {},
|
|
8917
|
+
...operationTargetIdentity ? { operationTargetIdentity } : {},
|
|
8663
8918
|
...resourceIdentity ? { resourceIdentity } : {},
|
|
8664
8919
|
...resourceRevisionIdentity ? { resourceRevisionIdentity } : {},
|
|
8665
8920
|
...pullRequest ? { pullRequest } : {},
|
|
@@ -8699,10 +8954,12 @@ function ledgerResourceIdentity(operation, resourceIdentity, resourceRevisionIde
|
|
|
8699
8954
|
function toLedgerContext(operation, context) {
|
|
8700
8955
|
const {
|
|
8701
8956
|
resourceRevisionIdentity: _resourceRevisionIdentity,
|
|
8957
|
+
operationTargetIdentity: _operationTargetIdentity,
|
|
8702
8958
|
...ledgerContext
|
|
8703
8959
|
} = context;
|
|
8704
8960
|
return {
|
|
8705
8961
|
...ledgerContext,
|
|
8962
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: context.operationTargetIdentity } : {},
|
|
8706
8963
|
resourceIdentity: ledgerResourceIdentity(operation, context.resourceIdentity, context.resourceRevisionIdentity)
|
|
8707
8964
|
};
|
|
8708
8965
|
}
|
|
@@ -8712,10 +8969,12 @@ function toLedgerAfter(operation, after) {
|
|
|
8712
8969
|
pullRequest: _pullRequest,
|
|
8713
8970
|
checkState: _checkState,
|
|
8714
8971
|
reviewDecision: _reviewDecision,
|
|
8972
|
+
operationTargetIdentity: _operationTargetIdentity,
|
|
8715
8973
|
...ledgerAfter
|
|
8716
8974
|
} = after;
|
|
8717
8975
|
return {
|
|
8718
8976
|
...ledgerAfter,
|
|
8977
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
|
|
8719
8978
|
resourceIdentity: ledgerResourceIdentity(operation, after.resourceIdentity, after.resourceRevisionIdentity)
|
|
8720
8979
|
};
|
|
8721
8980
|
}
|
|
@@ -8769,12 +9028,13 @@ function parseU4Readback(input) {
|
|
|
8769
9028
|
"workspaceIdentity",
|
|
8770
9029
|
"repositoryIdentity",
|
|
8771
9030
|
"worktreeIdentity",
|
|
9031
|
+
"operationTargetIdentity",
|
|
8772
9032
|
"resourceIdentity",
|
|
8773
9033
|
"resourceRevisionIdentity",
|
|
8774
9034
|
"pullRequest",
|
|
8775
9035
|
"checkState",
|
|
8776
9036
|
"reviewDecision"
|
|
8777
|
-
])) || input.operation !== undefined && !isOperation4(input.operation)) {
|
|
9037
|
+
])) || input.operation !== undefined && !isOperation4(input.operation) || input.operation !== undefined && isOperation4(input.operation) && !isLocalOperation(input.operation) && Object.hasOwn(input, "operationTargetIdentity")) {
|
|
8778
9038
|
return;
|
|
8779
9039
|
}
|
|
8780
9040
|
const { operation: _operation, ...afterInput } = input;
|
|
@@ -8786,6 +9046,7 @@ function parseU4Readback(input) {
|
|
|
8786
9046
|
workspaceIdentity: after.workspaceIdentity,
|
|
8787
9047
|
repositoryIdentity: after.repositoryIdentity,
|
|
8788
9048
|
worktreeIdentity: after.worktreeIdentity,
|
|
9049
|
+
operationTargetIdentity: after.operationTargetIdentity,
|
|
8789
9050
|
resourceIdentity: after.resourceIdentity,
|
|
8790
9051
|
resourceRevisionIdentity: after.resourceRevisionIdentity,
|
|
8791
9052
|
pullRequest: after.pullRequest,
|
|
@@ -8936,11 +9197,21 @@ function createWorkflowGuard(options) {
|
|
|
8936
9197
|
return "epoch-mismatch";
|
|
8937
9198
|
if (input.context.unitId !== unit.unitId)
|
|
8938
9199
|
return "unit-mismatch";
|
|
8939
|
-
return currentIdentityReason(input.context) ?? resourceBeforeReason(input, unit);
|
|
9200
|
+
return currentIdentityReason(input.context) ?? operationTargetReason(input, unit) ?? resourceBeforeReason(input, unit);
|
|
9201
|
+
}
|
|
9202
|
+
function operationTargetReason(input, unit) {
|
|
9203
|
+
if (!isLocalOperation(input.operation))
|
|
9204
|
+
return;
|
|
9205
|
+
if (input.after && input.after.operationTargetIdentity !== input.context.operationTargetIdentity) {
|
|
9206
|
+
return "operation-target-mismatch";
|
|
9207
|
+
}
|
|
9208
|
+
return unit.pinnedOperationTargetIdentity !== undefined && input.context.operationTargetIdentity !== unit.pinnedOperationTargetIdentity ? "operation-target-mismatch" : undefined;
|
|
8940
9209
|
}
|
|
8941
9210
|
function currentIdentityReason(context) {
|
|
8942
9211
|
if (context.workspaceIdentity !== currentWorkspaceIdentity)
|
|
8943
9212
|
return "workspace-mismatch";
|
|
9213
|
+
if (context.operationTargetIdentity !== undefined)
|
|
9214
|
+
return;
|
|
8944
9215
|
if (context.repositoryIdentity !== currentRepositoryIdentity)
|
|
8945
9216
|
return "receipt-mismatch";
|
|
8946
9217
|
return context.worktreeIdentity !== currentWorktreeIdentity ? "receipt-mismatch" : undefined;
|
|
@@ -8971,7 +9242,7 @@ function createWorkflowGuard(options) {
|
|
|
8971
9242
|
if (input.after.workspaceIdentity !== input.context.workspaceIdentity) {
|
|
8972
9243
|
return "workspace-mismatch";
|
|
8973
9244
|
}
|
|
8974
|
-
return resourceAfterReason(input, unit);
|
|
9245
|
+
return operationTargetReason(input, unit) ?? resourceAfterReason(input, unit);
|
|
8975
9246
|
}
|
|
8976
9247
|
function resourceAfterReason(input, unit) {
|
|
8977
9248
|
if (!operationUsesResource(input.operation))
|
|
@@ -9243,6 +9514,9 @@ function createWorkflowGuard(options) {
|
|
|
9243
9514
|
if (envelope.canonical.unitDigest !== options.ledger.digestIdentity("unit", unit.unitId)) {
|
|
9244
9515
|
return "receipt-mismatch";
|
|
9245
9516
|
}
|
|
9517
|
+
if (isLocalOperation(envelope.canonical.operation) && unit.pinnedOperationTargetIdentity !== undefined && envelope.canonical.operationTargetIdentity !== unit.pinnedOperationTargetIdentity) {
|
|
9518
|
+
return "operation-target-mismatch";
|
|
9519
|
+
}
|
|
9246
9520
|
const historicalImplementation = envelope.canonical.operation === "implementation" && unit.evidence.get("implementation")?.canonical.receiptId === envelope.canonical.receiptId;
|
|
9247
9521
|
if (!historicalImplementation) {
|
|
9248
9522
|
if (envelope.canonical.workspaceDigest !== options.ledger.digestIdentity("workspace", currentWorkspaceIdentity)) {
|
|
@@ -9353,7 +9627,7 @@ function createWorkflowGuard(options) {
|
|
|
9353
9627
|
return { satisfied, missing };
|
|
9354
9628
|
}
|
|
9355
9629
|
function issueKind(reasonCode) {
|
|
9356
|
-
return reasonCode === "incompatible-receipt" || reasonCode === "guard-unavailable" ? "unavailable" : "rejected";
|
|
9630
|
+
return reasonCode === "incompatible-receipt" || reasonCode === "guard-unavailable" || reasonCode === "operation-target-mismatch" ? "unavailable" : "rejected";
|
|
9357
9631
|
}
|
|
9358
9632
|
function makeStatus(state, reasonCode, repair, satisfied = [], missing = []) {
|
|
9359
9633
|
const epochSnapshot = epoch ? cloneEpoch(epoch) : null;
|
|
@@ -9487,6 +9761,7 @@ function createWorkflowGuard(options) {
|
|
|
9487
9761
|
workspaceIdentity: currentWorkspaceIdentity,
|
|
9488
9762
|
repositoryIdentity: currentRepositoryIdentity,
|
|
9489
9763
|
worktreeIdentity: currentWorktreeIdentity,
|
|
9764
|
+
operationTargetIdentity: envelope.canonical.operationTargetIdentity,
|
|
9490
9765
|
resourceIdentity: ledgerResourceIdentity(operation, currentResource(unit, operation), currentResourceRevisionIdentities.get(operation))
|
|
9491
9766
|
};
|
|
9492
9767
|
}
|
|
@@ -9556,6 +9831,9 @@ function createWorkflowGuard(options) {
|
|
|
9556
9831
|
return { status: "accepted", operation };
|
|
9557
9832
|
}
|
|
9558
9833
|
unit.evidence.set(operation, read.envelope);
|
|
9834
|
+
if (isLocalOperation(operation) && unit.pinnedOperationTargetIdentity === undefined && read.envelope.canonical.operationTargetIdentity !== undefined) {
|
|
9835
|
+
unit.pinnedOperationTargetIdentity = read.envelope.canonical.operationTargetIdentity;
|
|
9836
|
+
}
|
|
9559
9837
|
clearIssue(unit, operation);
|
|
9560
9838
|
globalIssue = undefined;
|
|
9561
9839
|
return { status: "accepted", operation };
|
|
@@ -9566,6 +9844,7 @@ function createWorkflowGuard(options) {
|
|
|
9566
9844
|
workspaceIdentity: after.workspaceIdentity,
|
|
9567
9845
|
repositoryIdentity: after.repositoryIdentity,
|
|
9568
9846
|
worktreeIdentity: after.worktreeIdentity,
|
|
9847
|
+
...isLocalOperation(operation) ? { operationTargetIdentity: after.operationTargetIdentity } : {},
|
|
9569
9848
|
resourceIdentity: after.resourceIdentity,
|
|
9570
9849
|
resourceRevisionIdentity: after.resourceRevisionIdentity,
|
|
9571
9850
|
pullRequest: after.pullRequest,
|
|
@@ -9887,6 +10166,7 @@ function createWorkflowGuard(options) {
|
|
|
9887
10166
|
workspaceIdentity: parsed.after.workspaceIdentity,
|
|
9888
10167
|
repositoryIdentity: parsed.after.repositoryIdentity,
|
|
9889
10168
|
worktreeIdentity: parsed.after.worktreeIdentity,
|
|
10169
|
+
operationTargetIdentity: parsed.after.operationTargetIdentity,
|
|
9890
10170
|
resourceIdentity: parsed.after.resourceIdentity,
|
|
9891
10171
|
resourceRevisionIdentity: parsed.after.resourceRevisionIdentity
|
|
9892
10172
|
}));
|
|
@@ -9910,6 +10190,9 @@ function createWorkflowGuard(options) {
|
|
|
9910
10190
|
if (!global?.repositoryIdentity || !global.worktreeIdentity) {
|
|
9911
10191
|
return "invalid-receipt";
|
|
9912
10192
|
}
|
|
10193
|
+
if (unit.pinnedOperationTargetIdentity !== undefined && global.operationTargetIdentity !== unit.pinnedOperationTargetIdentity) {
|
|
10194
|
+
return "operation-target-mismatch";
|
|
10195
|
+
}
|
|
9913
10196
|
const required = RESOURCE_FINAL_READBACK_OPERATIONS.filter((operation) => unit.evidence.has(operation));
|
|
9914
10197
|
for (const operation of required) {
|
|
9915
10198
|
const readback = readbacks.find((item) => item.operation === operation);
|
|
@@ -10029,6 +10312,16 @@ function createWorkflowGuard(options) {
|
|
|
10029
10312
|
}
|
|
10030
10313
|
return;
|
|
10031
10314
|
}
|
|
10315
|
+
function recoveryTargetPinReason(unit, matching) {
|
|
10316
|
+
if (!unit)
|
|
10317
|
+
return;
|
|
10318
|
+
const localTargets = matching.filter((envelope) => isLocalOperation(envelope.canonical.operation)).map((envelope) => envelope.canonical.operationTargetIdentity);
|
|
10319
|
+
if (unit.pinnedOperationTargetIdentity !== undefined) {
|
|
10320
|
+
return localTargets.some((target) => target !== unit.pinnedOperationTargetIdentity) ? "operation-target-mismatch" : undefined;
|
|
10321
|
+
}
|
|
10322
|
+
const distinctTargets = new Set(localTargets.filter((target) => target !== undefined));
|
|
10323
|
+
return distinctTargets.size > 1 ? "operation-target-mismatch" : undefined;
|
|
10324
|
+
}
|
|
10032
10325
|
function resourceScopeMatches(operation, persisted, trusted) {
|
|
10033
10326
|
return persisted === trusted || persisted === options.ledger.digestIdentity("resource", trusted) || persisted === options.ledger.digestIdentity("resource", ledgerResourceIdentity(operation, trusted, currentResourceRevisionIdentities.get(operation)) ?? trusted);
|
|
10034
10327
|
}
|
|
@@ -10058,6 +10351,7 @@ function createWorkflowGuard(options) {
|
|
|
10058
10351
|
workspaceIdentity: currentWorkspaceIdentity,
|
|
10059
10352
|
repositoryIdentity: currentRepositoryIdentity,
|
|
10060
10353
|
worktreeIdentity: currentWorktreeIdentity,
|
|
10354
|
+
operationTargetIdentity: envelope.canonical.operationTargetIdentity,
|
|
10061
10355
|
resourceIdentity: resourceScopes.get(envelope.canonical.operation)
|
|
10062
10356
|
};
|
|
10063
10357
|
}
|
|
@@ -10151,6 +10445,9 @@ function createWorkflowGuard(options) {
|
|
|
10151
10445
|
const boundaryFailure = receiptSet.matching.map(recoveryBoundaryReason).find((reason) => reason !== undefined);
|
|
10152
10446
|
if (boundaryFailure)
|
|
10153
10447
|
return { status: "rejected", reasonCode: boundaryFailure };
|
|
10448
|
+
const targetPinFailure = recoveryTargetPinReason(state.progression.unit, receiptSet.matching);
|
|
10449
|
+
if (targetPinFailure)
|
|
10450
|
+
return { status: "rejected", reasonCode: targetPinFailure };
|
|
10154
10451
|
const completionFailure = recoveryCompletionReason(state.progression, receiptSet.matching);
|
|
10155
10452
|
if (completionFailure)
|
|
10156
10453
|
return { status: "rejected", reasonCode: completionFailure };
|
|
@@ -10160,12 +10457,16 @@ function createWorkflowGuard(options) {
|
|
|
10160
10457
|
const snapshot = progression.unit;
|
|
10161
10458
|
if (!snapshot)
|
|
10162
10459
|
return;
|
|
10460
|
+
const inferredOperationTargetIdentity = snapshot.pinnedOperationTargetIdentity ?? matching.find((envelope) => isLocalOperation(envelope.canonical.operation))?.canonical.operationTargetIdentity;
|
|
10163
10461
|
const unit = {
|
|
10164
10462
|
unitId: snapshot.unitId,
|
|
10165
10463
|
status: snapshot.state === "completed" ? "completed" : "active",
|
|
10166
10464
|
requiredOperations: Object.freeze([...snapshot.requiredOperations]),
|
|
10167
10465
|
declaredResourceOperations: Object.freeze(snapshot.resourceScopes.map((scope) => scope.operation)),
|
|
10168
10466
|
resourceScopes: new Map(resourceScopes),
|
|
10467
|
+
...inferredOperationTargetIdentity ? {
|
|
10468
|
+
pinnedOperationTargetIdentity: inferredOperationTargetIdentity
|
|
10469
|
+
} : {},
|
|
10169
10470
|
evidence: new Map,
|
|
10170
10471
|
issues: new Map,
|
|
10171
10472
|
staleReceiptIds: new Set,
|
|
@@ -10432,7 +10733,7 @@ function createWorkflowGuard(options) {
|
|
|
10432
10733
|
// src/lib/opencode-workflow-guard.ts
|
|
10433
10734
|
var MARKER_OPEN = "<SYSTEMATIC_WORKFLOW_GUARD>";
|
|
10434
10735
|
var MARKER_CLOSE = "</SYSTEMATIC_WORKFLOW_GUARD>";
|
|
10435
|
-
var MARKER_PROTOCOL_VERSION =
|
|
10736
|
+
var MARKER_PROTOCOL_VERSION = 2;
|
|
10436
10737
|
var MAX_MARKER_LENGTH = 4096;
|
|
10437
10738
|
var MAX_MARKER_SOURCES = 8;
|
|
10438
10739
|
var MAX_CALL_ID_LENGTH = 256;
|
|
@@ -10494,6 +10795,7 @@ var REASON_CODES = new Set([
|
|
|
10494
10795
|
"no-active-unit",
|
|
10495
10796
|
"no-op-operation",
|
|
10496
10797
|
"operation-not-required",
|
|
10798
|
+
"operation-target-mismatch",
|
|
10497
10799
|
"receipt-mismatch",
|
|
10498
10800
|
"rejected-operation",
|
|
10499
10801
|
"resource-mismatch",
|
|
@@ -10637,6 +10939,216 @@ function digestCall(ledger, callID) {
|
|
|
10637
10939
|
function isLocalOperationTool(tool) {
|
|
10638
10940
|
return tool === "write" || tool === "edit" || tool === "apply_patch" || tool === "bash";
|
|
10639
10941
|
}
|
|
10942
|
+
function unavailableOperationTarget() {
|
|
10943
|
+
return { status: "unavailable", reasonCode: "target-unavailable" };
|
|
10944
|
+
}
|
|
10945
|
+
function canonicalExistingPath(filePath, realPath) {
|
|
10946
|
+
try {
|
|
10947
|
+
return realPath(filePath);
|
|
10948
|
+
} catch {
|
|
10949
|
+
return;
|
|
10950
|
+
}
|
|
10951
|
+
}
|
|
10952
|
+
function registeredWorktreeIdentity(validation) {
|
|
10953
|
+
return validation.status === "ok" ? `${validation.gitDir}
|
|
10954
|
+
${validation.commonDir}` : undefined;
|
|
10955
|
+
}
|
|
10956
|
+
function canonicalFileTarget(filePath, realPath) {
|
|
10957
|
+
const existing = canonicalExistingPath(filePath, realPath);
|
|
10958
|
+
if (existing)
|
|
10959
|
+
return existing;
|
|
10960
|
+
const parent = canonicalExistingPath(path9.dirname(filePath), realPath);
|
|
10961
|
+
const basename = path9.basename(filePath);
|
|
10962
|
+
return parent && basename && basename !== "." && basename !== ".." ? path9.join(parent, basename) : undefined;
|
|
10963
|
+
}
|
|
10964
|
+
function pathWithinOrEqual(root, candidate) {
|
|
10965
|
+
const relative = path9.relative(root, candidate);
|
|
10966
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(relative);
|
|
10967
|
+
}
|
|
10968
|
+
function targetIsGitAdminStorage(targetPath, validation) {
|
|
10969
|
+
return pathWithinOrEqual(path9.join(validation.targetRoot, ".git"), targetPath) || pathWithinOrEqual(validation.gitDir, targetPath) || pathWithinOrEqual(validation.commonDir, targetPath);
|
|
10970
|
+
}
|
|
10971
|
+
function targetResultFromValidation(candidatePath, validation) {
|
|
10972
|
+
if (validation.status === "error")
|
|
10973
|
+
return unavailableOperationTarget();
|
|
10974
|
+
if (!pathWithinOrEqual(validation.targetRoot, candidatePath)) {
|
|
10975
|
+
return unavailableOperationTarget();
|
|
10976
|
+
}
|
|
10977
|
+
if (targetIsGitAdminStorage(candidatePath, validation)) {
|
|
10978
|
+
return unavailableOperationTarget();
|
|
10979
|
+
}
|
|
10980
|
+
return { status: "available", targetRoot: validation.targetRoot };
|
|
10981
|
+
}
|
|
10982
|
+
function trustedParentTarget(parentTargetRoot, candidatePath) {
|
|
10983
|
+
if (!pathWithinOrEqual(parentTargetRoot, candidatePath))
|
|
10984
|
+
return;
|
|
10985
|
+
if (pathWithinOrEqual(path9.join(parentTargetRoot, ".git"), candidatePath)) {
|
|
10986
|
+
return unavailableOperationTarget();
|
|
10987
|
+
}
|
|
10988
|
+
return { status: "available", targetRoot: parentTargetRoot };
|
|
10989
|
+
}
|
|
10990
|
+
function validationForCandidate(candidatePath, options, targetPath = candidatePath) {
|
|
10991
|
+
try {
|
|
10992
|
+
return targetResultFromValidation(targetPath, options.validateRegisteredWorktree(candidatePath));
|
|
10993
|
+
} catch {
|
|
10994
|
+
return unavailableOperationTarget();
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
function deriveFileTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath, allowParentFastPath = true) {
|
|
10998
|
+
const resolvedPath = path9.resolve(baseDirectory, rawPath);
|
|
10999
|
+
const canonicalPath2 = canonicalFileTarget(resolvedPath, realPath);
|
|
11000
|
+
if (!canonicalPath2)
|
|
11001
|
+
return unavailableOperationTarget();
|
|
11002
|
+
const parentResult = allowParentFastPath && !path9.isAbsolute(rawPath) ? trustedParentTarget(parentTargetRoot, canonicalPath2) : undefined;
|
|
11003
|
+
if (parentResult)
|
|
11004
|
+
return parentResult;
|
|
11005
|
+
return validationForCandidate(path9.dirname(canonicalPath2), options, canonicalPath2);
|
|
11006
|
+
}
|
|
11007
|
+
function sharedTargetRoot(targets, extraRoots = []) {
|
|
11008
|
+
const roots = [...extraRoots];
|
|
11009
|
+
for (const target of targets) {
|
|
11010
|
+
if (target.status === "unavailable")
|
|
11011
|
+
return;
|
|
11012
|
+
roots.push(target.targetRoot);
|
|
11013
|
+
}
|
|
11014
|
+
const targetRoot = roots[0];
|
|
11015
|
+
return targetRoot && roots.every((root) => root === targetRoot) ? targetRoot : undefined;
|
|
11016
|
+
}
|
|
11017
|
+
function deriveDirectoryTarget(rawPath, baseDirectory, parentTargetRoot, options, realPath) {
|
|
11018
|
+
const resolvedPath = canonicalExistingPath(path9.resolve(baseDirectory, rawPath), realPath);
|
|
11019
|
+
if (!resolvedPath)
|
|
11020
|
+
return { status: "unavailable" };
|
|
11021
|
+
const parentResult = path9.isAbsolute(rawPath) ? undefined : trustedParentTarget(parentTargetRoot, resolvedPath);
|
|
11022
|
+
if (parentResult?.status === "unavailable") {
|
|
11023
|
+
return { status: "unavailable" };
|
|
11024
|
+
}
|
|
11025
|
+
if (parentResult) {
|
|
11026
|
+
return {
|
|
11027
|
+
status: "available",
|
|
11028
|
+
target: { targetRoot: parentResult.targetRoot, resolvedPath }
|
|
11029
|
+
};
|
|
11030
|
+
}
|
|
11031
|
+
const validated = validationForCandidate(resolvedPath, options);
|
|
11032
|
+
return validated.status === "available" ? {
|
|
11033
|
+
status: "available",
|
|
11034
|
+
target: { targetRoot: validated.targetRoot, resolvedPath }
|
|
11035
|
+
} : { status: "unavailable" };
|
|
11036
|
+
}
|
|
11037
|
+
var PATCH_FILE_PREFIXES = [
|
|
11038
|
+
"*** Add File:",
|
|
11039
|
+
"*** Delete File:",
|
|
11040
|
+
"*** Update File:",
|
|
11041
|
+
"*** Move to:"
|
|
11042
|
+
];
|
|
11043
|
+
function patchTextFileTargets(patchText) {
|
|
11044
|
+
const paths = [];
|
|
11045
|
+
for (const line of patchText.split(/\r?\n/)) {
|
|
11046
|
+
const prefix = PATCH_FILE_PREFIXES.find((candidate) => line.startsWith(candidate));
|
|
11047
|
+
if (!prefix)
|
|
11048
|
+
continue;
|
|
11049
|
+
const filePath = line.slice(prefix.length).trim();
|
|
11050
|
+
if (!filePath)
|
|
11051
|
+
return;
|
|
11052
|
+
paths.push(filePath);
|
|
11053
|
+
}
|
|
11054
|
+
return paths;
|
|
11055
|
+
}
|
|
11056
|
+
function hunkFileTargets(value) {
|
|
11057
|
+
if (!Array.isArray(value))
|
|
11058
|
+
return;
|
|
11059
|
+
const paths = [];
|
|
11060
|
+
for (const hunk of value) {
|
|
11061
|
+
if (!isRecord7(hunk) || typeof hunk.path !== "string" || !hunk.path) {
|
|
11062
|
+
return;
|
|
11063
|
+
}
|
|
11064
|
+
paths.push(hunk.path);
|
|
11065
|
+
if (hunk.move_path === undefined)
|
|
11066
|
+
continue;
|
|
11067
|
+
if (typeof hunk.move_path !== "string" || !hunk.move_path)
|
|
11068
|
+
return;
|
|
11069
|
+
paths.push(hunk.move_path);
|
|
11070
|
+
}
|
|
11071
|
+
return paths;
|
|
11072
|
+
}
|
|
11073
|
+
function patchFileTargets(args2) {
|
|
11074
|
+
const patchValue = args2.patchText ?? args2.patch;
|
|
11075
|
+
if (typeof patchValue === "string")
|
|
11076
|
+
return patchTextFileTargets(patchValue);
|
|
11077
|
+
if (patchValue !== undefined)
|
|
11078
|
+
return;
|
|
11079
|
+
return args2.hunks === undefined ? [] : hunkFileTargets(args2.hunks);
|
|
11080
|
+
}
|
|
11081
|
+
function fileTargetArguments(args2) {
|
|
11082
|
+
const paths = [];
|
|
11083
|
+
for (const key of ["filePath", "path"]) {
|
|
11084
|
+
if (!(key in args2))
|
|
11085
|
+
continue;
|
|
11086
|
+
if (typeof args2[key] !== "string" || args2[key].length === 0) {
|
|
11087
|
+
return;
|
|
11088
|
+
}
|
|
11089
|
+
paths.push(args2[key]);
|
|
11090
|
+
}
|
|
11091
|
+
return paths.length > 0 ? paths : undefined;
|
|
11092
|
+
}
|
|
11093
|
+
function deriveFileOperationTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
|
|
11094
|
+
const paths = fileTargetArguments(args2);
|
|
11095
|
+
if (!paths)
|
|
11096
|
+
return unavailableOperationTarget();
|
|
11097
|
+
const targets = paths.map((rawPath) => deriveFileTarget(rawPath, sessionLocation, parentTargetRoot, options, realPath));
|
|
11098
|
+
const targetRoot = sharedTargetRoot(targets);
|
|
11099
|
+
return targetRoot ? { status: "available", targetRoot } : unavailableOperationTarget();
|
|
11100
|
+
}
|
|
11101
|
+
function deriveApplyPatchTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
|
|
11102
|
+
if (args2.workdir !== undefined && (typeof args2.workdir !== "string" || args2.workdir.length === 0)) {
|
|
11103
|
+
return unavailableOperationTarget();
|
|
11104
|
+
}
|
|
11105
|
+
const patchPaths = patchFileTargets(args2);
|
|
11106
|
+
if (!patchPaths)
|
|
11107
|
+
return unavailableOperationTarget();
|
|
11108
|
+
const workdir = args2.workdir === undefined ? {
|
|
11109
|
+
status: "available",
|
|
11110
|
+
target: {
|
|
11111
|
+
targetRoot: parentTargetRoot,
|
|
11112
|
+
resolvedPath: sessionLocation
|
|
11113
|
+
}
|
|
11114
|
+
} : deriveDirectoryTarget(args2.workdir, sessionLocation, parentTargetRoot, options, realPath);
|
|
11115
|
+
if (workdir.status === "unavailable")
|
|
11116
|
+
return unavailableOperationTarget();
|
|
11117
|
+
const targets = patchPaths.map((rawPath) => deriveFileTarget(rawPath, workdir.target.resolvedPath, parentTargetRoot, options, realPath, args2.workdir === undefined));
|
|
11118
|
+
const targetRoot = sharedTargetRoot(targets, [workdir.target.targetRoot]);
|
|
11119
|
+
return targetRoot ? { status: "available", targetRoot } : unavailableOperationTarget();
|
|
11120
|
+
}
|
|
11121
|
+
function deriveBashTarget(args2, sessionLocation, parentTargetRoot, options, realPath) {
|
|
11122
|
+
if (args2.workdir === undefined) {
|
|
11123
|
+
return { status: "available", targetRoot: parentTargetRoot };
|
|
11124
|
+
}
|
|
11125
|
+
if (typeof args2.workdir !== "string" || args2.workdir.length === 0) {
|
|
11126
|
+
return unavailableOperationTarget();
|
|
11127
|
+
}
|
|
11128
|
+
const candidatePath = canonicalExistingPath(path9.resolve(sessionLocation, args2.workdir), realPath);
|
|
11129
|
+
if (!candidatePath)
|
|
11130
|
+
return unavailableOperationTarget();
|
|
11131
|
+
const parentResult = path9.isAbsolute(args2.workdir) ? undefined : trustedParentTarget(parentTargetRoot, candidatePath);
|
|
11132
|
+
return parentResult ?? validationForCandidate(candidatePath, options);
|
|
11133
|
+
}
|
|
11134
|
+
function deriveOpencodeOperationTarget(tool, args2, options) {
|
|
11135
|
+
const realPath = options.realPath ?? fs10.realpathSync;
|
|
11136
|
+
const parentTargetRoot = canonicalExistingPath(options.parentTargetRoot, realPath);
|
|
11137
|
+
if (!parentTargetRoot)
|
|
11138
|
+
return unavailableOperationTarget();
|
|
11139
|
+
if (!isRecord7(args2))
|
|
11140
|
+
return unavailableOperationTarget();
|
|
11141
|
+
const sessionLocation = canonicalExistingPath(options.sessionLocation ?? parentTargetRoot, realPath);
|
|
11142
|
+
if (!sessionLocation)
|
|
11143
|
+
return unavailableOperationTarget();
|
|
11144
|
+
if (tool === "write" || tool === "edit") {
|
|
11145
|
+
return deriveFileOperationTarget(args2, sessionLocation, parentTargetRoot, options, realPath);
|
|
11146
|
+
}
|
|
11147
|
+
if (tool === "apply_patch") {
|
|
11148
|
+
return deriveApplyPatchTarget(args2, sessionLocation, parentTargetRoot, options, realPath);
|
|
11149
|
+
}
|
|
11150
|
+
return tool === "bash" ? deriveBashTarget(args2, sessionLocation, parentTargetRoot, options, realPath) : unavailableOperationTarget();
|
|
11151
|
+
}
|
|
10640
11152
|
function serializeStableArray(value, depth, budget) {
|
|
10641
11153
|
const entries = [];
|
|
10642
11154
|
let remaining = budget;
|
|
@@ -10719,7 +11231,7 @@ function terminalForOutput(tool, output) {
|
|
|
10719
11231
|
};
|
|
10720
11232
|
}
|
|
10721
11233
|
function localOperation(operation) {
|
|
10722
|
-
return operation
|
|
11234
|
+
return operation !== null && isLocalOperation(operation);
|
|
10723
11235
|
}
|
|
10724
11236
|
function remoteOperation(operation) {
|
|
10725
11237
|
return operation === "push" || operation === "pr-creation" || operation === "check-readback" || operation === "review-readback";
|
|
@@ -10905,7 +11417,7 @@ function buildMarker(sources, malformed, current) {
|
|
|
10905
11417
|
};
|
|
10906
11418
|
return `${MARKER_OPEN}${JSON.stringify(document2)}${MARKER_CLOSE}`;
|
|
10907
11419
|
}
|
|
10908
|
-
function createSessionRuntime(options) {
|
|
11420
|
+
function createSessionRuntime(options, operationObservers, recoveredOperationContexts) {
|
|
10909
11421
|
let ledger;
|
|
10910
11422
|
let guard;
|
|
10911
11423
|
let initialized = false;
|
|
@@ -10928,6 +11440,74 @@ function createSessionRuntime(options) {
|
|
|
10928
11440
|
const pendingQuestionChallenges = new Map;
|
|
10929
11441
|
const blockedQuestionCalls = new Map;
|
|
10930
11442
|
const consumedQuestionTargets = new Set;
|
|
11443
|
+
function rememberOperationObserver(registration) {
|
|
11444
|
+
const existing = operationObservers.get(registration.observer.targetDigest);
|
|
11445
|
+
if (existing && existing.targetRoot !== registration.targetRoot)
|
|
11446
|
+
return;
|
|
11447
|
+
operationObservers.set(registration.observer.targetDigest, registration);
|
|
11448
|
+
}
|
|
11449
|
+
function parentObserverRegistration() {
|
|
11450
|
+
if (!options.observer)
|
|
11451
|
+
return;
|
|
11452
|
+
const targetRoot = canonicalExistingPath(options.targetDirectory ?? process.cwd(), fs10.realpathSync);
|
|
11453
|
+
if (!targetRoot)
|
|
11454
|
+
return;
|
|
11455
|
+
let validation;
|
|
11456
|
+
try {
|
|
11457
|
+
validation = options.observer.validateRegisteredWorktree(targetRoot);
|
|
11458
|
+
} catch {
|
|
11459
|
+
return;
|
|
11460
|
+
}
|
|
11461
|
+
const registeredIdentity = registeredWorktreeIdentity(validation);
|
|
11462
|
+
if (validation.status !== "ok" || registeredIdentity === undefined || validation.targetRoot !== targetRoot) {
|
|
11463
|
+
return;
|
|
11464
|
+
}
|
|
11465
|
+
const registration = {
|
|
11466
|
+
targetRoot,
|
|
11467
|
+
registeredWorktreeIdentity: registeredIdentity,
|
|
11468
|
+
observer: options.observer
|
|
11469
|
+
};
|
|
11470
|
+
rememberOperationObserver(registration);
|
|
11471
|
+
return registration;
|
|
11472
|
+
}
|
|
11473
|
+
function operationObserverRegistration(targetIdentity) {
|
|
11474
|
+
const existing = operationObservers.get(targetIdentity);
|
|
11475
|
+
if (existing)
|
|
11476
|
+
return existing;
|
|
11477
|
+
if (options.observer?.targetDigest !== targetIdentity)
|
|
11478
|
+
return;
|
|
11479
|
+
return parentObserverRegistration();
|
|
11480
|
+
}
|
|
11481
|
+
function effectiveOperationObserver() {
|
|
11482
|
+
const parentObserver = options.observer;
|
|
11483
|
+
if (!parentObserver)
|
|
11484
|
+
return;
|
|
11485
|
+
const pinnedTargetIdentity = guard.status().unit?.pinnedOperationTargetIdentity;
|
|
11486
|
+
if (pinnedTargetIdentity === undefined || pinnedTargetIdentity === options.workspaceIdentity) {
|
|
11487
|
+
return {
|
|
11488
|
+
observer: parentObserver,
|
|
11489
|
+
targetIdentity: options.workspaceIdentity,
|
|
11490
|
+
pinned: false
|
|
11491
|
+
};
|
|
11492
|
+
}
|
|
11493
|
+
const registration = operationObserverRegistration(pinnedTargetIdentity);
|
|
11494
|
+
if (!registration)
|
|
11495
|
+
return;
|
|
11496
|
+
let validation;
|
|
11497
|
+
try {
|
|
11498
|
+
validation = parentObserver.validateRegisteredWorktree(registration.targetRoot);
|
|
11499
|
+
} catch {
|
|
11500
|
+
return;
|
|
11501
|
+
}
|
|
11502
|
+
if (validation.status !== "ok" || validation.targetRoot !== registration.targetRoot || registeredWorktreeIdentity(validation) !== registration.registeredWorktreeIdentity || registration.observer.targetDigest !== pinnedTargetIdentity) {
|
|
11503
|
+
return;
|
|
11504
|
+
}
|
|
11505
|
+
return {
|
|
11506
|
+
observer: registration.observer,
|
|
11507
|
+
targetIdentity: pinnedTargetIdentity,
|
|
11508
|
+
pinned: true
|
|
11509
|
+
};
|
|
11510
|
+
}
|
|
10931
11511
|
function questionResource(target) {
|
|
10932
11512
|
const status = guard.status();
|
|
10933
11513
|
return `workflow/${target}/${status.unit?.unitId ?? status.epoch?.epochId ?? "unknown"}`;
|
|
@@ -11330,17 +11910,14 @@ function createSessionRuntime(options) {
|
|
|
11330
11910
|
markUnavailable();
|
|
11331
11911
|
return;
|
|
11332
11912
|
}
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
return;
|
|
11337
|
-
}
|
|
11913
|
+
try {
|
|
11914
|
+
await options.observer?.snapshot();
|
|
11915
|
+
} catch {}
|
|
11338
11916
|
const currentStatus = guard.status();
|
|
11339
11917
|
if (!currentStatus.epoch || !currentStatus.unit)
|
|
11340
11918
|
return;
|
|
11341
11919
|
const expectedWorkspace = childLedger.digestIdentity("workspace", parentBefore.workspaceIdentity);
|
|
11342
|
-
|
|
11343
|
-
const expectedWorktree = childLedger.digestIdentity("worktree", current.snapshot.worktreeRevisionDigest);
|
|
11920
|
+
let batchTargetIdentity = currentStatus.unit.pinnedOperationTargetIdentity;
|
|
11344
11921
|
const candidates = [];
|
|
11345
11922
|
for (const childReceipt of recovered.receipts) {
|
|
11346
11923
|
const operation = childReceipt.canonical.operation;
|
|
@@ -11350,15 +11927,66 @@ function createSessionRuntime(options) {
|
|
|
11350
11927
|
markUnavailable();
|
|
11351
11928
|
return;
|
|
11352
11929
|
}
|
|
11930
|
+
const targetIdentity = childReceipt.canonical.operationTargetIdentity;
|
|
11931
|
+
if (!targetIdentity) {
|
|
11932
|
+
markUnavailable();
|
|
11933
|
+
return;
|
|
11934
|
+
}
|
|
11935
|
+
if (batchTargetIdentity !== undefined && targetIdentity !== batchTargetIdentity) {
|
|
11936
|
+
markUnavailable();
|
|
11937
|
+
return;
|
|
11938
|
+
}
|
|
11939
|
+
batchTargetIdentity = targetIdentity;
|
|
11940
|
+
const registration = operationObserverRegistration(targetIdentity);
|
|
11941
|
+
if (!registration || !options.observer) {
|
|
11942
|
+
markUnavailable();
|
|
11943
|
+
return;
|
|
11944
|
+
}
|
|
11945
|
+
let validation;
|
|
11946
|
+
try {
|
|
11947
|
+
validation = options.observer.validateRegisteredWorktree(registration.targetRoot);
|
|
11948
|
+
} catch {
|
|
11949
|
+
markUnavailable();
|
|
11950
|
+
return;
|
|
11951
|
+
}
|
|
11952
|
+
if (validation.status === "error" || validation.targetRoot !== registration.targetRoot || registeredWorktreeIdentity(validation) !== registration.registeredWorktreeIdentity || registration.observer.targetDigest !== targetIdentity) {
|
|
11953
|
+
markUnavailable();
|
|
11954
|
+
return;
|
|
11955
|
+
}
|
|
11956
|
+
let targetResult;
|
|
11957
|
+
try {
|
|
11958
|
+
targetResult = await registration.observer.snapshot();
|
|
11959
|
+
} catch {
|
|
11960
|
+
markUnavailable();
|
|
11961
|
+
return;
|
|
11962
|
+
}
|
|
11963
|
+
if (targetResult.status === "unavailable" || targetResult.snapshot.targetDigest !== targetIdentity) {
|
|
11964
|
+
markUnavailable();
|
|
11965
|
+
return;
|
|
11966
|
+
}
|
|
11967
|
+
const recoveredContext = recoveredOperationContexts.get(childReceipt.canonical.receiptId);
|
|
11968
|
+
if (recoveredContext && recoveredContext.targetIdentity !== targetIdentity || targetIdentity !== options.observer.targetDigest && !recoveredContext) {
|
|
11969
|
+
markUnavailable();
|
|
11970
|
+
return;
|
|
11971
|
+
}
|
|
11972
|
+
const expectedRepository = childLedger.digestIdentity("repository", targetResult.snapshot.repositoryRevisionDigest);
|
|
11973
|
+
const expectedWorktree = childLedger.digestIdentity("worktree", targetResult.snapshot.worktreeRevisionDigest);
|
|
11353
11974
|
if (childReceipt.canonical.repositoryDigest !== expectedRepository || childReceipt.canonical.worktreeDigest !== expectedWorktree) {
|
|
11354
11975
|
continue;
|
|
11355
11976
|
}
|
|
11356
|
-
candidates.push(
|
|
11977
|
+
candidates.push({
|
|
11978
|
+
receipt: childReceipt,
|
|
11979
|
+
snapshot: targetResult.snapshot,
|
|
11980
|
+
before: recoveredContext?.before
|
|
11981
|
+
});
|
|
11357
11982
|
}
|
|
11358
11983
|
let minted = false;
|
|
11359
|
-
for (const
|
|
11984
|
+
for (const candidate of candidates) {
|
|
11985
|
+
const childReceipt = candidate.receipt;
|
|
11986
|
+
const targetSnapshot = candidate.snapshot;
|
|
11360
11987
|
const operation = childReceipt.canonical.operation;
|
|
11361
11988
|
const parentContext = guard.currentOperationContext();
|
|
11989
|
+
const beforeSnapshot = candidate.before;
|
|
11362
11990
|
const callID = `task-${host.callID}-${childReceipt.canonical.receiptId}`;
|
|
11363
11991
|
const observation = {
|
|
11364
11992
|
callId: callID,
|
|
@@ -11368,13 +11996,15 @@ function createSessionRuntime(options) {
|
|
|
11368
11996
|
epochId: currentStatus.epoch.epochId,
|
|
11369
11997
|
unitId: currentStatus.unit.unitId,
|
|
11370
11998
|
workspaceIdentity: parentContext.workspaceIdentity,
|
|
11371
|
-
|
|
11372
|
-
|
|
11999
|
+
operationTargetIdentity: childReceipt.canonical.operationTargetIdentity,
|
|
12000
|
+
repositoryIdentity: beforeSnapshot?.repositoryRevisionDigest ?? parentContext.repositoryIdentity ?? targetSnapshot.repositoryRevisionDigest,
|
|
12001
|
+
worktreeIdentity: beforeSnapshot?.worktreeRevisionDigest ?? parentContext.worktreeIdentity ?? targetSnapshot.worktreeRevisionDigest
|
|
11373
12002
|
},
|
|
11374
12003
|
after: {
|
|
11375
12004
|
workspaceIdentity: parentContext.workspaceIdentity,
|
|
11376
|
-
|
|
11377
|
-
|
|
12005
|
+
operationTargetIdentity: childReceipt.canonical.operationTargetIdentity,
|
|
12006
|
+
repositoryIdentity: targetSnapshot.repositoryRevisionDigest,
|
|
12007
|
+
worktreeIdentity: targetSnapshot.worktreeRevisionDigest
|
|
11378
12008
|
},
|
|
11379
12009
|
terminal: {
|
|
11380
12010
|
status: "success",
|
|
@@ -11477,35 +12107,37 @@ function createSessionRuntime(options) {
|
|
|
11477
12107
|
abandonPending();
|
|
11478
12108
|
if (!options.observer)
|
|
11479
12109
|
return;
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
result = await options.observer.snapshot();
|
|
11483
|
-
} catch {
|
|
12110
|
+
const effective = effectiveOperationObserver();
|
|
12111
|
+
if (!effective) {
|
|
11484
12112
|
markUnavailable();
|
|
11485
12113
|
return;
|
|
11486
12114
|
}
|
|
11487
|
-
|
|
12115
|
+
let result;
|
|
12116
|
+
try {
|
|
12117
|
+
result = await effective.observer.snapshot();
|
|
12118
|
+
} catch {
|
|
11488
12119
|
markUnavailable();
|
|
11489
12120
|
return;
|
|
11490
12121
|
}
|
|
11491
|
-
if (result.snapshot.targetDigest !==
|
|
12122
|
+
if (result.status === "unavailable" || result.snapshot.targetDigest !== effective.targetIdentity) {
|
|
11492
12123
|
markUnavailable();
|
|
11493
12124
|
return;
|
|
11494
12125
|
}
|
|
11495
12126
|
const observed = guard.observeReadback({
|
|
11496
12127
|
workspaceIdentity: options.workspaceIdentity,
|
|
11497
12128
|
repositoryIdentity: result.snapshot.repositoryRevisionDigest,
|
|
11498
|
-
worktreeIdentity: result.snapshot.worktreeRevisionDigest
|
|
12129
|
+
worktreeIdentity: result.snapshot.worktreeRevisionDigest,
|
|
12130
|
+
...effective.pinned ? { operationTargetIdentity: effective.targetIdentity } : {}
|
|
11499
12131
|
});
|
|
11500
|
-
if (observed.status === "rejected" && observed.reasonCode === "workspace-mismatch" || !await refreshRemoteReadbacks(result.snapshot)) {
|
|
12132
|
+
if (observed.status === "rejected" && observed.reasonCode === "workspace-mismatch" || !await refreshRemoteReadbacks(effective.observer, result.snapshot)) {
|
|
11501
12133
|
markUnavailable();
|
|
11502
12134
|
}
|
|
11503
12135
|
}
|
|
11504
|
-
async function refreshRemoteReadbacks(local) {
|
|
12136
|
+
async function refreshRemoteReadbacks(observer, local) {
|
|
11505
12137
|
const remoteOperations = guard.status().satisfiedOperations.filter(remoteOperation);
|
|
11506
12138
|
if (remoteOperations.length === 0)
|
|
11507
12139
|
return true;
|
|
11508
|
-
const remoteSnapshot =
|
|
12140
|
+
const remoteSnapshot = observer.remoteSnapshot;
|
|
11509
12141
|
if (!remoteSnapshot)
|
|
11510
12142
|
return false;
|
|
11511
12143
|
for (const operation of remoteOperations) {
|
|
@@ -11711,7 +12343,7 @@ function createSessionRuntime(options) {
|
|
|
11711
12343
|
}
|
|
11712
12344
|
blockedCompletes.set(callDigest, target);
|
|
11713
12345
|
}
|
|
11714
|
-
async function remoteIntentForOperation(host, args2) {
|
|
12346
|
+
async function remoteIntentForOperation(host, args2, observer) {
|
|
11715
12347
|
if (host.tool !== "bash" || !isRecord7(args2))
|
|
11716
12348
|
return;
|
|
11717
12349
|
const command = bashCommand(args2);
|
|
@@ -11720,7 +12352,7 @@ function createSessionRuntime(options) {
|
|
|
11720
12352
|
const classification = await classifyCommandIntent(options.classifier, command);
|
|
11721
12353
|
if (!classification || !remoteOperation(classification.category))
|
|
11722
12354
|
return;
|
|
11723
|
-
const remoteSnapshot =
|
|
12355
|
+
const remoteSnapshot = observer.remoteSnapshot;
|
|
11724
12356
|
if (!remoteSnapshot)
|
|
11725
12357
|
return { operation: classification.category };
|
|
11726
12358
|
try {
|
|
@@ -11744,9 +12376,40 @@ function createSessionRuntime(options) {
|
|
|
11744
12376
|
if (bindCall(callDigest, "operation", `${host.tool}:${fingerprint}`) !== "new") {
|
|
11745
12377
|
return;
|
|
11746
12378
|
}
|
|
12379
|
+
const parentTargetRoot = options.targetDirectory ?? process.cwd();
|
|
12380
|
+
const sessionLocation = options.sessionLocation ?? parentTargetRoot;
|
|
12381
|
+
const target = deriveOpencodeOperationTarget(host.tool, args2, {
|
|
12382
|
+
parentTargetRoot,
|
|
12383
|
+
sessionLocation,
|
|
12384
|
+
validateRegisteredWorktree: options.observer.validateRegisteredWorktree
|
|
12385
|
+
});
|
|
12386
|
+
if (target.status === "unavailable") {
|
|
12387
|
+
markUnavailable();
|
|
12388
|
+
return;
|
|
12389
|
+
}
|
|
12390
|
+
let registeredTarget;
|
|
12391
|
+
try {
|
|
12392
|
+
registeredTarget = options.observer.validateRegisteredWorktree(target.targetRoot);
|
|
12393
|
+
} catch {
|
|
12394
|
+
markUnavailable();
|
|
12395
|
+
return;
|
|
12396
|
+
}
|
|
12397
|
+
const registeredIdentity = registeredWorktreeIdentity(registeredTarget);
|
|
12398
|
+
if (registeredTarget.status !== "ok" || registeredIdentity === undefined || registeredTarget.targetRoot !== target.targetRoot) {
|
|
12399
|
+
markUnavailable();
|
|
12400
|
+
return;
|
|
12401
|
+
}
|
|
12402
|
+
const canonicalParentTargetRoot = canonicalExistingPath(parentTargetRoot, fs10.realpathSync);
|
|
12403
|
+
if (!canonicalParentTargetRoot) {
|
|
12404
|
+
markUnavailable();
|
|
12405
|
+
return;
|
|
12406
|
+
}
|
|
12407
|
+
const operationObserver = target.targetRoot === canonicalParentTargetRoot ? options.observer : [...operationObservers.values()].find((registration) => registration.targetRoot === target.targetRoot)?.observer ?? createOpencodeOperationObserver({
|
|
12408
|
+
targetDirectory: target.targetRoot
|
|
12409
|
+
});
|
|
11747
12410
|
let result;
|
|
11748
12411
|
try {
|
|
11749
|
-
result = await
|
|
12412
|
+
result = await operationObserver.snapshot();
|
|
11750
12413
|
} catch {
|
|
11751
12414
|
markUnavailable();
|
|
11752
12415
|
return;
|
|
@@ -11755,15 +12418,24 @@ function createSessionRuntime(options) {
|
|
|
11755
12418
|
markUnavailable();
|
|
11756
12419
|
return;
|
|
11757
12420
|
}
|
|
11758
|
-
if (result.snapshot.targetDigest !==
|
|
12421
|
+
if (result.snapshot.targetDigest !== operationObserver.targetDigest) {
|
|
11759
12422
|
markUnavailable();
|
|
11760
12423
|
return;
|
|
11761
12424
|
}
|
|
11762
|
-
|
|
12425
|
+
rememberOperationObserver({
|
|
12426
|
+
targetRoot: target.targetRoot,
|
|
12427
|
+
registeredWorktreeIdentity: registeredIdentity,
|
|
12428
|
+
observer: operationObserver
|
|
12429
|
+
});
|
|
12430
|
+
const remote = await remoteIntentForOperation(host, args2, operationObserver);
|
|
11763
12431
|
pendingOperations.set(callDigest, {
|
|
11764
12432
|
callID: host.callID,
|
|
11765
12433
|
tool: host.tool,
|
|
11766
12434
|
argsFingerprint: fingerprint,
|
|
12435
|
+
targetRoot: target.targetRoot,
|
|
12436
|
+
targetIdentity: operationObserver.targetDigest,
|
|
12437
|
+
registeredWorktreeIdentity: registeredIdentity,
|
|
12438
|
+
observer: operationObserver,
|
|
11767
12439
|
before: result.snapshot,
|
|
11768
12440
|
...remote ? { remoteOperation: remote.operation, remoteBefore: remote.before } : {}
|
|
11769
12441
|
});
|
|
@@ -12079,44 +12751,87 @@ function createSessionRuntime(options) {
|
|
|
12079
12751
|
async function completionReadbacks() {
|
|
12080
12752
|
if (!options.observer)
|
|
12081
12753
|
return { status: "none" };
|
|
12082
|
-
let
|
|
12754
|
+
let parentResult;
|
|
12083
12755
|
try {
|
|
12084
|
-
|
|
12756
|
+
parentResult = await options.observer.snapshot();
|
|
12085
12757
|
} catch {
|
|
12086
12758
|
return { status: "unavailable" };
|
|
12087
12759
|
}
|
|
12088
|
-
if (
|
|
12760
|
+
if (parentResult.status === "unavailable" || parentResult.snapshot.targetDigest !== options.workspaceIdentity) {
|
|
12761
|
+
return { status: "unavailable" };
|
|
12762
|
+
}
|
|
12763
|
+
const effective = effectiveOperationObserver();
|
|
12764
|
+
if (!effective)
|
|
12089
12765
|
return { status: "unavailable" };
|
|
12766
|
+
let effectiveSnapshot = parentResult.snapshot;
|
|
12767
|
+
if (effective.pinned) {
|
|
12768
|
+
let pinnedResult;
|
|
12769
|
+
try {
|
|
12770
|
+
pinnedResult = await effective.observer.snapshot();
|
|
12771
|
+
} catch {
|
|
12772
|
+
return { status: "unavailable" };
|
|
12773
|
+
}
|
|
12774
|
+
if (pinnedResult.status === "unavailable" || pinnedResult.snapshot.targetDigest !== effective.targetIdentity) {
|
|
12775
|
+
return { status: "unavailable" };
|
|
12776
|
+
}
|
|
12777
|
+
effectiveSnapshot = pinnedResult.snapshot;
|
|
12090
12778
|
}
|
|
12091
|
-
const
|
|
12779
|
+
const initialEffectiveSnapshot = effectiveSnapshot;
|
|
12780
|
+
const remoteReadbacks = await completionRemoteReadbacks(effective.observer);
|
|
12092
12781
|
if (!remoteReadbacks)
|
|
12093
12782
|
return { status: "unavailable" };
|
|
12094
|
-
let
|
|
12783
|
+
let finalParentResult;
|
|
12095
12784
|
try {
|
|
12096
|
-
|
|
12785
|
+
finalParentResult = await options.observer.snapshot();
|
|
12097
12786
|
} catch {
|
|
12098
12787
|
return { status: "unavailable" };
|
|
12099
12788
|
}
|
|
12100
|
-
if (
|
|
12789
|
+
if (finalParentResult.status === "unavailable" || finalParentResult.snapshot.targetDigest !== parentResult.snapshot.targetDigest || finalParentResult.snapshot.repositoryRevisionDigest !== parentResult.snapshot.repositoryRevisionDigest || finalParentResult.snapshot.worktreeRevisionDigest !== parentResult.snapshot.worktreeRevisionDigest) {
|
|
12101
12790
|
return { status: "unavailable" };
|
|
12102
12791
|
}
|
|
12792
|
+
let finalEffective = effective;
|
|
12793
|
+
if (effective.pinned) {
|
|
12794
|
+
const revalidated = effectiveOperationObserver();
|
|
12795
|
+
if (!revalidated)
|
|
12796
|
+
return { status: "unavailable" };
|
|
12797
|
+
finalEffective = revalidated;
|
|
12798
|
+
if (finalEffective.observer !== effective.observer || finalEffective.targetIdentity !== effective.targetIdentity || !finalEffective.pinned) {
|
|
12799
|
+
return { status: "unavailable" };
|
|
12800
|
+
}
|
|
12801
|
+
let pinnedResult;
|
|
12802
|
+
try {
|
|
12803
|
+
pinnedResult = await finalEffective.observer.snapshot();
|
|
12804
|
+
} catch {
|
|
12805
|
+
return { status: "unavailable" };
|
|
12806
|
+
}
|
|
12807
|
+
if (pinnedResult.status === "unavailable" || pinnedResult.snapshot.targetDigest !== finalEffective.targetIdentity) {
|
|
12808
|
+
return { status: "unavailable" };
|
|
12809
|
+
}
|
|
12810
|
+
effectiveSnapshot = pinnedResult.snapshot;
|
|
12811
|
+
if (effectiveSnapshot.repositoryRevisionDigest !== initialEffectiveSnapshot.repositoryRevisionDigest || effectiveSnapshot.worktreeRevisionDigest !== initialEffectiveSnapshot.worktreeRevisionDigest) {
|
|
12812
|
+
return { status: "unavailable" };
|
|
12813
|
+
}
|
|
12814
|
+
} else {
|
|
12815
|
+
effectiveSnapshot = finalParentResult.snapshot;
|
|
12816
|
+
}
|
|
12103
12817
|
return {
|
|
12104
12818
|
status: "ready",
|
|
12105
12819
|
readbacks: [
|
|
12106
12820
|
{
|
|
12107
12821
|
workspaceIdentity: options.workspaceIdentity,
|
|
12108
|
-
repositoryIdentity:
|
|
12109
|
-
worktreeIdentity:
|
|
12822
|
+
repositoryIdentity: effectiveSnapshot.repositoryRevisionDigest,
|
|
12823
|
+
worktreeIdentity: effectiveSnapshot.worktreeRevisionDigest,
|
|
12824
|
+
operationTargetIdentity: finalEffective.targetIdentity
|
|
12110
12825
|
},
|
|
12111
|
-
...remoteReadbacks.map(({ operation, snapshot }) => remoteReadbackInput(operation,
|
|
12826
|
+
...remoteReadbacks.map(({ operation, snapshot }) => remoteReadbackInput(operation, effectiveSnapshot, options.workspaceIdentity, snapshot))
|
|
12112
12827
|
]
|
|
12113
12828
|
};
|
|
12114
12829
|
}
|
|
12115
|
-
async function completionRemoteReadbacks() {
|
|
12830
|
+
async function completionRemoteReadbacks(observer) {
|
|
12116
12831
|
const remoteOperations = guard.status().satisfiedOperations.filter(remoteOperation);
|
|
12117
12832
|
if (remoteOperations.length === 0)
|
|
12118
12833
|
return [];
|
|
12119
|
-
const remoteSnapshot =
|
|
12834
|
+
const remoteSnapshot = observer.remoteSnapshot;
|
|
12120
12835
|
if (!remoteSnapshot)
|
|
12121
12836
|
return;
|
|
12122
12837
|
const readbacks = [];
|
|
@@ -12185,19 +12900,28 @@ function createSessionRuntime(options) {
|
|
|
12185
12900
|
}
|
|
12186
12901
|
return pending;
|
|
12187
12902
|
}
|
|
12188
|
-
async function captureAfterOperation() {
|
|
12903
|
+
async function captureAfterOperation(pending) {
|
|
12189
12904
|
if (!options.observer)
|
|
12190
12905
|
return;
|
|
12906
|
+
let validation;
|
|
12907
|
+
try {
|
|
12908
|
+
validation = options.observer.validateRegisteredWorktree(pending.targetRoot);
|
|
12909
|
+
} catch {
|
|
12910
|
+
return;
|
|
12911
|
+
}
|
|
12912
|
+
if (validation.status === "error" || validation.targetRoot !== pending.targetRoot || registeredWorktreeIdentity(validation) !== pending.registeredWorktreeIdentity) {
|
|
12913
|
+
return;
|
|
12914
|
+
}
|
|
12191
12915
|
let result;
|
|
12192
12916
|
try {
|
|
12193
|
-
result = await
|
|
12917
|
+
result = await pending.observer.snapshot();
|
|
12194
12918
|
} catch {
|
|
12195
12919
|
return;
|
|
12196
12920
|
}
|
|
12197
|
-
return result.status === "available" && result.snapshot.targetDigest ===
|
|
12921
|
+
return result.status === "available" && result.snapshot.targetDigest === pending.targetIdentity ? result.snapshot : undefined;
|
|
12198
12922
|
}
|
|
12199
|
-
async function captureRemoteAfter(operation) {
|
|
12200
|
-
const remoteSnapshot =
|
|
12923
|
+
async function captureRemoteAfter(operation, observer) {
|
|
12924
|
+
const remoteSnapshot = observer.remoteSnapshot;
|
|
12201
12925
|
if (!remoteSnapshot)
|
|
12202
12926
|
return;
|
|
12203
12927
|
let result;
|
|
@@ -12231,6 +12955,7 @@ function createSessionRuntime(options) {
|
|
|
12231
12955
|
workspaceIdentity: options.workspaceIdentity,
|
|
12232
12956
|
repositoryIdentity: pending.before.repositoryRevisionDigest,
|
|
12233
12957
|
worktreeIdentity: pending.before.worktreeRevisionDigest,
|
|
12958
|
+
...localOperation(operation) ? { operationTargetIdentity: pending.targetIdentity } : {},
|
|
12234
12959
|
...pending.remoteBefore?.status === "available" ? {
|
|
12235
12960
|
resourceIdentity: pending.remoteBefore.snapshot.resourceIdentity,
|
|
12236
12961
|
resourceRevisionIdentity: pending.remoteBefore.snapshot.resourceRevisionIdentity
|
|
@@ -12240,6 +12965,7 @@ function createSessionRuntime(options) {
|
|
|
12240
12965
|
workspaceIdentity: options.workspaceIdentity,
|
|
12241
12966
|
repositoryIdentity: after2.repositoryRevisionDigest,
|
|
12242
12967
|
worktreeIdentity: after2.worktreeRevisionDigest,
|
|
12968
|
+
...localOperation(operation) ? { operationTargetIdentity: pending.targetIdentity } : {},
|
|
12243
12969
|
commitClosure: after2.commitClosure,
|
|
12244
12970
|
...remoteAfter ? {
|
|
12245
12971
|
resourceIdentity: remoteAfter.resourceIdentity,
|
|
@@ -12292,7 +13018,7 @@ function createSessionRuntime(options) {
|
|
|
12292
13018
|
sealOperation(callDigest, pending, true);
|
|
12293
13019
|
return { status: "unavailable" };
|
|
12294
13020
|
}
|
|
12295
|
-
const afterSnapshot = await captureAfterOperation();
|
|
13021
|
+
const afterSnapshot = await captureAfterOperation(pending);
|
|
12296
13022
|
if (!afterSnapshot) {
|
|
12297
13023
|
sealOperation(callDigest, pending, true);
|
|
12298
13024
|
return { status: "unavailable" };
|
|
@@ -12309,7 +13035,7 @@ function createSessionRuntime(options) {
|
|
|
12309
13035
|
}
|
|
12310
13036
|
let finalObservation = classification.observation;
|
|
12311
13037
|
if (remoteOperation(finalObservation.operation)) {
|
|
12312
|
-
const remoteAfter = await captureRemoteAfter(finalObservation.operation);
|
|
13038
|
+
const remoteAfter = await captureRemoteAfter(finalObservation.operation, pending.observer);
|
|
12313
13039
|
if (!remoteAfter) {
|
|
12314
13040
|
sealOperation(callDigest, pending, true);
|
|
12315
13041
|
return { status: "unavailable" };
|
|
@@ -12346,7 +13072,11 @@ function createSessionRuntime(options) {
|
|
|
12346
13072
|
}
|
|
12347
13073
|
if (observed.status !== "accepted")
|
|
12348
13074
|
return { status: "ignored" };
|
|
12349
|
-
return {
|
|
13075
|
+
return {
|
|
13076
|
+
status: "accepted",
|
|
13077
|
+
operation: observed.operation,
|
|
13078
|
+
after: afterSnapshot
|
|
13079
|
+
};
|
|
12350
13080
|
}
|
|
12351
13081
|
function receiptForOperation(callID, operation) {
|
|
12352
13082
|
const callDigest = digestCall(ledger, callID);
|
|
@@ -12390,8 +13120,16 @@ function createSessionRuntime(options) {
|
|
|
12390
13120
|
}
|
|
12391
13121
|
if (result.status === "accepted") {
|
|
12392
13122
|
const receipt = receiptForOperation(host.callID, result.operation);
|
|
12393
|
-
if (receipt)
|
|
13123
|
+
if (receipt) {
|
|
13124
|
+
if (localOperation(result.operation)) {
|
|
13125
|
+
recoveredOperationContexts.set(receipt.canonical.receiptId, {
|
|
13126
|
+
targetIdentity: pending.targetIdentity,
|
|
13127
|
+
before: pending.before,
|
|
13128
|
+
after: result.after
|
|
13129
|
+
});
|
|
13130
|
+
}
|
|
12394
13131
|
mergeReceiptMarker(output, receipt);
|
|
13132
|
+
}
|
|
12395
13133
|
}
|
|
12396
13134
|
}
|
|
12397
13135
|
async function after(input, output) {
|
|
@@ -12557,6 +13295,8 @@ function makeWorkflowTool(description, args2, getRuntime, execute) {
|
|
|
12557
13295
|
}
|
|
12558
13296
|
function createOpencodeWorkflowGuard(options) {
|
|
12559
13297
|
const sessions = new Map;
|
|
13298
|
+
const operationObservers = new Map;
|
|
13299
|
+
const recoveredOperationContexts = new Map;
|
|
12560
13300
|
function sessionRuntimeFor(sessionID) {
|
|
12561
13301
|
const existing = sessions.get(sessionID);
|
|
12562
13302
|
if (existing)
|
|
@@ -12565,7 +13305,7 @@ function createOpencodeWorkflowGuard(options) {
|
|
|
12565
13305
|
...options,
|
|
12566
13306
|
registrationIdentity: options.registrationIdentity,
|
|
12567
13307
|
sessionSalt: options.sessionSalt
|
|
12568
|
-
});
|
|
13308
|
+
}, operationObservers, recoveredOperationContexts);
|
|
12569
13309
|
sessions.set(sessionID, runtime);
|
|
12570
13310
|
return runtime;
|
|
12571
13311
|
}
|
|
@@ -12694,8 +13434,8 @@ function createOpencodeWorkflowGuard(options) {
|
|
|
12694
13434
|
}
|
|
12695
13435
|
|
|
12696
13436
|
// src/lib/skill-resolver.ts
|
|
12697
|
-
import
|
|
12698
|
-
import
|
|
13437
|
+
import fs11 from "fs";
|
|
13438
|
+
import path10 from "path";
|
|
12699
13439
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
12700
13440
|
function getAllSkills(options) {
|
|
12701
13441
|
const { bundledSkillsDir, disabledSkills } = options;
|
|
@@ -12731,7 +13471,7 @@ function buildSkillToolParameterHint(options) {
|
|
|
12731
13471
|
}
|
|
12732
13472
|
function buildSkillContentOutput(matchedSkill) {
|
|
12733
13473
|
const body2 = extractSkillBody(matchedSkill.wrappedTemplate);
|
|
12734
|
-
const dir =
|
|
13474
|
+
const dir = path10.dirname(matchedSkill.skillFile);
|
|
12735
13475
|
const base = pathToFileURL2(dir).href;
|
|
12736
13476
|
const files = discoverSkillFiles(dir);
|
|
12737
13477
|
const lines = [
|
|
@@ -12762,17 +13502,17 @@ function discoverSkillFiles(dir, limit = 10) {
|
|
|
12762
13502
|
function handleEntry(entry, currentDir) {
|
|
12763
13503
|
if (entry.isDirectory()) {
|
|
12764
13504
|
if (!shouldSkipDirectory(entry.name)) {
|
|
12765
|
-
recurse(
|
|
13505
|
+
recurse(path10.resolve(currentDir, entry.name));
|
|
12766
13506
|
}
|
|
12767
13507
|
} else if (shouldIncludeFile(entry.name)) {
|
|
12768
|
-
files.push(
|
|
13508
|
+
files.push(path10.resolve(currentDir, entry.name));
|
|
12769
13509
|
}
|
|
12770
13510
|
}
|
|
12771
13511
|
function recurse(currentDir) {
|
|
12772
13512
|
if (files.length >= limit)
|
|
12773
13513
|
return;
|
|
12774
13514
|
try {
|
|
12775
|
-
const entries =
|
|
13515
|
+
const entries = fs11.readdirSync(currentDir, { withFileTypes: true });
|
|
12776
13516
|
for (const entry of entries) {
|
|
12777
13517
|
if (files.length >= limit)
|
|
12778
13518
|
break;
|
|
@@ -12829,19 +13569,19 @@ function createSkillTool(options) {
|
|
|
12829
13569
|
}
|
|
12830
13570
|
|
|
12831
13571
|
// src/index.ts
|
|
12832
|
-
var __dirname3 =
|
|
12833
|
-
var packageRoot2 =
|
|
12834
|
-
var bundledSkillsDir =
|
|
12835
|
-
var bundledAgentsDir2 =
|
|
12836
|
-
var bundledCommandsDir =
|
|
12837
|
-
var packageJsonPath =
|
|
12838
|
-
var canonicalPackageSource = pathToFileURL3(
|
|
13572
|
+
var __dirname3 = path11.dirname(fileURLToPath3(import.meta.url));
|
|
13573
|
+
var packageRoot2 = path11.resolve(__dirname3, "..");
|
|
13574
|
+
var bundledSkillsDir = path11.join(packageRoot2, "skills");
|
|
13575
|
+
var bundledAgentsDir2 = path11.join(packageRoot2, "agents");
|
|
13576
|
+
var bundledCommandsDir = path11.join(packageRoot2, "commands");
|
|
13577
|
+
var packageJsonPath = path11.join(packageRoot2, "package.json");
|
|
13578
|
+
var canonicalPackageSource = pathToFileURL3(fs12.realpathSync(packageRoot2)).href;
|
|
12839
13579
|
var registrationSourceIdentity = createHash5("sha256").update(`systematic/opencode-registration-source/v1/${canonicalPackageSource}`).digest("hex");
|
|
12840
13580
|
var getPackageVersion = () => {
|
|
12841
13581
|
try {
|
|
12842
|
-
if (!
|
|
13582
|
+
if (!fs12.existsSync(packageJsonPath))
|
|
12843
13583
|
return "unknown";
|
|
12844
|
-
const content =
|
|
13584
|
+
const content = fs12.readFileSync(packageJsonPath, "utf8");
|
|
12845
13585
|
const parsed = JSON.parse(content);
|
|
12846
13586
|
return parsed.version ?? "unknown";
|
|
12847
13587
|
} catch {
|
|
@@ -12889,6 +13629,8 @@ var initializePlugin = async ({
|
|
|
12889
13629
|
repositoryIdentity: initialIdentities.repositoryRevisionDigest,
|
|
12890
13630
|
worktreeIdentity: initialIdentities.worktreeRevisionDigest,
|
|
12891
13631
|
registrationIdentity: registrationSourceIdentity,
|
|
13632
|
+
targetDirectory: typeof worktree === "string" ? worktree : directory,
|
|
13633
|
+
sessionLocation: directory,
|
|
12892
13634
|
observer,
|
|
12893
13635
|
classifier: createReceiptClassifier(),
|
|
12894
13636
|
hostReadback: (() => {
|