agentwheel 0.20.8 → 0.20.9

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.
@@ -7,9 +7,9 @@ import {
7
7
  } from "./chunk-7VPI5J5Y.js";
8
8
 
9
9
  // src/install/transaction.ts
10
- import { createHash as createHash5 } from "crypto";
11
- import { cp, mkdir as mkdir3, readFile as readFile7, rm as rm4, stat as stat2 } from "fs/promises";
12
- import { dirname as dirname5, join as join6, resolve as resolve9 } from "path";
10
+ import { createHash as createHash6 } from "crypto";
11
+ import { cp, mkdir as mkdir3, readFile as readFile8, rm as rm4, stat as stat2 } from "fs/promises";
12
+ import { dirname as dirname5, join as join7, resolve as resolve10 } from "path";
13
13
 
14
14
  // src/transport/local.ts
15
15
  import { execFile } from "child_process";
@@ -151,13 +151,13 @@ async function spawnWithInput(command, args, input) {
151
151
  if (result.stderr) throw new Error(result.stderr);
152
152
  }
153
153
  function waitForProcess(child, label) {
154
- return new Promise((resolve10, reject) => {
154
+ return new Promise((resolve11, reject) => {
155
155
  const stderr = [];
156
156
  child.stderr?.on("data", (chunk) => stderr.push(chunk));
157
157
  child.on("error", reject);
158
158
  child.on("close", (code) => {
159
159
  const message = Buffer.concat(stderr).toString("utf8");
160
- if (code === 0) resolve10({ stderr: "" });
160
+ if (code === 0) resolve11({ stderr: "" });
161
161
  else reject(new Error(`${label} exited ${code}${message ? `: ${message}` : ""}`));
162
162
  });
163
163
  });
@@ -488,8 +488,8 @@ function sanitizeStateKey(value) {
488
488
 
489
489
  // src/mutation/coordinator.ts
490
490
  import { randomUUID as randomUUID2 } from "crypto";
491
- import { homedir as homedir4 } from "os";
492
- import { resolve as resolve8 } from "path";
491
+ import { homedir as homedir5 } from "os";
492
+ import { isAbsolute as isAbsolute6, relative as relative3, resolve as resolve9, sep as sep3 } from "path";
493
493
 
494
494
  // src/model/mutation.ts
495
495
  import { z as z3 } from "zod";
@@ -548,10 +548,10 @@ var mutationPolicySchema = z3.object({
548
548
  });
549
549
 
550
550
  // src/model/workspace.ts
551
- import { readFile as readFile4 } from "fs/promises";
552
- import { homedir as homedir3 } from "os";
553
- import { dirname as dirname4, join as join4, resolve as resolve5 } from "path";
554
- import { z as z6 } from "zod";
551
+ import { readFile as readFile5 } from "fs/promises";
552
+ import { homedir as homedir4 } from "os";
553
+ import { dirname as dirname4, join as join5, resolve as resolve6 } from "path";
554
+ import { z as z7 } from "zod";
555
555
 
556
556
  // src/resolve/semver.ts
557
557
  var semverPattern = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -651,7 +651,7 @@ function compareSemver(a, b) {
651
651
 
652
652
  // src/mutation/declarations.ts
653
653
  import { chmodSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, writeSync } from "fs";
654
- import { isAbsolute as isAbsolute3, join as join3, relative, resolve as resolve4, sep } from "path";
654
+ import { isAbsolute as isAbsolute4, join as join4, relative, resolve as resolve5, sep } from "path";
655
655
 
656
656
  // src/mutation/protocol.ts
657
657
  import { createHash } from "crypto";
@@ -808,16 +808,280 @@ function canonicalJson(value) {
808
808
  }
809
809
 
810
810
  // src/mutation/receipts.ts
811
- import { createHash as createHash2, randomUUID } from "crypto";
812
- import { chmod, link, mkdir as mkdir2, open, readFile as readFile3, readdir as readdir2, rename as rename2, rm as rm2 } from "fs/promises";
811
+ import { createHash as createHash3, randomUUID } from "crypto";
812
+ import { chmod, link, mkdir as mkdir2, open, readFile as readFile4, readdir as readdir2, rename as rename2, rm as rm2 } from "fs/promises";
813
+ import { homedir as homedir3 } from "os";
814
+ import { dirname as dirname3, join as join3, resolve as resolve4 } from "path";
815
+ import { z as z6 } from "zod";
816
+
817
+ // src/mutation/session-ownership.ts
818
+ import { createHash as createHash2 } from "crypto";
819
+ import { readFile as readFile3 } from "fs/promises";
813
820
  import { homedir as homedir2 } from "os";
814
- import { dirname as dirname3, join as join2, resolve as resolve3 } from "path";
821
+ import { isAbsolute as isAbsolute3, join as join2, posix as posix2, resolve as resolve3 } from "path";
815
822
  import { z as z5 } from "zod";
816
- var preexistingPathSchema = z5.object({
817
- path: z5.string().min(1).max(4096),
818
- sha256: z5.string().regex(/^[a-f0-9]{64}$/).nullable()
823
+
824
+ // src/mutation/process.ts
825
+ import { spawn as spawn2 } from "child_process";
826
+ async function runProcess(command, args, options = {}) {
827
+ return new Promise((resolve11, reject) => {
828
+ const useProcessGroup = options.killProcessGroup === true && process.platform !== "win32";
829
+ const child = spawn2(command, args, {
830
+ cwd: options.cwd,
831
+ env: options.env,
832
+ stdio: options.inheritedFileDescriptor === void 0 ? ["pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe", options.inheritedFileDescriptor],
833
+ detached: useProcessGroup,
834
+ windowsHide: true
835
+ });
836
+ const stdout = [];
837
+ const stderr = [];
838
+ let stdoutBytes = 0;
839
+ let stderrBytes = 0;
840
+ let outputExceeded = false;
841
+ let timedOut = false;
842
+ const terminate = () => {
843
+ if (useProcessGroup && child.pid !== void 0) {
844
+ try {
845
+ process.kill(-child.pid, "SIGKILL");
846
+ return;
847
+ } catch {
848
+ }
849
+ }
850
+ child.kill("SIGKILL");
851
+ };
852
+ const timeout = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
853
+ timedOut = true;
854
+ terminate();
855
+ }, options.timeoutMs);
856
+ const collect = (target, stream, chunk) => {
857
+ if (outputExceeded) return;
858
+ if (stream === "stdout") stdoutBytes += chunk.length;
859
+ else stderrBytes += chunk.length;
860
+ if (options.maxOutputBytes !== void 0 && (stdoutBytes > options.maxOutputBytes || stderrBytes > options.maxOutputBytes)) {
861
+ outputExceeded = true;
862
+ terminate();
863
+ return;
864
+ }
865
+ target.push(chunk);
866
+ };
867
+ child.stdout.on("data", (chunk) => collect(stdout, "stdout", chunk));
868
+ child.stderr.on("data", (chunk) => collect(stderr, "stderr", chunk));
869
+ child.on("error", reject);
870
+ child.on("close", (code) => {
871
+ if (timeout) clearTimeout(timeout);
872
+ const exitCode = code ?? 1;
873
+ const result = { stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), exitCode };
874
+ if (timedOut) {
875
+ reject(new Error(`${command} exceeded the ${options.timeoutMs}ms execution timeout.`));
876
+ return;
877
+ }
878
+ if (outputExceeded) {
879
+ reject(new Error(`${command} exceeded the provider output limit.`));
880
+ return;
881
+ }
882
+ if (!(options.allowExitCodes ?? [0]).includes(exitCode)) {
883
+ const detail = options.includeFailureOutput === false ? "" : result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim();
884
+ reject(new Error(`${command} exited ${exitCode}${detail ? `: ${detail}` : ""}`));
885
+ return;
886
+ }
887
+ resolve11(result);
888
+ });
889
+ if (options.input !== void 0) child.stdin.end(options.input);
890
+ else child.stdin.end();
891
+ });
892
+ }
893
+
894
+ // src/mutation/session-ownership.ts
895
+ var safeSingleLineSchema = z5.string().max(4096).refine(
896
+ (value) => !/[\r\n\u0000-\u001f\u007f]/u.test(value),
897
+ { message: "must be a single printable line" }
898
+ );
899
+ var opaqueRefSchema = z5.string().regex(/^[a-z][a-z0-9-]{0,63}:[A-Za-z0-9][A-Za-z0-9._:/-]{0,255}$/);
900
+ var graphNodeStatusSchema = z5.enum(["active", "waiting", "blocked", "quiet", "closed"]);
901
+ var graphNodeSchema = z5.object({
902
+ id: safeSingleLineSchema.pipe(z5.string().min(1)),
903
+ agent: safeSingleLineSchema.pipe(z5.string().min(1)),
904
+ tmuxTarget: safeSingleLineSchema,
905
+ roleProfile: safeSingleLineSchema.optional().default(""),
906
+ title: safeSingleLineSchema.optional().default(""),
907
+ summary: safeSingleLineSchema.optional().default(""),
908
+ status: graphNodeStatusSchema,
909
+ domains: z5.array(safeSingleLineSchema).optional().default([]),
910
+ runtimeUuid: z5.string().uuid().nullable().optional().default(null),
911
+ refs: z5.array(opaqueRefSchema).optional().default([]),
912
+ createdAt: z5.string().datetime(),
913
+ lastSeenAt: z5.string().datetime().optional()
914
+ }).passthrough();
915
+ var sessionGraphSchema = z5.object({
916
+ schema: z5.literal("agent-mesh.session-graph.v1"),
917
+ generatedAt: z5.string().datetime(),
918
+ nodes: z5.array(graphNodeSchema)
919
+ }).passthrough();
920
+ var liveStatuses = /* @__PURE__ */ new Set(["active", "waiting", "blocked"]);
921
+ var runtimeUuidSchema = z5.string().uuid();
922
+ function agentMeshGraphPath() {
923
+ if (process.env.AGENT_MESH_GRAPH_PATH) return resolve3(process.env.AGENT_MESH_GRAPH_PATH);
924
+ const stateRoot = process.env.XDG_STATE_HOME ? resolve3(process.env.XDG_STATE_HOME) : join2(homedir2(), ".local", "state");
925
+ return join2(stateRoot, "agent-mesh", "graph", "graph.json");
926
+ }
927
+ function agentwheelResourceRef(gitCommonDir, repositoryRelativePath) {
928
+ const commonDir = resolve3(gitCommonDir);
929
+ const normalizedPath = normalizeRepositoryRelativePath(repositoryRelativePath);
930
+ const digest = createHash2("sha256").update(commonDir).update("\0").update(normalizedPath).digest("hex");
931
+ return `agentwheel-resource:${digest}`;
932
+ }
933
+ async function resourceRefsForRepositoryPaths(repositoryRoot, paths) {
934
+ const result = await runProcess("git", [
935
+ "-C",
936
+ repositoryRoot,
937
+ "rev-parse",
938
+ "--path-format=absolute",
939
+ "--git-common-dir"
940
+ ]);
941
+ const gitCommonDir = result.stdout.toString("utf8").trim();
942
+ if (!gitCommonDir || !isAbsolute3(gitCommonDir)) {
943
+ throw new Error("Git did not return an absolute common directory for ownership diagnostics.");
944
+ }
945
+ return new Map([...paths].map((path) => [path, agentwheelResourceRef(gitCommonDir, path)]));
946
+ }
947
+ async function lookupResourceOwners(refs) {
948
+ const requested = [...new Set(refs)];
949
+ const graph = await readSessionGraph();
950
+ if (!graph.ok) {
951
+ return new Map(requested.map((ref) => [ref, { kind: "unknown", reason: graph.reason }]));
952
+ }
953
+ return new Map(requested.map((ref) => [ref, ownershipForNodes(
954
+ graph.nodes.filter((node) => node.refs.includes(ref))
955
+ )]));
956
+ }
957
+ async function lookupRuntimeOwner(runtimeUuid) {
958
+ const graph = await readSessionGraph();
959
+ if (!graph.ok) return { kind: "unknown", reason: graph.reason };
960
+ const normalized = runtimeUuid.toLowerCase();
961
+ return ownershipForNodes(graph.nodes.filter((node) => node.runtimeUuid?.toLowerCase() === normalized));
962
+ }
963
+ async function describeDirtyPathOwnershipByPath(repositoryRoot, paths) {
964
+ let refs;
965
+ try {
966
+ refs = await resourceRefsForRepositoryPaths(repositoryRoot, paths);
967
+ } catch {
968
+ return new Map(paths.map((path) => [path, `${path}: ${unknownNextAction("resource identity could not be computed")}`]));
969
+ }
970
+ const ownership = await lookupResourceOwners(refs.values());
971
+ return new Map(paths.map((path) => {
972
+ const match = ownership.get(refs.get(path));
973
+ if (match?.kind === "unique") {
974
+ return [path, `${path}: belongs to ${describeSession(match.owner)}. Wait for that session's handoff or ask the rollout coordinator before retrying.`];
975
+ }
976
+ if (match?.kind === "ambiguous") {
977
+ return [path, `${path}: owner unknown because multiple live sessions claim this resource. Candidates: ${match.candidates.map(describeSession).join("; ")}. Ask the rollout coordinator to disambiguate the candidates before retrying; do not remove the safety block.`];
978
+ }
979
+ return [path, `${path}: ${unknownNextAction(reasonText(match?.kind === "unknown" ? match.reason : "no-live-match"))}`];
980
+ }));
981
+ }
982
+ async function describeMutationLockOwner(owner) {
983
+ if (!owner) {
984
+ return unknownNextAction("the lock owner metadata is missing or malformed");
985
+ }
986
+ const facts = [
987
+ owner.operationId ? `operation ${owner.operationId}` : void 0,
988
+ owner.pid !== void 0 ? `pid ${owner.pid}` : void 0,
989
+ owner.runtimeUuid ? `runtime UUID ${owner.runtimeUuid}` : void 0,
990
+ owner.createdAt ? `lock created at ${owner.createdAt}` : void 0
991
+ ].filter(Boolean).join(", ");
992
+ if (!owner.runtimeUuid) {
993
+ return `Lock owner facts: ${facts || "none"}. ${unknownNextAction("no runtime UUID was recorded")}`;
994
+ }
995
+ const match = await lookupRuntimeOwner(owner.runtimeUuid);
996
+ if (match.kind === "unique") {
997
+ return `Lock owner facts: ${facts}. Correlated to ${describeSession(match.owner)}. Wait for that session's handoff or ask the rollout coordinator before retrying.`;
998
+ }
999
+ if (match.kind === "ambiguous") {
1000
+ return `Lock owner facts: ${facts}. Owner unknown because the runtime UUID matches multiple live sessions. Candidates: ${match.candidates.map(describeSession).join("; ")}. Ask the rollout coordinator to disambiguate before retrying; do not remove the lock.`;
1001
+ }
1002
+ return `Lock owner facts: ${facts}. ${unknownNextAction(reasonText(match.reason))}`;
1003
+ }
1004
+ function runtimeUuidForCurrentProcess() {
1005
+ for (const name of ["AGENT_MESH_RUNTIME_UUID", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "CLAUDE_CODE_SESSION_ID"]) {
1006
+ const value = process.env[name]?.trim();
1007
+ if (value && runtimeUuidSchema.safeParse(value).success) return value.toLowerCase();
1008
+ }
1009
+ return void 0;
1010
+ }
1011
+ function ownershipForNodes(nodes) {
1012
+ const live = nodes.filter((node) => liveStatuses.has(node.status)).map(sessionFacts);
1013
+ if (live.length === 1) return { kind: "unique", owner: live[0] };
1014
+ if (live.length > 1) return { kind: "ambiguous", candidates: live.sort((a, b) => a.sessionId.localeCompare(b.sessionId)) };
1015
+ if (nodes.length > 0) return { kind: "unknown", reason: "only-inactive-matches" };
1016
+ return { kind: "unknown", reason: "no-live-match" };
1017
+ }
1018
+ async function readSessionGraph() {
1019
+ let raw;
1020
+ try {
1021
+ raw = await readFile3(agentMeshGraphPath(), "utf8");
1022
+ } catch (error) {
1023
+ if (isMissing(error)) return { ok: false, reason: "missing-graph" };
1024
+ return { ok: false, reason: "malformed-graph" };
1025
+ }
1026
+ try {
1027
+ const graph = sessionGraphSchema.parse(JSON.parse(raw));
1028
+ return { ok: true, nodes: graph.nodes };
1029
+ } catch {
1030
+ return { ok: false, reason: "malformed-graph" };
1031
+ }
1032
+ }
1033
+ function sessionFacts(node) {
1034
+ return {
1035
+ nodeId: node.id,
1036
+ sessionId: node.tmuxTarget || node.runtimeUuid || node.id,
1037
+ agent: node.agent,
1038
+ runtimeUuid: node.runtimeUuid,
1039
+ status: node.status,
1040
+ domains: [...node.domains],
1041
+ createdAt: node.createdAt,
1042
+ summary: node.summary,
1043
+ title: node.title
1044
+ };
1045
+ }
1046
+ function describeSession(owner) {
1047
+ const context = owner.summary || owner.title || owner.domains.join(", ");
1048
+ return `session ${owner.sessionId}, ${owner.status} since ${owner.createdAt}${context ? ` on ${context}` : ""}`;
1049
+ }
1050
+ function unknownNextAction(reason) {
1051
+ return `Owner unknown because ${reason}. Inspect the Agent Mesh session graph or ask the rollout coordinator to identify or clear the owner before retrying; do not remove the safety block based on this diagnostic.`;
1052
+ }
1053
+ function reasonText(reason) {
1054
+ switch (reason) {
1055
+ case "missing-graph":
1056
+ return "the Agent Mesh graph projection is missing";
1057
+ case "malformed-graph":
1058
+ return "the Agent Mesh graph projection is malformed";
1059
+ case "only-inactive-matches":
1060
+ return "only quiet or closed session claims were found";
1061
+ case "no-live-match":
1062
+ return "no live session claim matches this resource";
1063
+ }
1064
+ }
1065
+ function normalizeRepositoryRelativePath(path) {
1066
+ if (!path || isAbsolute3(path) || path.includes("\\") || path.includes("\0")) {
1067
+ throw new Error("Ownership paths must be normalized repository-relative paths.");
1068
+ }
1069
+ const normalized = posix2.normalize(path);
1070
+ if (normalized !== path || path.split("/").some((part) => part === "" || part === "." || part === "..")) {
1071
+ throw new Error("Ownership paths must be normalized repository-relative paths.");
1072
+ }
1073
+ return normalized;
1074
+ }
1075
+ function isMissing(error) {
1076
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1077
+ }
1078
+
1079
+ // src/mutation/receipts.ts
1080
+ var preexistingPathSchema = z6.object({
1081
+ path: z6.string().min(1).max(4096),
1082
+ sha256: z6.string().regex(/^[a-f0-9]{64}$/).nullable()
819
1083
  }).strict();
820
- var receiptStatusSchema = z5.enum([
1084
+ var receiptStatusSchema = z6.enum([
821
1085
  "prepared",
822
1086
  "handler-succeeded",
823
1087
  "mutation-applied",
@@ -830,35 +1094,35 @@ var receiptStatusSchema = z5.enum([
830
1094
  "postcheck-failed",
831
1095
  "failed"
832
1096
  ]);
833
- var runtimeJournalSchema = z5.object({
834
- path: z5.string().min(1).max(4096),
835
- status: z5.enum(["reserved", "pending", "resolving", "resolved"]),
836
- transport: z5.enum(["local", "ssh"]),
837
- transportDescription: z5.string().min(1).max(1024),
838
- journalDigest: z5.string().regex(/^[a-f0-9]{64}$/)
1097
+ var runtimeJournalSchema = z6.object({
1098
+ path: z6.string().min(1).max(4096),
1099
+ status: z6.enum(["reserved", "pending", "resolving", "resolved"]),
1100
+ transport: z6.enum(["local", "ssh"]),
1101
+ transportDescription: z6.string().min(1).max(1024),
1102
+ journalDigest: z6.string().regex(/^[a-f0-9]{64}$/)
839
1103
  }).strict();
840
- var mutationReceiptBaseSchema = z5.object({
841
- version: z5.literal(1),
842
- revision: z5.number().int().min(1),
843
- receiptDigest: z5.string().regex(/^[a-f0-9]{64}$/),
1104
+ var mutationReceiptBaseSchema = z6.object({
1105
+ version: z6.literal(1),
1106
+ revision: z6.number().int().min(1),
1107
+ receiptDigest: z6.string().regex(/^[a-f0-9]{64}$/),
844
1108
  operationId: mutationOperationIdSchema,
845
- commandName: z5.string().min(1).max(256),
846
- reason: z5.string().min(1).max(4096),
847
- noCommit: z5.boolean(),
848
- workspaceRoot: z5.string().min(1),
849
- repositoryRoot: z5.string().min(1).nullable(),
850
- expectedHead: z5.string().min(1).max(256).nullable(),
851
- expectedManifestDigest: z5.string().regex(/^[a-f0-9]{64}$/).nullable(),
852
- revisionMode: z5.enum(["off", "commit-after-verify"]),
1109
+ commandName: z6.string().min(1).max(256),
1110
+ reason: z6.string().min(1).max(4096),
1111
+ noCommit: z6.boolean(),
1112
+ workspaceRoot: z6.string().min(1),
1113
+ repositoryRoot: z6.string().min(1).nullable(),
1114
+ expectedHead: z6.string().min(1).max(256).nullable(),
1115
+ expectedManifestDigest: z6.string().regex(/^[a-f0-9]{64}$/).nullable(),
1116
+ revisionMode: z6.enum(["off", "commit-after-verify"]),
853
1117
  provider: revisionProviderConfigSchema.nullable(),
854
- preexistingPaths: z5.array(preexistingPathSchema),
855
- paths: z5.array(revisionPathSchema),
856
- runtimeJournals: z5.array(runtimeJournalSchema).default([]),
1118
+ preexistingPaths: z6.array(preexistingPathSchema),
1119
+ paths: z6.array(revisionPathSchema),
1120
+ runtimeJournals: z6.array(runtimeJournalSchema).default([]),
857
1121
  status: receiptStatusSchema,
858
- createdAt: z5.string().datetime(),
859
- updatedAt: z5.string().datetime(),
1122
+ createdAt: z6.string().datetime(),
1123
+ updatedAt: z6.string().datetime(),
860
1124
  providerResponse: revisionProviderResultSchema.optional(),
861
- error: z5.string().min(1).max(4096).optional()
1125
+ error: z6.string().min(1).max(4096).optional()
862
1126
  }).strict();
863
1127
  var mutationReceiptSchema = mutationReceiptBaseSchema.superRefine((receipt, ctx) => {
864
1128
  const expected = mutationReceiptDigest(receipt);
@@ -870,8 +1134,18 @@ var mutationReceiptSchema = mutationReceiptBaseSchema.superRefine((receipt, ctx)
870
1134
  });
871
1135
  }
872
1136
  });
1137
+ var MutationLockContentionError = class extends Error {
1138
+ constructor(lockPath, owner, diagnostic) {
1139
+ super(`Another Agentwheel mutation owns the repository lock at ${lockPath}. ${diagnostic}`);
1140
+ this.lockPath = lockPath;
1141
+ this.owner = owner;
1142
+ this.name = "MutationLockContentionError";
1143
+ }
1144
+ lockPath;
1145
+ owner;
1146
+ };
873
1147
  function mutationStateRoot() {
874
- return resolve3(process.env.AGENTWHEEL_MUTATION_STATE_ROOT ?? join2(homedir2(), ".agentwheel", "mutations"));
1148
+ return resolve4(process.env.AGENTWHEEL_MUTATION_STATE_ROOT ?? join3(homedir3(), ".agentwheel", "mutations"));
875
1149
  }
876
1150
  async function createMutationReceipt(receipt) {
877
1151
  const path = receiptPath(receipt.operationId);
@@ -932,48 +1206,50 @@ async function readMutationReceipt(operationId) {
932
1206
  const id = mutationOperationIdSchema.parse(operationId);
933
1207
  const path = receiptPath(id);
934
1208
  try {
935
- return mutationReceiptSchema.parse(JSON.parse(await readFile3(path, "utf8")));
1209
+ return mutationReceiptSchema.parse(JSON.parse(await readFile4(path, "utf8")));
936
1210
  } catch (error) {
937
- if (isMissing(error)) throw new Error(`Unknown mutation operation '${id}'.`);
1211
+ if (isMissing2(error)) throw new Error(`Unknown mutation operation '${id}'.`);
938
1212
  throw error;
939
1213
  }
940
1214
  }
941
1215
  async function listMutationReceipts() {
942
- const root = join2(mutationStateRoot(), "receipts");
1216
+ const root = join3(mutationStateRoot(), "receipts");
943
1217
  let names;
944
1218
  try {
945
1219
  names = await readdir2(root);
946
1220
  } catch (error) {
947
- if (isMissing(error)) return [];
1221
+ if (isMissing2(error)) return [];
948
1222
  throw error;
949
1223
  }
950
1224
  const receipts = [];
951
1225
  for (const name of names.filter((entry) => entry.endsWith(".json")).sort()) {
952
- receipts.push(mutationReceiptSchema.parse(JSON.parse(await readFile3(join2(root, name), "utf8"))));
1226
+ receipts.push(mutationReceiptSchema.parse(JSON.parse(await readFile4(join3(root, name), "utf8"))));
953
1227
  }
954
1228
  return receipts.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
955
1229
  }
956
1230
  async function acquireMutationLock(repositoryRoot, operationId) {
957
1231
  const root = mutationStateRoot();
958
- const digest = createHash2("sha256").update(resolve3(repositoryRoot)).digest("hex");
959
- const lockPath = join2(root, "locks", `${digest}.lock`);
960
- await mkdir2(join2(root, "locks"), { recursive: true, mode: 448 });
961
- await chmod(join2(root, "locks"), 448);
1232
+ const digest = createHash3("sha256").update(resolve4(repositoryRoot)).digest("hex");
1233
+ const lockPath = join3(root, "locks", `${digest}.lock`);
1234
+ await mkdir2(join3(root, "locks"), { recursive: true, mode: 448 });
1235
+ await chmod(join3(root, "locks"), 448);
962
1236
  try {
963
1237
  await mkdir2(lockPath, { mode: 448 });
964
1238
  } catch (error) {
965
1239
  if (!isAlreadyExists(error)) throw error;
966
1240
  if (!await reapStaleLock(lockPath)) {
967
- throw new Error(`Another Agentwheel mutation owns the repository lock at ${lockPath}.`);
1241
+ const owner = await readMutationLockOwner(lockPath);
1242
+ throw new MutationLockContentionError(lockPath, owner, await describeMutationLockOwner(owner));
968
1243
  }
969
1244
  await mkdir2(lockPath, { mode: 448 });
970
1245
  }
971
- const ownerPath = join2(lockPath, "owner.json");
1246
+ const ownerPath = join3(lockPath, "owner.json");
972
1247
  await writeSecureJson(ownerPath, {
973
1248
  version: 1,
974
1249
  operationId,
975
1250
  pid: process.pid,
976
- repositoryRoot: resolve3(repositoryRoot),
1251
+ runtimeUuid: runtimeUuidForCurrentProcess(),
1252
+ repositoryRoot: resolve4(repositoryRoot),
977
1253
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
978
1254
  });
979
1255
  let released = false;
@@ -986,14 +1262,33 @@ async function acquireMutationLock(repositoryRoot, operationId) {
986
1262
  }
987
1263
  };
988
1264
  }
1265
+ async function readMutationLockOwner(lockPath) {
1266
+ try {
1267
+ const parsed = JSON.parse(await readFile4(join3(lockPath, "owner.json"), "utf8"));
1268
+ const owner = {};
1269
+ if (typeof parsed.operationId === "string" && /^[a-z0-9][a-z0-9_-]{0,62}$/i.test(parsed.operationId)) {
1270
+ owner.operationId = parsed.operationId;
1271
+ }
1272
+ if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0) owner.pid = parsed.pid;
1273
+ if (typeof parsed.runtimeUuid === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(parsed.runtimeUuid)) {
1274
+ owner.runtimeUuid = parsed.runtimeUuid.toLowerCase();
1275
+ }
1276
+ if (typeof parsed.createdAt === "string" && !Number.isNaN(Date.parse(parsed.createdAt))) {
1277
+ owner.createdAt = parsed.createdAt;
1278
+ }
1279
+ return Object.keys(owner).length > 0 ? owner : void 0;
1280
+ } catch {
1281
+ return void 0;
1282
+ }
1283
+ }
989
1284
  function receiptPath(operationId) {
990
- return join2(mutationStateRoot(), "receipts", `${mutationOperationIdSchema.parse(operationId)}.json`);
1285
+ return join3(mutationStateRoot(), "receipts", `${mutationOperationIdSchema.parse(operationId)}.json`);
991
1286
  }
992
1287
  async function ensureStateDirectory() {
993
1288
  const root = mutationStateRoot();
994
- await mkdir2(join2(root, "receipts"), { recursive: true, mode: 448 });
1289
+ await mkdir2(join3(root, "receipts"), { recursive: true, mode: 448 });
995
1290
  await chmod(root, 448);
996
- await chmod(join2(root, "receipts"), 448);
1291
+ await chmod(join3(root, "receipts"), 448);
997
1292
  }
998
1293
  async function writeReceipt(path, receipt) {
999
1294
  await writeSecureJson(path, receipt);
@@ -1004,7 +1299,7 @@ function sealMutationReceipt(value) {
1004
1299
  }
1005
1300
  function mutationReceiptDigest(receipt) {
1006
1301
  const { receiptDigest: _ignored, ...payload } = receipt;
1007
- return createHash2("sha256").update(canonicalJson2(payload)).digest("hex");
1302
+ return createHash3("sha256").update(canonicalJson2(payload)).digest("hex");
1008
1303
  }
1009
1304
  function canonicalJson2(value) {
1010
1305
  if (Array.isArray(value)) return `[${value.map((entry) => canonicalJson2(entry === void 0 ? null : entry)).join(",")}]`;
@@ -1057,7 +1352,7 @@ async function acquireReceiptUpdateLock(path, operationId) {
1057
1352
  }
1058
1353
  async function reapStaleReceiptUpdateLock(lockPath) {
1059
1354
  try {
1060
- const owner = JSON.parse(await readFile3(lockPath, "utf8"));
1355
+ const owner = JSON.parse(await readFile4(lockPath, "utf8"));
1061
1356
  if (typeof owner.pid !== "number" || processIsAlive(owner.pid)) return false;
1062
1357
  } catch {
1063
1358
  return false;
@@ -1096,16 +1391,16 @@ async function fsyncDirectory(path) {
1096
1391
  }
1097
1392
  async function fileExists(path) {
1098
1393
  try {
1099
- await readFile3(path);
1394
+ await readFile4(path);
1100
1395
  return true;
1101
1396
  } catch (error) {
1102
- if (isMissing(error)) return false;
1397
+ if (isMissing2(error)) return false;
1103
1398
  throw error;
1104
1399
  }
1105
1400
  }
1106
1401
  async function reapStaleLock(lockPath) {
1107
1402
  try {
1108
- const owner = JSON.parse(await readFile3(join2(lockPath, "owner.json"), "utf8"));
1403
+ const owner = JSON.parse(await readFile4(join3(lockPath, "owner.json"), "utf8"));
1109
1404
  if (typeof owner.pid !== "number" || processIsAlive(owner.pid)) return false;
1110
1405
  } catch (error) {
1111
1406
  return false;
@@ -1130,19 +1425,19 @@ function processIsAlive(pid) {
1130
1425
  function isAlreadyExists(error) {
1131
1426
  return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
1132
1427
  }
1133
- function isMissing(error) {
1428
+ function isMissing2(error) {
1134
1429
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1135
1430
  }
1136
1431
 
1137
1432
  // src/mutation/declarations.ts
1138
1433
  var active;
1139
- function beginMutationPathDeclarations(repositoryRoot, operationIdInput, blockedPaths = []) {
1434
+ function beginMutationPathDeclarations(repositoryRoot, operationIdInput, blockedPaths = [], blockedPathDescriptions = /* @__PURE__ */ new Map()) {
1140
1435
  if (active) throw new Error("A mutation path declaration scope is already active.");
1141
1436
  const operationId = mutationOperationIdSchema.parse(operationIdInput);
1142
- const root = join3(mutationStateRoot(), "declarations");
1437
+ const root = join4(mutationStateRoot(), "declarations");
1143
1438
  mkdirSync(root, { recursive: true, mode: 448 });
1144
1439
  chmodSync(root, 448);
1145
- const journalPath = join3(root, `${operationId}.jsonl`);
1440
+ const journalPath = join4(root, `${operationId}.jsonl`);
1146
1441
  const journal = openSync(journalPath, "wx", 384);
1147
1442
  try {
1148
1443
  fsyncSync(journal);
@@ -1156,38 +1451,41 @@ function beginMutationPathDeclarations(repositoryRoot, operationIdInput, blocked
1156
1451
  closeSync(directory);
1157
1452
  }
1158
1453
  active = {
1159
- repositoryRoot: resolve4(repositoryRoot),
1454
+ repositoryRoot: resolve5(repositoryRoot),
1160
1455
  operationId,
1161
1456
  journalPath,
1162
1457
  paths: /* @__PURE__ */ new Set(),
1163
- blockedPaths: new Set(blockedPaths)
1458
+ blockedPaths: new Set(blockedPaths),
1459
+ blockedPathDescriptions
1164
1460
  };
1165
1461
  }
1166
1462
  function resumeMutationPathDeclarations(repositoryRoot, operationIdInput, blockedPaths = []) {
1167
1463
  if (active) throw new Error("A mutation path declaration scope is already active.");
1168
1464
  const operationId = mutationOperationIdSchema.parse(operationIdInput);
1169
- const journalPath = join3(mutationStateRoot(), "declarations", `${operationId}.jsonl`);
1465
+ const journalPath = join4(mutationStateRoot(), "declarations", `${operationId}.jsonl`);
1170
1466
  const paths = readMutationPathDeclarations(operationId);
1171
1467
  if (paths.length === 0) {
1172
1468
  const journal = openSync(journalPath, "r");
1173
1469
  closeSync(journal);
1174
1470
  }
1175
1471
  active = {
1176
- repositoryRoot: resolve4(repositoryRoot),
1472
+ repositoryRoot: resolve5(repositoryRoot),
1177
1473
  operationId,
1178
1474
  journalPath,
1179
1475
  paths: new Set(paths),
1180
- blockedPaths: new Set(blockedPaths)
1476
+ blockedPaths: new Set(blockedPaths),
1477
+ blockedPathDescriptions: /* @__PURE__ */ new Map()
1181
1478
  };
1182
1479
  }
1183
1480
  function declareMutationPath(path) {
1184
1481
  if (!active) return;
1185
- const absolute = isAbsolute3(path) ? resolve4(path) : resolve4(process.cwd(), path);
1482
+ const absolute = isAbsolute4(path) ? resolve5(path) : resolve5(process.cwd(), path);
1186
1483
  const relativePath = relative(active.repositoryRoot, absolute);
1187
- if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute3(relativePath)) return;
1484
+ if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute4(relativePath)) return;
1188
1485
  const normalized = relativePath.split(sep).join("/");
1189
1486
  if (active.blockedPaths.has(normalized)) {
1190
- throw new Error(`Mutation intended path is already dirty and cannot be claimed: ${normalized}.`);
1487
+ const description = active.blockedPathDescriptions.get(normalized);
1488
+ throw new Error(`Mutation intended path is already dirty and cannot be claimed: ${normalized}.${description ? ` ${description}` : ""}`);
1191
1489
  }
1192
1490
  if (active.paths.has(normalized)) return;
1193
1491
  active.paths.add(normalized);
@@ -1208,7 +1506,7 @@ function endMutationPathDeclarations() {
1208
1506
  }
1209
1507
  function readMutationPathDeclarations(operationIdInput) {
1210
1508
  const operationId = mutationOperationIdSchema.parse(operationIdInput);
1211
- const path = join3(mutationStateRoot(), "declarations", `${operationId}.jsonl`);
1509
+ const path = join4(mutationStateRoot(), "declarations", `${operationId}.jsonl`);
1212
1510
  let content;
1213
1511
  try {
1214
1512
  content = readFileSync(path, "utf8");
@@ -1228,15 +1526,15 @@ function readMutationPathDeclarations(operationIdInput) {
1228
1526
  }
1229
1527
 
1230
1528
  // src/model/workspace.ts
1231
- var artifactSelectorListSchema = z6.array(z6.string().min(1));
1529
+ var artifactSelectorListSchema = z7.array(z7.string().min(1));
1232
1530
  var CURRENT_WORKSPACE_SCHEMA_VERSION = 4;
1233
- var workspaceSelectionImportSchema = z6.object({
1234
- export: z6.string().min(1),
1531
+ var workspaceSelectionImportSchema = z7.object({
1532
+ export: z7.string().min(1),
1235
1533
  add: artifactSelectorListSchema.optional(),
1236
1534
  exclude: artifactSelectorListSchema.optional()
1237
1535
  }).strict();
1238
- var workspaceSelectionExportSchema = z6.object({
1239
- extends: z6.string().min(1).optional(),
1536
+ var workspaceSelectionExportSchema = z7.object({
1537
+ extends: z7.string().min(1).optional(),
1240
1538
  select: artifactSelectorListSchema.optional(),
1241
1539
  add: artifactSelectorListSchema.optional(),
1242
1540
  exclude: artifactSelectorListSchema.optional()
@@ -1256,29 +1554,29 @@ var workspaceSelectionExportSchema = z6.object({
1256
1554
  });
1257
1555
  }
1258
1556
  });
1259
- var workspaceExportsSchema = z6.object({
1260
- selections: z6.record(z6.string().min(1), workspaceSelectionExportSchema).default({})
1557
+ var workspaceExportsSchema = z7.object({
1558
+ selections: z7.record(z7.string().min(1), workspaceSelectionExportSchema).default({})
1261
1559
  }).strict();
1262
- var workspacePackageBaseSchema = z6.object({
1263
- name: z6.string().min(1),
1264
- source: z6.string().min(1),
1265
- driver: z6.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
1266
- adapter: z6.string().min(1).default("openclaw"),
1267
- adapterConfig: z6.string().min(1).optional(),
1268
- adapterModule: z6.string().min(1).optional(),
1269
- adapterCodeHash: z6.string().min(16).optional(),
1560
+ var workspacePackageBaseSchema = z7.object({
1561
+ name: z7.string().min(1),
1562
+ source: z7.string().min(1),
1563
+ driver: z7.enum(["local", "git", "skillkit", "vercel-skills", "mcp-registry", "clawhub"]).default("local"),
1564
+ adapter: z7.string().min(1).default("openclaw"),
1565
+ adapterConfig: z7.string().min(1).optional(),
1566
+ adapterModule: z7.string().min(1).optional(),
1567
+ adapterCodeHash: z7.string().min(16).optional(),
1270
1568
  installationType: installationTypeSchema.optional(),
1271
- mode: z6.enum(["pinned", "tracking"]).default("pinned"),
1272
- version: z6.string().min(1).refine(isSupportedVersionRange, {
1569
+ mode: z7.enum(["pinned", "tracking"]).default("pinned"),
1570
+ version: z7.string().min(1).refine(isSupportedVersionRange, {
1273
1571
  message: "Version policy must be an exact semver, ~range, ^range, comparator range, or *"
1274
1572
  }).optional(),
1275
- requestedRef: z6.string().min(1).optional(),
1276
- select: z6.array(z6.string().min(1)).optional(),
1277
- skills: z6.array(z6.string().min(1)).optional(),
1278
- withSuggestions: z6.boolean().optional(),
1279
- suggestions: z6.array(z6.string().min(1)).optional(),
1280
- aliases: z6.record(z6.string(), z6.string().min(1)).optional(),
1281
- overrides: z6.array(z6.string().min(1)).optional()
1573
+ requestedRef: z7.string().min(1).optional(),
1574
+ select: z7.array(z7.string().min(1)).optional(),
1575
+ skills: z7.array(z7.string().min(1)).optional(),
1576
+ withSuggestions: z7.boolean().optional(),
1577
+ suggestions: z7.array(z7.string().min(1)).optional(),
1578
+ aliases: z7.record(z7.string(), z7.string().min(1)).optional(),
1579
+ overrides: z7.array(z7.string().min(1)).optional()
1282
1580
  });
1283
1581
  var workspacePackageSchema = workspacePackageBaseSchema.extend({
1284
1582
  selection: workspaceSelectionImportSchema.optional()
@@ -1292,43 +1590,43 @@ var workspacePackageSchema = workspacePackageBaseSchema.extend({
1292
1590
  }
1293
1591
  });
1294
1592
  var workspacePackageV1Schema = workspacePackageBaseSchema.extend({
1295
- selection: z6.never().optional()
1593
+ selection: z7.never().optional()
1296
1594
  });
1297
- var commandSchema = z6.array(z6.string().min(1)).min(1);
1298
- var commandListSchema = z6.array(commandSchema).min(1).optional();
1299
- var installStateKeySchema = z6.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i);
1300
- var fleetIdSchema = z6.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i);
1301
- var registeredFleetSchema = z6.object({
1302
- root: z6.string().min(1),
1303
- requiredPackages: z6.array(z6.string().min(1)).min(1)
1595
+ var commandSchema = z7.array(z7.string().min(1)).min(1);
1596
+ var commandListSchema = z7.array(commandSchema).min(1).optional();
1597
+ var installStateKeySchema = z7.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i);
1598
+ var fleetIdSchema = z7.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i);
1599
+ var registeredFleetSchema = z7.object({
1600
+ root: z7.string().min(1),
1601
+ requiredPackages: z7.array(z7.string().min(1)).min(1)
1304
1602
  }).strict().superRefine((fleet, ctx) => {
1305
1603
  if (new Set(fleet.requiredPackages).size !== fleet.requiredPackages.length) {
1306
1604
  ctx.addIssue({ code: "custom", path: ["requiredPackages"], message: "Required fleet packages must be unique." });
1307
1605
  }
1308
1606
  });
1309
- var workspaceProfileRuntimeSchema = z6.object({
1310
- agent: z6.string().min(1).optional(),
1311
- adapter: z6.string().min(1).default("openclaw"),
1312
- adapterConfig: z6.string().min(1).optional(),
1313
- adapterModule: z6.string().min(1).optional(),
1607
+ var workspaceProfileRuntimeSchema = z7.object({
1608
+ agent: z7.string().min(1).optional(),
1609
+ adapter: z7.string().min(1).default("openclaw"),
1610
+ adapterConfig: z7.string().min(1).optional(),
1611
+ adapterModule: z7.string().min(1).optional(),
1314
1612
  installationType: installationTypeSchema.optional(),
1315
1613
  stateKey: installStateKeySchema.optional(),
1316
- targetRoot: z6.string().min(1).optional(),
1317
- executePlugins: z6.boolean().optional(),
1318
- reloadRuntimes: z6.boolean().optional(),
1614
+ targetRoot: z7.string().min(1).optional(),
1615
+ executePlugins: z7.boolean().optional(),
1616
+ reloadRuntimes: z7.boolean().optional(),
1319
1617
  reloadCommands: commandListSchema
1320
1618
  });
1321
- var workspaceProfileMemberSchema = z6.object({
1322
- id: z6.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i),
1323
- workspace: z6.string().min(1),
1324
- profile: z6.string().min(1),
1619
+ var workspaceProfileMemberSchema = z7.object({
1620
+ id: z7.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i),
1621
+ workspace: z7.string().min(1),
1622
+ profile: z7.string().min(1),
1325
1623
  fleet: fleetIdSchema.optional(),
1326
- transport: z6.enum(["local", "ssh"]).default("local"),
1327
- host: z6.string().min(1).optional(),
1328
- user: z6.string().min(1).optional(),
1329
- port: z6.number().int().positive().optional(),
1330
- identityFile: z6.string().min(1).optional(),
1331
- refreshTtlSeconds: z6.number().int().positive().optional()
1624
+ transport: z7.enum(["local", "ssh"]).default("local"),
1625
+ host: z7.string().min(1).optional(),
1626
+ user: z7.string().min(1).optional(),
1627
+ port: z7.number().int().positive().optional(),
1628
+ identityFile: z7.string().min(1).optional(),
1629
+ refreshTtlSeconds: z7.number().int().positive().optional()
1332
1630
  }).strict().superRefine((member, ctx) => {
1333
1631
  if (member.transport === "ssh" && !member.host) {
1334
1632
  ctx.addIssue({ code: "custom", path: ["host"], message: "SSH profile members require host" });
@@ -1337,14 +1635,14 @@ var workspaceProfileMemberSchema = z6.object({
1337
1635
  ctx.addIssue({ code: "custom", path: ["workspace"], message: "SSH profile member workspaces must be absolute" });
1338
1636
  }
1339
1637
  });
1340
- var workspaceLeafProfileSchema = z6.object({
1341
- runtimes: z6.array(workspaceProfileRuntimeSchema).min(1),
1342
- members: z6.never().optional()
1638
+ var workspaceLeafProfileSchema = z7.object({
1639
+ runtimes: z7.array(workspaceProfileRuntimeSchema).min(1),
1640
+ members: z7.never().optional()
1343
1641
  }).strict();
1344
- var workspaceCompositeProfileSchema = z6.object({
1345
- members: z6.array(workspaceProfileMemberSchema).min(1),
1346
- runtimes: z6.never().optional(),
1347
- refreshTtlSeconds: z6.number().int().positive().default(86400)
1642
+ var workspaceCompositeProfileSchema = z7.object({
1643
+ members: z7.array(workspaceProfileMemberSchema).min(1),
1644
+ runtimes: z7.never().optional(),
1645
+ refreshTtlSeconds: z7.number().int().positive().default(86400)
1348
1646
  }).strict().superRefine((profile, ctx) => {
1349
1647
  const seen = /* @__PURE__ */ new Set();
1350
1648
  for (const [index, member] of profile.members.entries()) {
@@ -1354,32 +1652,32 @@ var workspaceCompositeProfileSchema = z6.object({
1354
1652
  seen.add(member.id);
1355
1653
  }
1356
1654
  });
1357
- var workspaceProfileSchema = z6.union([
1655
+ var workspaceProfileSchema = z7.union([
1358
1656
  workspaceLeafProfileSchema,
1359
1657
  workspaceCompositeProfileSchema
1360
1658
  ]);
1361
- var workspaceRegistrySchema = z6.object({
1362
- sources: z6.array(z6.string().min(1)).optional(),
1363
- ttlSeconds: z6.number().int().positive().optional()
1659
+ var workspaceRegistrySchema = z7.object({
1660
+ sources: z7.array(z7.string().min(1)).optional(),
1661
+ ttlSeconds: z7.number().int().positive().optional()
1364
1662
  }).default({});
1365
- var workspaceTrustSchema = z6.object({
1366
- allow: z6.array(z6.string().min(1)).optional(),
1367
- acceptedSources: z6.array(z6.string().min(1)).optional(),
1368
- denyArtifactTypes: z6.array(artifactTypeSchema).optional(),
1369
- requireReviewForTransitive: z6.boolean().optional()
1663
+ var workspaceTrustSchema = z7.object({
1664
+ allow: z7.array(z7.string().min(1)).optional(),
1665
+ acceptedSources: z7.array(z7.string().min(1)).optional(),
1666
+ denyArtifactTypes: z7.array(artifactTypeSchema).optional(),
1667
+ requireReviewForTransitive: z7.boolean().optional()
1370
1668
  }).default({});
1371
- var workspaceAgentSchema = z6.object({
1372
- adapter: z6.string().min(1),
1373
- adapterConfig: z6.string().min(1).optional(),
1374
- adapterModule: z6.string().min(1).optional(),
1375
- root: z6.string().min(1),
1669
+ var workspaceAgentSchema = z7.object({
1670
+ adapter: z7.string().min(1),
1671
+ adapterConfig: z7.string().min(1).optional(),
1672
+ adapterModule: z7.string().min(1).optional(),
1673
+ root: z7.string().min(1),
1376
1674
  installationType: installationTypeSchema.optional(),
1377
1675
  stateKey: installStateKeySchema.optional(),
1378
- transport: z6.enum(["local", "ssh"]).default("local"),
1379
- host: z6.string().min(1).optional(),
1380
- user: z6.string().min(1).optional(),
1381
- port: z6.number().int().positive().optional(),
1382
- identityFile: z6.string().min(1).optional(),
1676
+ transport: z7.enum(["local", "ssh"]).default("local"),
1677
+ host: z7.string().min(1).optional(),
1678
+ user: z7.string().min(1).optional(),
1679
+ port: z7.number().int().positive().optional(),
1680
+ identityFile: z7.string().min(1).optional(),
1383
1681
  reloadCommands: commandListSchema
1384
1682
  }).superRefine((agent, ctx) => {
1385
1683
  if (agent.transport !== "ssh") return;
@@ -1391,39 +1689,39 @@ var workspaceAgentSchema = z6.object({
1391
1689
  });
1392
1690
  }
1393
1691
  });
1394
- var workspaceConfigBaseSchema = z6.object({
1395
- bootstrapSkills: z6.boolean().optional(),
1692
+ var workspaceConfigBaseSchema = z7.object({
1693
+ bootstrapSkills: z7.boolean().optional(),
1396
1694
  registry: workspaceRegistrySchema,
1397
1695
  trust: workspaceTrustSchema,
1398
- profiles: z6.record(z6.string(), workspaceProfileSchema).default({}),
1399
- agents: z6.record(z6.string(), workspaceAgentSchema).default({})
1696
+ profiles: z7.record(z7.string(), workspaceProfileSchema).default({}),
1697
+ agents: z7.record(z7.string(), workspaceAgentSchema).default({})
1400
1698
  });
1401
1699
  var workspaceConfigV1Schema = workspaceConfigBaseSchema.extend({
1402
- schemaVersion: z6.literal(1),
1403
- packages: z6.array(workspacePackageV1Schema).default([]),
1404
- exports: z6.never().optional()
1700
+ schemaVersion: z7.literal(1),
1701
+ packages: z7.array(workspacePackageV1Schema).default([]),
1702
+ exports: z7.never().optional()
1405
1703
  }).strict();
1406
1704
  var workspaceConfigV2Schema = workspaceConfigBaseSchema.extend({
1407
- schemaVersion: z6.literal(2),
1408
- packages: z6.array(workspacePackageSchema).default([]),
1705
+ schemaVersion: z7.literal(2),
1706
+ packages: z7.array(workspacePackageSchema).default([]),
1409
1707
  exports: workspaceExportsSchema.default({ selections: {} })
1410
1708
  }).strict();
1411
1709
  var workspaceConfigV3Schema = workspaceConfigBaseSchema.extend({
1412
- schemaVersion: z6.literal(3),
1413
- packages: z6.array(workspacePackageSchema).default([]),
1710
+ schemaVersion: z7.literal(3),
1711
+ packages: z7.array(workspacePackageSchema).default([]),
1414
1712
  exports: workspaceExportsSchema.default({ selections: {} }),
1415
1713
  fleetId: fleetIdSchema.optional(),
1416
- fleets: z6.record(fleetIdSchema, registeredFleetSchema).default({})
1714
+ fleets: z7.record(fleetIdSchema, registeredFleetSchema).default({})
1417
1715
  }).strict();
1418
1716
  var workspaceConfigV4Schema = workspaceConfigBaseSchema.extend({
1419
- schemaVersion: z6.literal(CURRENT_WORKSPACE_SCHEMA_VERSION),
1420
- packages: z6.array(workspacePackageSchema).default([]),
1717
+ schemaVersion: z7.literal(CURRENT_WORKSPACE_SCHEMA_VERSION),
1718
+ packages: z7.array(workspacePackageSchema).default([]),
1421
1719
  exports: workspaceExportsSchema.default({ selections: {} }),
1422
1720
  fleetId: fleetIdSchema.optional(),
1423
- fleets: z6.record(fleetIdSchema, registeredFleetSchema).default({}),
1721
+ fleets: z7.record(fleetIdSchema, registeredFleetSchema).default({}),
1424
1722
  mutationPolicy: mutationPolicySchema.optional()
1425
1723
  }).strict();
1426
- var workspaceConfigSchema = z6.discriminatedUnion("schemaVersion", [
1724
+ var workspaceConfigSchema = z7.discriminatedUnion("schemaVersion", [
1427
1725
  workspaceConfigV1Schema,
1428
1726
  workspaceConfigV2Schema,
1429
1727
  workspaceConfigV3Schema,
@@ -1433,12 +1731,12 @@ function supportsFleetConfig(config) {
1433
1731
  return config.schemaVersion === 3 || config.schemaVersion === 4;
1434
1732
  }
1435
1733
  function workspaceConfigPath(workspaceRoot) {
1436
- return join4(workspaceRoot, ".agentwheel", "config.json");
1734
+ return join5(workspaceRoot, ".agentwheel", "config.json");
1437
1735
  }
1438
1736
  async function readWorkspaceConfig(workspaceRoot) {
1439
1737
  const path = workspaceConfigPath(workspaceRoot);
1440
1738
  if (!await pathExists(path)) return emptyWorkspaceConfig();
1441
- return workspaceConfigSchema.parse(JSON.parse(await readFile4(path, "utf8")));
1739
+ return workspaceConfigSchema.parse(JSON.parse(await readFile5(path, "utf8")));
1442
1740
  }
1443
1741
  async function writeWorkspaceConfig(workspaceRoot, config) {
1444
1742
  const path = workspaceConfigPath(workspaceRoot);
@@ -1452,20 +1750,20 @@ function upsertPackage(config, entry) {
1452
1750
  packages.sort((a, b) => a.name.localeCompare(b.name));
1453
1751
  return workspaceConfigSchema.parse({ ...parsed, packages });
1454
1752
  }
1455
- function globalWorkspaceConfigPath(globalRoot = homedir3()) {
1456
- return join4(globalRoot, ".agentwheel", "config.json");
1753
+ function globalWorkspaceConfigPath(globalRoot = homedir4()) {
1754
+ return join5(globalRoot, ".agentwheel", "config.json");
1457
1755
  }
1458
1756
  async function findWorkspaceRoot(start = process.cwd()) {
1459
- let current = resolve5(start);
1757
+ let current = resolve6(start);
1460
1758
  while (true) {
1461
1759
  if (await pathExists(workspaceConfigPath(current))) return current;
1462
1760
  const parent = dirname4(current);
1463
- if (parent === current) return resolve5(start);
1761
+ if (parent === current) return resolve6(start);
1464
1762
  current = parent;
1465
1763
  }
1466
1764
  }
1467
1765
  async function findExistingWorkspaceRoot(start = process.cwd()) {
1468
- let current = resolve5(start);
1766
+ let current = resolve6(start);
1469
1767
  while (true) {
1470
1768
  if (await pathExists(workspaceConfigPath(current))) return current;
1471
1769
  const parent = dirname4(current);
@@ -1500,9 +1798,9 @@ function mergeWorkspaceConfig(global, project) {
1500
1798
  });
1501
1799
  }
1502
1800
  function resolveConfigPath(path, baseRoot) {
1503
- if (path.startsWith("~/")) return resolve5(homedir3(), path.slice(2));
1504
- if (path === "~") return homedir3();
1505
- return path.startsWith("/") ? resolve5(path) : resolve5(baseRoot, path);
1801
+ if (path.startsWith("~/")) return resolve6(homedir4(), path.slice(2));
1802
+ if (path === "~") return homedir4();
1803
+ return path.startsWith("/") ? resolve6(path) : resolve6(baseRoot, path);
1506
1804
  }
1507
1805
  function emptyWorkspaceConfig() {
1508
1806
  return { schemaVersion: 1, packages: [], registry: {}, trust: {}, profiles: {}, agents: {} };
@@ -1512,7 +1810,7 @@ function isCompositeWorkspaceProfile(profile) {
1512
1810
  }
1513
1811
  async function readConfigPath(path) {
1514
1812
  if (!await pathExists(path)) return emptyWorkspaceConfig();
1515
- return workspaceConfigSchema.parse(JSON.parse(await readFile4(path, "utf8")));
1813
+ return workspaceConfigSchema.parse(JSON.parse(await readFile5(path, "utf8")));
1516
1814
  }
1517
1815
  function mergeWorkspaceTrust(global, project) {
1518
1816
  return {
@@ -1527,87 +1825,15 @@ function sortedUnique(values) {
1527
1825
  }
1528
1826
 
1529
1827
  // src/mutation/providers.ts
1530
- import { createHash as createHash4 } from "crypto";
1531
- import { chmod as chmod2, mkdtemp, open as open2, readFile as readFile6, rename as rename3, rm as rm3, rmdir, stat, writeFile as writeFile2 } from "fs/promises";
1828
+ import { createHash as createHash5 } from "crypto";
1829
+ import { chmod as chmod2, mkdtemp, open as open2, readFile as readFile7, rename as rename3, rm as rm3, rmdir, stat, writeFile as writeFile2 } from "fs/promises";
1532
1830
  import { tmpdir } from "os";
1533
- import { isAbsolute as isAbsolute4, join as join5, resolve as resolve7 } from "path";
1534
-
1535
- // src/mutation/repository.ts
1536
- import { createHash as createHash3 } from "crypto";
1537
- import { lstat, readFile as readFile5, readlink, readdir as readdir3 } from "fs/promises";
1538
- import { posix as posix2, relative as relative2, resolve as resolve6, sep as sep2 } from "path";
1539
-
1540
- // src/mutation/process.ts
1541
- import { spawn as spawn2 } from "child_process";
1542
- async function runProcess(command, args, options = {}) {
1543
- return new Promise((resolve10, reject) => {
1544
- const useProcessGroup = options.killProcessGroup === true && process.platform !== "win32";
1545
- const child = spawn2(command, args, {
1546
- cwd: options.cwd,
1547
- env: options.env,
1548
- stdio: options.inheritedFileDescriptor === void 0 ? ["pipe", "pipe", "pipe"] : ["pipe", "pipe", "pipe", options.inheritedFileDescriptor],
1549
- detached: useProcessGroup,
1550
- windowsHide: true
1551
- });
1552
- const stdout = [];
1553
- const stderr = [];
1554
- let stdoutBytes = 0;
1555
- let stderrBytes = 0;
1556
- let outputExceeded = false;
1557
- let timedOut = false;
1558
- const terminate = () => {
1559
- if (useProcessGroup && child.pid !== void 0) {
1560
- try {
1561
- process.kill(-child.pid, "SIGKILL");
1562
- return;
1563
- } catch {
1564
- }
1565
- }
1566
- child.kill("SIGKILL");
1567
- };
1568
- const timeout = options.timeoutMs === void 0 ? void 0 : setTimeout(() => {
1569
- timedOut = true;
1570
- terminate();
1571
- }, options.timeoutMs);
1572
- const collect = (target, stream, chunk) => {
1573
- if (outputExceeded) return;
1574
- if (stream === "stdout") stdoutBytes += chunk.length;
1575
- else stderrBytes += chunk.length;
1576
- if (options.maxOutputBytes !== void 0 && (stdoutBytes > options.maxOutputBytes || stderrBytes > options.maxOutputBytes)) {
1577
- outputExceeded = true;
1578
- terminate();
1579
- return;
1580
- }
1581
- target.push(chunk);
1582
- };
1583
- child.stdout.on("data", (chunk) => collect(stdout, "stdout", chunk));
1584
- child.stderr.on("data", (chunk) => collect(stderr, "stderr", chunk));
1585
- child.on("error", reject);
1586
- child.on("close", (code) => {
1587
- if (timeout) clearTimeout(timeout);
1588
- const exitCode = code ?? 1;
1589
- const result = { stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), exitCode };
1590
- if (timedOut) {
1591
- reject(new Error(`${command} exceeded the ${options.timeoutMs}ms execution timeout.`));
1592
- return;
1593
- }
1594
- if (outputExceeded) {
1595
- reject(new Error(`${command} exceeded the provider output limit.`));
1596
- return;
1597
- }
1598
- if (!(options.allowExitCodes ?? [0]).includes(exitCode)) {
1599
- const detail = options.includeFailureOutput === false ? "" : result.stderr.toString("utf8").trim() || result.stdout.toString("utf8").trim();
1600
- reject(new Error(`${command} exited ${exitCode}${detail ? `: ${detail}` : ""}`));
1601
- return;
1602
- }
1603
- resolve10(result);
1604
- });
1605
- if (options.input !== void 0) child.stdin.end(options.input);
1606
- else child.stdin.end();
1607
- });
1608
- }
1831
+ import { isAbsolute as isAbsolute5, join as join6, resolve as resolve8 } from "path";
1609
1832
 
1610
1833
  // src/mutation/repository.ts
1834
+ import { createHash as createHash4 } from "crypto";
1835
+ import { lstat, readFile as readFile6, readlink, readdir as readdir3 } from "fs/promises";
1836
+ import { posix as posix3, relative as relative2, resolve as resolve7, sep as sep2 } from "path";
1611
1837
  async function discoverGitRepository(start) {
1612
1838
  const probe = await runProcess("git", ["-C", start, "rev-parse", "--show-toplevel"], { allowExitCodes: [0, 128] });
1613
1839
  if (probe.exitCode !== 0) return void 0;
@@ -1630,7 +1856,7 @@ async function assertGitPreflight(repository) {
1630
1856
  if (result.exitCode === 0) throw new Error(`Revisioning refuses an in-progress Git operation (${marker}) in ${gitDir}.`);
1631
1857
  }
1632
1858
  for (const marker of ["rebase-merge", "rebase-apply"]) {
1633
- if (await exists(resolve6(gitDir, marker))) throw new Error(`Revisioning refuses an in-progress Git operation (${marker}) in ${gitDir}.`);
1859
+ if (await exists(resolve7(gitDir, marker))) throw new Error(`Revisioning refuses an in-progress Git operation (${marker}) in ${gitDir}.`);
1634
1860
  }
1635
1861
  }
1636
1862
  async function snapshotRepository(repositoryRoot) {
@@ -1695,29 +1921,29 @@ async function hashHeadPath(repositoryRoot, head, path) {
1695
1921
  return sha256(result.stdout);
1696
1922
  }
1697
1923
  async function hashWorkingPath(repositoryRoot, path) {
1698
- const absolute = resolve6(repositoryRoot, ...path.split("/"));
1924
+ const absolute = resolve7(repositoryRoot, ...path.split("/"));
1699
1925
  let stats;
1700
1926
  try {
1701
1927
  stats = await lstat(absolute);
1702
1928
  } catch (error) {
1703
- if (isMissing2(error)) return null;
1929
+ if (isMissing3(error)) return null;
1704
1930
  throw error;
1705
1931
  }
1706
1932
  if (stats.isSymbolicLink()) return sha256(Buffer.from(await readlink(absolute), "utf8"));
1707
- if (stats.isFile()) return sha256(await readFile5(absolute));
1933
+ if (stats.isFile()) return sha256(await readFile6(absolute));
1708
1934
  if (stats.isDirectory()) return hashDirectory(absolute);
1709
1935
  throw new Error(`Unsupported repository path kind: ${path}`);
1710
1936
  }
1711
1937
  async function hashDirectory(root) {
1712
- const hash = createHash3("sha256");
1938
+ const hash = createHash4("sha256");
1713
1939
  async function walk(dir) {
1714
1940
  for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
1715
1941
  if (entry.name === ".git" || entry.name === "node_modules") continue;
1716
- const absolute = resolve6(dir, entry.name);
1942
+ const absolute = resolve7(dir, entry.name);
1717
1943
  const relativePath = normalizeRepoPath(relative2(root, absolute));
1718
1944
  if (entry.isDirectory()) await walk(absolute);
1719
1945
  else if (entry.isSymbolicLink()) hash.update(relativePath).update("\0").update(await readlink(absolute)).update("\0");
1720
- else if (entry.isFile()) hash.update(relativePath).update("\0").update(await readFile5(absolute)).update("\0");
1946
+ else if (entry.isFile()) hash.update(relativePath).update("\0").update(await readFile6(absolute)).update("\0");
1721
1947
  }
1722
1948
  }
1723
1949
  await walk(root);
@@ -1727,12 +1953,12 @@ function splitNull(value) {
1727
1953
  return value.toString("utf8").split("\0").filter(Boolean);
1728
1954
  }
1729
1955
  function normalizeRepoPath(path) {
1730
- return sep2 === "/" ? path : path.split(sep2).join(posix2.sep);
1956
+ return sep2 === "/" ? path : path.split(sep2).join(posix3.sep);
1731
1957
  }
1732
1958
  function sha256(value) {
1733
- return createHash3("sha256").update(value).digest("hex");
1959
+ return createHash4("sha256").update(value).digest("hex");
1734
1960
  }
1735
- function isMissing2(error) {
1961
+ function isMissing3(error) {
1736
1962
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
1737
1963
  }
1738
1964
  async function exists(path) {
@@ -1740,7 +1966,7 @@ async function exists(path) {
1740
1966
  await lstat(path);
1741
1967
  return true;
1742
1968
  } catch (error) {
1743
- if (isMissing2(error)) return false;
1969
+ if (isMissing3(error)) return false;
1744
1970
  throw error;
1745
1971
  }
1746
1972
  }
@@ -1845,7 +2071,7 @@ async function readVerifiedExecutable(executable, expectedSha256) {
1845
2071
  const info = await handle.stat();
1846
2072
  if (!info.isFile()) throw new Error(`Revision provider executable must be a regular file: ${executable}.`);
1847
2073
  const bytes = await handle.readFile();
1848
- const actualSha256 = createHash4("sha256").update(bytes).digest("hex");
2074
+ const actualSha256 = createHash5("sha256").update(bytes).digest("hex");
1849
2075
  if (actualSha256 !== expectedSha256) {
1850
2076
  throw new Error(
1851
2077
  `Revision provider executable hash mismatch for ${executable}: expected ${expectedSha256}, found ${actualSha256}.`
@@ -1862,8 +2088,8 @@ async function openImmutableExecutableSnapshot(bytes) {
1862
2088
  `Command revision providers require an unlinked immutable executable snapshot, which is unsupported on ${process.platform}.`
1863
2089
  );
1864
2090
  }
1865
- const root = await mkdtemp(join5(tmpdir(), "agentwheel-revision-command-"));
1866
- const path = join5(root, "entrypoint");
2091
+ const root = await mkdtemp(join6(tmpdir(), "agentwheel-revision-command-"));
2092
+ const path = join6(root, "entrypoint");
1867
2093
  let snapshot;
1868
2094
  try {
1869
2095
  await chmod2(root, 448);
@@ -1958,7 +2184,7 @@ async function assertProviderSemantics(request, response) {
1958
2184
  }
1959
2185
  async function invokeGitProvider(providerId, request) {
1960
2186
  const repository = await discoverGitRepository(request.repositoryRoot);
1961
- if (!repository || repository.root !== resolve7(request.repositoryRoot)) {
2187
+ if (!repository || repository.root !== resolve8(request.repositoryRoot)) {
1962
2188
  throw new Error(`Git revision provider requires the repository root itself: ${request.repositoryRoot}.`);
1963
2189
  }
1964
2190
  if (request.action === "check" || request.action === "preflight") {
@@ -2003,8 +2229,8 @@ async function finalizeGit(providerId, request) {
2003
2229
  const indexLease = await captureIndexLease(request.repositoryRoot);
2004
2230
  await assertIndexTree(request.repositoryRoot, request.expectedHead);
2005
2231
  const hookBaseline = await snapshotRepository(request.repositoryRoot);
2006
- const tempRoot = await mkdtemp(join5(tmpdir(), "agentwheel-git-index-"));
2007
- const indexPath = join5(tempRoot, "index");
2232
+ const tempRoot = await mkdtemp(join6(tmpdir(), "agentwheel-git-index-"));
2233
+ const indexPath = join6(tempRoot, "index");
2008
2234
  const env = { ...process.env, GIT_INDEX_FILE: indexPath };
2009
2235
  let commitSha;
2010
2236
  try {
@@ -2015,14 +2241,14 @@ async function finalizeGit(providerId, request) {
2015
2241
  await verifyTemporaryIndexPaths(request, env);
2016
2242
  await runProcess("git", ["-C", request.repositoryRoot, "diff", "--cached", "--check"], { env });
2017
2243
  const tree = (await runProcess("git", ["-C", request.repositoryRoot, "write-tree"], { env })).stdout.toString("utf8").trim();
2018
- const messagePath = join5(tempRoot, "COMMIT_EDITMSG");
2244
+ const messagePath = join6(tempRoot, "COMMIT_EDITMSG");
2019
2245
  await writeFile2(messagePath, commitMessage(request, planDigest), { encoding: "utf8", mode: 384 });
2020
2246
  await runCommitHooks(request, env, messagePath);
2021
2247
  assertRepositorySnapshotEqual(hookBaseline, await snapshotRepository(request.repositoryRoot), "Git commit hooks");
2022
2248
  const stagedAfterHooks = splitNull2((await runProcess("git", ["-C", request.repositoryRoot, "diff", "--cached", "--name-only", "-z"], { env })).stdout);
2023
2249
  assertExactSet(stagedAfterHooks, request.paths.map((entry) => entry.path), "temporary Git index after hooks");
2024
2250
  await verifyTemporaryIndexPaths(request, env);
2025
- const message = await readFile6(messagePath, "utf8");
2251
+ const message = await readFile7(messagePath, "utf8");
2026
2252
  assertCommitMessageContract(message, request, planDigest);
2027
2253
  commitSha = (await runProcess("git", ["-C", request.repositoryRoot, "commit-tree", tree, "-p", request.expectedHead], {
2028
2254
  env,
@@ -2053,12 +2279,12 @@ async function runCommitHooks(request, env, messagePath) {
2053
2279
  ["commit-msg", [messagePath]]
2054
2280
  ]) {
2055
2281
  const rawPath = (await runProcess("git", ["-C", request.repositoryRoot, "rev-parse", "--git-path", `hooks/${name}`])).stdout.toString("utf8").trim();
2056
- const hookPath = isAbsolute4(rawPath) ? rawPath : resolve7(request.repositoryRoot, rawPath);
2282
+ const hookPath = isAbsolute5(rawPath) ? rawPath : resolve8(request.repositoryRoot, rawPath);
2057
2283
  let executable = false;
2058
2284
  try {
2059
2285
  executable = ((await stat(hookPath)).mode & 73) !== 0;
2060
2286
  } catch (error) {
2061
- if (!isMissing3(error)) throw error;
2287
+ if (!isMissing4(error)) throw error;
2062
2288
  }
2063
2289
  if (!executable) continue;
2064
2290
  try {
@@ -2137,25 +2363,25 @@ async function realignIndexAfterRecoveredCommit(repositoryRoot, expectedHead, co
2137
2363
  }
2138
2364
  async function captureIndexLease(repositoryRoot) {
2139
2365
  const rawPath = (await runProcess("git", ["-C", repositoryRoot, "rev-parse", "--git-path", "index"])).stdout.toString("utf8").trim();
2140
- const path = isAbsolute4(rawPath) ? rawPath : resolve7(repositoryRoot, rawPath);
2366
+ const path = isAbsolute5(rawPath) ? rawPath : resolve8(repositoryRoot, rawPath);
2141
2367
  try {
2142
- const [content, info] = await Promise.all([readFile6(path), stat(path)]);
2368
+ const [content, info] = await Promise.all([readFile7(path), stat(path)]);
2143
2369
  return { path, sha256: sha2562(content), mode: info.mode & 511 };
2144
2370
  } catch (error) {
2145
- if (isMissing3(error)) return { path, sha256: null, mode: 384 };
2371
+ if (isMissing4(error)) return { path, sha256: null, mode: 384 };
2146
2372
  throw error;
2147
2373
  }
2148
2374
  }
2149
2375
  async function replaceIndexCas(repositoryRoot, lease, commitSha) {
2150
- const tempRoot = await mkdtemp(join5(tmpdir(), "agentwheel-index-replacement-"));
2151
- const replacementPath = join5(tempRoot, "index");
2376
+ const tempRoot = await mkdtemp(join6(tmpdir(), "agentwheel-index-replacement-"));
2377
+ const replacementPath = join6(tempRoot, "index");
2152
2378
  const lockPath = `${lease.path}.lock`;
2153
2379
  let handle;
2154
2380
  try {
2155
2381
  await runProcess("git", ["-C", repositoryRoot, "read-tree", commitSha], {
2156
2382
  env: { ...process.env, GIT_INDEX_FILE: replacementPath }
2157
2383
  });
2158
- const replacement = await readFile6(replacementPath);
2384
+ const replacement = await readFile7(replacementPath);
2159
2385
  handle = await open2(lockPath, "wx", lease.mode);
2160
2386
  const current = await readOptionalFile(lease.path);
2161
2387
  const currentSha256 = current ? sha2562(current) : null;
@@ -2197,16 +2423,16 @@ async function hashGitObject(repositoryRoot, spec) {
2197
2423
  }
2198
2424
  async function readOptionalFile(path) {
2199
2425
  try {
2200
- return await readFile6(path);
2426
+ return await readFile7(path);
2201
2427
  } catch (error) {
2202
- if (isMissing3(error)) return null;
2428
+ if (isMissing4(error)) return null;
2203
2429
  throw error;
2204
2430
  }
2205
2431
  }
2206
2432
  function sha2562(value) {
2207
- return createHash4("sha256").update(value).digest("hex");
2433
+ return createHash5("sha256").update(value).digest("hex");
2208
2434
  }
2209
- function isMissing3(error) {
2435
+ function isMissing4(error) {
2210
2436
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
2211
2437
  }
2212
2438
  async function assertGitProviderCompatible(repositoryRoot) {
@@ -2352,15 +2578,18 @@ var GovernedMutation = class _GovernedMutation {
2352
2578
  runtimeJournals: [],
2353
2579
  status: "prepared"
2354
2580
  });
2355
- if (policy.revisioning.mode === "commit-after-verify" && options.requireCleanWorkingTree && baseline && baseline.changed.size > 0) {
2581
+ const blockedPathDescriptions = baseline && repository && baseline.changed.size > 0 ? await describeDirtyPathOwnershipByPath(repository.root, [...baseline.changed.keys()]) : /* @__PURE__ */ new Map();
2582
+ const dirtyDeclaredPaths = baseline && repository ? dirtyRepositoryPaths(repository.root, baseline, options.anticipatedPaths ?? []) : [];
2583
+ if (dirtyDeclaredPaths.length > 0) {
2356
2584
  throw new Error(
2357
- `This governed command computes declarative paths during planning and requires a clean working tree before runtime mutation; found: ${[...baseline.changed.keys()].sort().join(", ")}.`
2585
+ `Mutation declared paths are already dirty and cannot be claimed: ${dirtyDeclaredPaths.join(", ")}. ${dirtyDeclaredPaths.map((path) => blockedPathDescriptions.get(path)).join(" ")}`
2358
2586
  );
2359
2587
  }
2360
2588
  beginMutationPathDeclarations(
2361
2589
  repository?.root ?? options.workspaceRoot,
2362
2590
  operationId,
2363
- baseline?.changed.keys()
2591
+ baseline?.changed.keys(),
2592
+ blockedPathDescriptions
2364
2593
  );
2365
2594
  for (const path of options.anticipatedPaths ?? []) declareMutationPath(path);
2366
2595
  if (policy.revisioning.mode === "commit-after-verify" && repository) {
@@ -2511,6 +2740,17 @@ var GovernedMutation = class _GovernedMutation {
2511
2740
  await this.lock.release();
2512
2741
  }
2513
2742
  };
2743
+ function dirtyRepositoryPaths(repositoryRoot, baseline, paths) {
2744
+ const conflicts = /* @__PURE__ */ new Set();
2745
+ for (const path of paths) {
2746
+ const absolute = isAbsolute6(path) ? resolve9(path) : resolve9(process.cwd(), path);
2747
+ const relativePath = relative3(repositoryRoot, absolute);
2748
+ if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep3}`) || isAbsolute6(relativePath)) continue;
2749
+ const normalized = relativePath.split(sep3).join("/");
2750
+ if (baseline.changed.has(normalized)) conflicts.add(normalized);
2751
+ }
2752
+ return [...conflicts].sort((left, right) => left.localeCompare(right));
2753
+ }
2514
2754
  var activeMutation;
2515
2755
  function activeMutationMetadata() {
2516
2756
  return activeMutation?.metadata();
@@ -2625,8 +2865,8 @@ async function recoverMutationRuntime(operationId, options = {}) {
2625
2865
  }
2626
2866
  mutation = GovernedMutation.activateExisting(receipt, baseline, lock);
2627
2867
  const [{ recoverPendingApply }, { readLinkedLocalApplyJournal: readLinkedLocalApplyJournal2, removeApplyJournal: removeApplyJournal2, localPathExists: localPathExists2 }] = await Promise.all([
2628
- import("./apply-OEKVIYBV.js"),
2629
- import("./transaction-D7VRZZX5.js")
2868
+ import("./apply-JBULQGW5.js"),
2869
+ import("./transaction-EPP7GHXD.js")
2630
2870
  ]);
2631
2871
  let missingReservation = false;
2632
2872
  for (const entry of pending) {
@@ -2714,10 +2954,10 @@ async function checkMutationProvider(workspaceRoot, globalRoot) {
2714
2954
  });
2715
2955
  return invokeRevisionProvider(policy.revisioning.provider, request, { workspaceRoot });
2716
2956
  }
2717
- async function mutationPolicyForWorkspace(workspaceRoot, globalRoot = homedir4()) {
2957
+ async function mutationPolicyForWorkspace(workspaceRoot, globalRoot = homedir5()) {
2718
2958
  const scopedConfig = await readWorkspaceConfig(workspaceRoot);
2719
2959
  const scoped = scopedConfig.schemaVersion === 4 ? scopedConfig.mutationPolicy : void 0;
2720
- if (resolve8(workspaceRoot) === resolve8(globalRoot)) return scoped;
2960
+ if (resolve9(workspaceRoot) === resolve9(globalRoot)) return scoped;
2721
2961
  const globalConfig = await readWorkspaceConfig(globalRoot);
2722
2962
  const global = globalConfig.schemaVersion === 4 ? globalConfig.mutationPolicy : void 0;
2723
2963
  return mergeMutationPolicies(global, scoped);
@@ -2790,17 +3030,17 @@ function upsertRuntimeJournal(entries, link2, status) {
2790
3030
  // src/install/transaction.ts
2791
3031
  function applyLockPath(targetRoot, adapter, scope = {}) {
2792
3032
  const installationType = scope.installationType ?? "local";
2793
- return join6(metadataDir(targetRoot), `${stateKeyFor(adapter, { installationType })}.runtime-apply-lock`);
3033
+ return join7(metadataDir(targetRoot), `${stateKeyFor(adapter, { installationType })}.runtime-apply-lock`);
2794
3034
  }
2795
3035
  function applyJournalPath(targetRoot, adapter, scope = {}) {
2796
- return join6(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-journal.json`);
3036
+ return join7(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-journal.json`);
2797
3037
  }
2798
3038
  function applyBackupDir(targetRoot, adapter, scope = {}) {
2799
- return join6(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-backups`);
3039
+ return join7(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-backups`);
2800
3040
  }
2801
3041
  async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}, scope = {}) {
2802
3042
  const lockPath = applyLockPath(targetRoot, adapter, scope);
2803
- const ownerPath = join6(lockPath, "owner.json");
3043
+ const ownerPath = join7(lockPath, "owner.json");
2804
3044
  const metadata = {
2805
3045
  pid: process.pid,
2806
3046
  adapter,
@@ -2864,7 +3104,7 @@ async function listApplyJournals(targetRoot, adapter, transport = localTransport
2864
3104
  const found = [];
2865
3105
  for (const fileName of await transport.listDir(metadataDir(targetRoot))) {
2866
3106
  if (!fileName.endsWith(suffix)) continue;
2867
- const path = join6(metadataDir(targetRoot), fileName);
3107
+ const path = join7(metadataDir(targetRoot), fileName);
2868
3108
  const journal = parseApplyJournal(JSON.parse(await transport.readFile(path)));
2869
3109
  if (journal.adapter !== adapter || (journal.installationType ?? "local") !== installationType) continue;
2870
3110
  found.push({ path, journal });
@@ -2876,15 +3116,15 @@ async function listAllApplyJournals(targetRoot, adapter, transport = localTransp
2876
3116
  const found = [];
2877
3117
  for (const fileName of await transport.listDir(metadataDir(targetRoot))) {
2878
3118
  if (!fileName.endsWith(suffix)) continue;
2879
- const path = join6(metadataDir(targetRoot), fileName);
3119
+ const path = join7(metadataDir(targetRoot), fileName);
2880
3120
  const journal = parseApplyJournal(JSON.parse(await transport.readFile(path)));
2881
3121
  if (journal.adapter === adapter) found.push({ path, journal });
2882
3122
  }
2883
3123
  return found.sort((a, b) => a.path.localeCompare(b.path));
2884
3124
  }
2885
3125
  async function readLinkedLocalApplyJournal(path, operationId, expectedDigest, expectedTransportDescription) {
2886
- const absolutePath = resolve9(path);
2887
- const journal = parseApplyJournal(JSON.parse(await readFile7(absolutePath, "utf8")));
3126
+ const absolutePath = resolve10(path);
3127
+ const journal = parseApplyJournal(JSON.parse(await readFile8(absolutePath, "utf8")));
2888
3128
  if (journal.version !== 2 || !journal.mutation) {
2889
3129
  throw new Error(`Runtime apply journal ${absolutePath} is not linked to a governed mutation.`);
2890
3130
  }
@@ -2902,7 +3142,7 @@ async function readLinkedLocalApplyJournal(path, operationId, expectedDigest, ex
2902
3142
  }) !== expectedDigest) {
2903
3143
  throw new Error(`Runtime apply journal ${absolutePath} digest does not match its durable receipt.`);
2904
3144
  }
2905
- const expectedPath = resolve9(applyJournalPath(journal.targetRoot, journal.adapter, {
3145
+ const expectedPath = resolve10(applyJournalPath(journal.targetRoot, journal.adapter, {
2906
3146
  installationType: journal.installationType,
2907
3147
  stateKey: journal.stateKey
2908
3148
  }));
@@ -2926,7 +3166,7 @@ async function abortApplyJournal(targetRoot, adapter, transport = localTransport
2926
3166
  const journalPath = applyJournalPath(targetRoot, adapter, scope);
2927
3167
  if (!await transport.pathExists(journalPath)) return void 0;
2928
3168
  const stateKey = stateKeyFor(adapter, scope);
2929
- const archivePath = join6(metadataDir(targetRoot), "archive", `${stateKey}.apply-journal.failed-${journalTimestamp(/* @__PURE__ */ new Date())}.json`);
3169
+ const archivePath = join7(metadataDir(targetRoot), "archive", `${stateKey}.apply-journal.failed-${journalTimestamp(/* @__PURE__ */ new Date())}.json`);
2930
3170
  const content = await transport.readFile(journalPath);
2931
3171
  const journal = parseApplyJournal(JSON.parse(content));
2932
3172
  await transport.writeFileAtomic(archivePath, content.endsWith("\n") ? content : `${content}
@@ -2958,7 +3198,7 @@ function assertGovernedRuntimeTransportSupported(transport) {
2958
3198
  }
2959
3199
  function applyJournalLinkDigest(journal, transport) {
2960
3200
  if (!journal.mutation) throw new Error("Cannot digest an unlinked runtime apply journal.");
2961
- return createHash5("sha256").update(canonicalJson3({
3201
+ return createHash6("sha256").update(canonicalJson3({
2962
3202
  version: journal.version,
2963
3203
  mutation: {
2964
3204
  operationId: journal.mutation.operationId,
@@ -2970,11 +3210,12 @@ function applyJournalLinkDigest(journal, transport) {
2970
3210
  adapter: journal.adapter,
2971
3211
  installationType: journal.installationType ?? null,
2972
3212
  stateKey: journal.stateKey ?? null,
2973
- targetRoot: resolve9(journal.targetRoot),
3213
+ targetRoot: resolve10(journal.targetRoot),
2974
3214
  baseRevision: journal.baseRevision,
2975
3215
  graphLockDigest: journal.graphLockDigest ?? null,
2976
3216
  graphLockPath: journal.graphLockPath ?? null,
2977
3217
  graphLockRemovePath: journal.graphLockRemovePath ?? null,
3218
+ stateRemoveKey: journal.stateRemoveKey ?? null,
2978
3219
  workspaceConfigPath: journal.workspaceConfigPath ?? null,
2979
3220
  operations: journal.operations
2980
3221
  })).digest("hex");
@@ -3041,7 +3282,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
3041
3282
  hadExisting
3042
3283
  };
3043
3284
  }
3044
- const backupPath = join6(applyBackupDir(targetRoot, adapter, scope), String(index));
3285
+ const backupPath = join7(applyBackupDir(targetRoot, adapter, scope), String(index));
3045
3286
  await rm4(backupPath, { recursive: true, force: true });
3046
3287
  await mkdir3(dirname5(backupPath), { recursive: true });
3047
3288
  await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });