agentwheel 0.22.1 → 0.22.3

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 CHANGED
@@ -797,11 +797,11 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
797
797
  onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
798
798
  ensurePropertyComplete(offset + length);
799
799
  },
800
- onSeparator: (sep12, offset, length) => {
800
+ onSeparator: (sep13, offset, length) => {
801
801
  if (currentParent.type === "property") {
802
- if (sep12 === ":") {
802
+ if (sep13 === ":") {
803
803
  currentParent.colonOffset = offset;
804
- } else if (sep12 === ",") {
804
+ } else if (sep13 === ",") {
805
805
  ensurePropertyComplete(offset);
806
806
  }
807
807
  }
@@ -14730,14 +14730,14 @@ var init_artifact = __esm({
14730
14730
  include: external_exports.string().min(1),
14731
14731
  markers: external_exports.boolean().optional(),
14732
14732
  optional: external_exports.boolean().optional()
14733
- });
14733
+ }).strict();
14734
14734
  packageCompositionRuleSchema = external_exports.object({
14735
14735
  target: external_exports.string().min(1),
14736
14736
  include: external_exports.string().min(1),
14737
14737
  exclude: external_exports.array(external_exports.string().min(1)).optional(),
14738
14738
  runtimes: external_exports.array(external_exports.string().min(1)).optional(),
14739
14739
  markers: external_exports.boolean().optional()
14740
- });
14740
+ }).strict();
14741
14741
  packageSupersedesEntrySchema = external_exports.object({
14742
14742
  package: external_exports.string().min(1),
14743
14743
  selector: external_exports.string().min(1),
@@ -18513,7 +18513,7 @@ var init_fleet = __esm({
18513
18513
  // src/mutation/repository.ts
18514
18514
  import { createHash as createHash8 } from "crypto";
18515
18515
  import { lstat as lstat3, readFile as readFile12, readlink, readdir as readdir5 } from "fs/promises";
18516
- import { posix as posix4, relative as relative7, resolve as resolve15, sep as sep5 } from "path";
18516
+ import { dirname as dirname10, posix as posix4, relative as relative7, resolve as resolve15, sep as sep5 } from "path";
18517
18517
  async function discoverGitRepository(start) {
18518
18518
  const probe = await runProcess("git", ["-C", start, "rev-parse", "--show-toplevel"], { allowExitCodes: [0, 128] });
18519
18519
  if (probe.exitCode !== 0) return void 0;
@@ -18540,6 +18540,9 @@ async function assertGitPreflight(repository) {
18540
18540
  }
18541
18541
  }
18542
18542
  async function assertHeadLease(repositoryRoot, expectedHead, head, paths, provider) {
18543
+ return assertHeadLeaseWithRecovery(repositoryRoot, expectedHead, head, paths, provider, []);
18544
+ }
18545
+ async function assertHeadLeaseWithRecovery(repositoryRoot, expectedHead, head, paths, provider, recoveryPaths, terminalChanges = /* @__PURE__ */ new Map()) {
18543
18546
  if (head === expectedHead) return;
18544
18547
  if (provider?.headLease !== "path-scoped" || paths.length === 0) {
18545
18548
  throw new Error(`Git HEAD lease mismatch: expected ${expectedHead}, found ${head}.`);
@@ -18575,10 +18578,65 @@ async function assertHeadLease(repositoryRoot, expectedHead, head, paths, provid
18575
18578
  "-z",
18576
18579
  commit
18577
18580
  ])).stdout);
18578
- if (touched.some((path) => protectedPaths.some((protectedPath) => path === protectedPath || path.startsWith(`${protectedPath}/`) || protectedPath.startsWith(`${path}/`)))) {
18579
- throw new Error(`Git HEAD lease overlaps protected paths in intervening commit ${commit}.`);
18581
+ for (const path of touched) {
18582
+ if (!protectedPaths.some((protectedPath) => overlaps(path, protectedPath))) continue;
18583
+ if (terminalChanges.get(commit)?.has(path)) continue;
18584
+ const proof = recoveryPaths.find((entry) => entry.path === path && paths.includes(path));
18585
+ if (!proof || overlaps(path, ".agentwheel/config.json") || overlaps(path, ".syncwheel/manifest.json") || await gitPathMode(repositoryRoot, commit, path) !== "100644" || await hashHeadPath(repositoryRoot, expectedHead, path) !== proof.beforeSha256 || await hashHeadPath(repositoryRoot, commit, path) !== proof.afterSha256 || await hashHeadPath(repositoryRoot, head, path) !== proof.afterSha256 || await hashWorkingPath(repositoryRoot, path) !== proof.afterSha256) {
18586
+ throw new Error(`Git HEAD lease overlaps protected paths in intervening commit ${commit}.`);
18587
+ }
18588
+ }
18589
+ }
18590
+ }
18591
+ function overlaps(left, right) {
18592
+ return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
18593
+ }
18594
+ async function committedRecoveryPaths(repositoryRoot, expectedHead, head, declarations, receiptPaths, provider) {
18595
+ await verifyRevisionPaths(repositoryRoot, expectedHead, receiptPaths);
18596
+ const committed = [];
18597
+ for (const entry of receiptPaths) {
18598
+ if (!declarations.includes(entry.path)) throw new Error(`Recovery path was not declared: ${entry.path}.`);
18599
+ if (await hashHeadPath(repositoryRoot, head, entry.path) === entry.afterSha256 && entry.beforeSha256 !== entry.afterSha256) {
18600
+ await assertRecoveryFileIdentity(repositoryRoot, expectedHead, head, entry.path);
18601
+ committed.push(entry);
18602
+ }
18603
+ }
18604
+ await assertHeadLeaseWithRecovery(repositoryRoot, expectedHead, head, declarations, provider, committed);
18605
+ return committed;
18606
+ }
18607
+ async function verifyCompletedRecoveryPaths(repositoryRoot, expectedHead, declarations, savedPaths, provider, productCommitSha, productPaths, controlCommitSha) {
18608
+ const head = await currentHead(repositoryRoot);
18609
+ await verifyRevisionPaths(repositoryRoot, expectedHead, savedPaths);
18610
+ for (const entry of savedPaths) {
18611
+ await assertRecoveryFileIdentity(repositoryRoot, expectedHead, head, entry.path);
18612
+ if (await hashHeadPath(repositoryRoot, head, entry.path) !== entry.afterSha256) {
18613
+ throw new Error(`Committed recovery path changed during provider completion: ${entry.path}.`);
18580
18614
  }
18581
18615
  }
18616
+ const terminalChanges = /* @__PURE__ */ new Map();
18617
+ if (productCommitSha) terminalChanges.set(productCommitSha, new Set(productPaths));
18618
+ if (controlCommitSha) terminalChanges.set(controlCommitSha, /* @__PURE__ */ new Set([".syncwheel/manifest.json"]));
18619
+ await assertHeadLeaseWithRecovery(repositoryRoot, expectedHead, head, declarations, provider, savedPaths, terminalChanges);
18620
+ }
18621
+ async function gitPathMode(repositoryRoot, head, path) {
18622
+ const tree = (await runProcess("git", ["-C", repositoryRoot, "ls-tree", "-z", head, "--", path])).stdout.toString("utf8");
18623
+ const entry = tree.split("\0").find((value) => value.slice(value.indexOf(" ") + 1) === path);
18624
+ return entry?.split(" ")[0];
18625
+ }
18626
+ async function assertRecoveryFileIdentity(repositoryRoot, expectedHead, head, path) {
18627
+ if (await gitPathMode(repositoryRoot, expectedHead, path) !== "100644" || await gitPathMode(repositoryRoot, head, path) !== "100644") {
18628
+ throw new Error(`Recovery requires an existing regular non-executable Git file: ${path}.`);
18629
+ }
18630
+ const absolute = resolve15(repositoryRoot, ...path.split("/"));
18631
+ const stats = await lstat3(absolute);
18632
+ if (!stats.isFile() || (stats.mode & 73) !== 0) throw new Error(`Recovery file identity changed: ${path}.`);
18633
+ let ancestor = dirname10(absolute);
18634
+ while (true) {
18635
+ if ((await lstat3(ancestor)).isSymbolicLink()) throw new Error(`Recovery symlink ancestor is unsupported: ${path}.`);
18636
+ const parent = dirname10(ancestor);
18637
+ if (parent === ancestor) break;
18638
+ ancestor = parent;
18639
+ }
18582
18640
  }
18583
18641
  function isAgentwheelWritablePath(path) {
18584
18642
  return /^\.agentwheel(?:\/|$)/u.test(path) || /^profiles\/[^/]+\/\.agentwheel(?:\/|$)/u.test(path);
@@ -19279,7 +19337,7 @@ var init_providers = __esm({
19279
19337
  // src/mutation/coordinator.ts
19280
19338
  import { randomUUID as randomUUID2 } from "crypto";
19281
19339
  import { homedir as homedir6 } from "os";
19282
- import { dirname as dirname10, isAbsolute as isAbsolute12, relative as relative8, resolve as resolve17, sep as sep6 } from "path";
19340
+ import { dirname as dirname11, isAbsolute as isAbsolute12, relative as relative8, resolve as resolve17, sep as sep6 } from "path";
19283
19341
  function dirtyRepositoryPaths(repositoryRoot, baseline, paths) {
19284
19342
  const conflicts = /* @__PURE__ */ new Set();
19285
19343
  for (const path of paths) {
@@ -19353,12 +19411,15 @@ async function resumeMutation(operationId, action) {
19353
19411
  );
19354
19412
  receipt = await updateMutationReceipt(receipt, { paths, status: "mutation-applied" });
19355
19413
  }
19356
- await invokeRevisionProvider(provider, providerRequest(receipt, "preflight", paths), {
19414
+ const request = action === "recover" && receipt.runtimeJournals.length > 0 ? await recoveryProviderRequest(receipt, paths) : providerRequest(receipt, action, paths);
19415
+ await invokeRevisionProvider(provider, revisionProviderRequestSchema.parse({ ...request, action: "preflight" }), {
19357
19416
  workspaceRoot: receipt.workspaceRoot
19358
19417
  });
19359
- const response = await invokeRevisionProvider(provider, providerRequest(receipt, action, paths), {
19418
+ if (action === "recover") await verifyRecoveryProjection(receipt, request);
19419
+ const response = await invokeRevisionProvider(provider, request, {
19360
19420
  workspaceRoot: receipt.workspaceRoot
19361
19421
  });
19422
+ await verifyCompletedRecoveryProjection(receipt, request, response);
19362
19423
  return updateMutationReceipt(receipt, {
19363
19424
  status: receiptStatusForProvider(response.status),
19364
19425
  providerResponse: response,
@@ -19401,17 +19462,19 @@ async function recoverMutationRuntime(operationId, options = {}) {
19401
19462
  `Mutation '${receipt.operationId}' repository HEAD lease changed before runtime recovery.`
19402
19463
  );
19403
19464
  }
19404
- await assertHeadLease(
19465
+ if (receipt.noCommit) await assertHeadLease(receipt.repositoryRoot, receipt.expectedHead, repository.head, [], receipt.provider);
19466
+ else await committedRecoveryPaths(
19405
19467
  receipt.repositoryRoot,
19406
19468
  receipt.expectedHead,
19407
19469
  repository.head,
19408
- receipt.noCommit ? [] : declarations,
19470
+ declarations,
19471
+ receipt.paths,
19409
19472
  receipt.provider
19410
19473
  );
19411
19474
  await assertGitPreflight(repository);
19412
19475
  await collectIntroducedPaths(receipt.repositoryRoot, receipt.expectedHead, baseline, declarations);
19413
19476
  }
19414
- mutation = GovernedMutation.activateExisting(receipt, baseline, lock);
19477
+ mutation = GovernedMutation.activateExisting(receipt, baseline, lock, options.beforeProviderCompletion);
19415
19478
  const [{ recoverPendingApply: recoverPendingApply2 }, { readLinkedLocalApplyJournal: readLinkedLocalApplyJournal2 }] = await Promise.all([
19416
19479
  Promise.resolve().then(() => (init_apply(), apply_exports)),
19417
19480
  Promise.resolve().then(() => (init_transaction(), transaction_exports))
@@ -19523,7 +19586,7 @@ async function mutationPolicyForWorkspace(workspaceRoot, globalRoot = homedir6()
19523
19586
  const globalConfig2 = await readWorkspaceConfig(globalRoot);
19524
19587
  const global = globalConfig2.schemaVersion === 4 ? globalConfig2.mutationPolicy : void 0;
19525
19588
  let existingRoot = resolve17(workspaceRoot);
19526
- while (!await pathExists(existingRoot) && dirname10(existingRoot) !== existingRoot) existingRoot = dirname10(existingRoot);
19589
+ while (!await pathExists(existingRoot) && dirname11(existingRoot) !== existingRoot) existingRoot = dirname11(existingRoot);
19527
19590
  const owner = await resolveWorkspaceOwnershipScope(existingRoot, { globalRoot });
19528
19591
  const fleetConfig = owner.fleetId ? await readWorkspaceConfig(owner.root) : void 0;
19529
19592
  const fleet = fleetConfig?.schemaVersion === 4 ? fleetConfig.mutationPolicy : void 0;
@@ -19545,6 +19608,50 @@ function mergeMutationPolicies(global, scoped) {
19545
19608
  revisioning
19546
19609
  });
19547
19610
  }
19611
+ async function recoveryProviderRequest(receipt, paths) {
19612
+ const request = providerRequest(receipt, "recover", paths);
19613
+ if (receipt.noCommit) return request;
19614
+ const head = await currentHead(request.repositoryRoot);
19615
+ const committed = await committedRecoveryPaths(
19616
+ request.repositoryRoot,
19617
+ request.expectedHead,
19618
+ head,
19619
+ readMutationPathDeclarations(receipt.operationId),
19620
+ paths,
19621
+ receipt.provider
19622
+ );
19623
+ if (committed.length === 0) return request;
19624
+ const saved = new Set(committed.map((entry) => entry.path));
19625
+ return revisionProviderRequestSchema.parse({
19626
+ ...request,
19627
+ expectedHead: head,
19628
+ paths: paths.filter((entry) => !saved.has(entry.path))
19629
+ });
19630
+ }
19631
+ async function verifyCompletedRecoveryProjection(receipt, request, response) {
19632
+ if (request.expectedHead === receipt.expectedHead) return;
19633
+ if (response.action !== "recover" && response.action !== "finalize") throw new Error("Recovery requires a terminal provider response.");
19634
+ const remaining = new Set(request.paths.map((entry) => entry.path));
19635
+ await verifyCompletedRecoveryPaths(
19636
+ request.repositoryRoot,
19637
+ receipt.expectedHead,
19638
+ readMutationPathDeclarations(receipt.operationId),
19639
+ receipt.paths.filter((entry) => !remaining.has(entry.path)),
19640
+ receipt.provider,
19641
+ response.productCommitSha,
19642
+ request.paths.map((entry) => entry.path),
19643
+ response.controlCommitSha
19644
+ );
19645
+ }
19646
+ async function verifyRecoveryProjection(receipt, request) {
19647
+ if (request.expectedHead === receipt.expectedHead) return;
19648
+ if (await currentHead(request.repositoryRoot) !== request.expectedHead) throw new Error("Recovery projection HEAD changed.");
19649
+ const verified = await recoveryProviderRequest(receipt, receipt.paths);
19650
+ if (JSON.stringify(verified) !== JSON.stringify(request)) throw new Error("Recovery projection changed before provider completion.");
19651
+ const repository = await discoverGitRepository(request.repositoryRoot);
19652
+ if (!repository) throw new Error("Recovery repository disappeared.");
19653
+ await assertGitPreflight(repository);
19654
+ }
19548
19655
  function providerRequest(receipt, action, paths) {
19549
19656
  if (!receipt.repositoryRoot || !receipt.expectedHead || !receipt.provider) {
19550
19657
  throw new Error(`Mutation '${receipt.operationId}' is missing revision provider request state.`);
@@ -19614,17 +19721,21 @@ var init_coordinator = __esm({
19614
19721
  init_repository();
19615
19722
  init_session_ownership();
19616
19723
  GovernedMutation = class _GovernedMutation {
19617
- constructor(policy, receipt, baseline, lock) {
19724
+ constructor(policy, receipt, baseline, lock, runtimeRecovery = false, beforeProviderCompletion) {
19618
19725
  this.policy = policy;
19619
19726
  this.receipt = receipt;
19620
19727
  this.baseline = baseline;
19621
19728
  this.lock = lock;
19729
+ this.runtimeRecovery = runtimeRecovery;
19730
+ this.beforeProviderCompletion = beforeProviderCompletion;
19622
19731
  this.operationId = receipt.operationId;
19623
19732
  }
19624
19733
  policy;
19625
19734
  receipt;
19626
19735
  baseline;
19627
19736
  lock;
19737
+ runtimeRecovery;
19738
+ beforeProviderCompletion;
19628
19739
  operationId;
19629
19740
  closed = false;
19630
19741
  static async begin(options) {
@@ -19730,7 +19841,7 @@ var init_coordinator = __esm({
19730
19841
  );
19731
19842
  }
19732
19843
  this.receipt = await updateMutationReceipt(this.receipt, { status: "handler-succeeded" });
19733
- paths = await this.collectPaths();
19844
+ paths = await this.collectPaths(action === "recover" && this.runtimeRecovery);
19734
19845
  this.receipt = await updateMutationReceipt(this.receipt, { paths, status: "mutation-applied" });
19735
19846
  } catch (error46) {
19736
19847
  this.receipt = await updateMutationReceipt(this.receipt, {
@@ -19748,12 +19859,16 @@ var init_coordinator = __esm({
19748
19859
  return this.receipt;
19749
19860
  }
19750
19861
  const provider = this.policy.revisioning.provider;
19751
- await invokeRevisionProvider(provider, providerRequest(this.receipt, "preflight", paths), {
19862
+ const request = this.runtimeRecovery && action === "recover" ? await recoveryProviderRequest(this.receipt, paths) : providerRequest(this.receipt, action, paths);
19863
+ await invokeRevisionProvider(provider, revisionProviderRequestSchema.parse({ ...request, action: "preflight" }), {
19752
19864
  workspaceRoot: this.receipt.workspaceRoot
19753
19865
  });
19754
- const response = await invokeRevisionProvider(provider, providerRequest(this.receipt, action, paths), {
19866
+ if (this.runtimeRecovery) await verifyRecoveryProjection(this.receipt, request);
19867
+ await this.beforeProviderCompletion?.();
19868
+ const response = await invokeRevisionProvider(provider, request, {
19755
19869
  workspaceRoot: this.receipt.workspaceRoot
19756
19870
  });
19871
+ await verifyCompletedRecoveryProjection(this.receipt, request, response);
19757
19872
  this.receipt = await updateMutationReceipt(this.receipt, {
19758
19873
  status: receiptStatusForProvider(response.status),
19759
19874
  providerResponse: response
@@ -19813,7 +19928,7 @@ var init_coordinator = __esm({
19813
19928
  const runtimeJournals = upsertRuntimeJournal(this.receipt.runtimeJournals, existing, status);
19814
19929
  this.receipt = await updateMutationReceipt(this.receipt, { runtimeJournals });
19815
19930
  }
19816
- static activateExisting(receipt, baseline, lock) {
19931
+ static activateExisting(receipt, baseline, lock, beforeProviderCompletion) {
19817
19932
  if (receipt.revisionMode === "commit-after-verify" && !receipt.provider) {
19818
19933
  throw new Error(`Mutation '${receipt.operationId}' is missing its revision provider recovery policy.`);
19819
19934
  }
@@ -19831,24 +19946,41 @@ var init_coordinator = __esm({
19831
19946
  journal: "required",
19832
19947
  revisioning: { mode: "off" }
19833
19948
  });
19834
- const mutation = new _GovernedMutation(policy, receipt, baseline, lock);
19949
+ const mutation = new _GovernedMutation(policy, receipt, baseline, lock, true, beforeProviderCompletion);
19835
19950
  activeMutation = mutation;
19836
19951
  return mutation;
19837
19952
  }
19838
- async collectPaths() {
19953
+ async collectPaths(recover = false) {
19839
19954
  if (!this.receipt.repositoryRoot || !this.receipt.expectedHead || !this.baseline) return [];
19840
19955
  const declarations = [.../* @__PURE__ */ new Set([
19841
19956
  ...declaredMutationPaths(),
19842
19957
  ...readMutationPathDeclarations(this.receipt.operationId)
19843
19958
  ])].sort((a, b) => a.localeCompare(b));
19844
- if (this.receipt.provider) await assertHeadLease(
19959
+ const head = await currentHead(this.receipt.repositoryRoot);
19960
+ const committed = recover && !this.receipt.noCommit ? await committedRecoveryPaths(
19845
19961
  this.receipt.repositoryRoot,
19846
19962
  this.receipt.expectedHead,
19847
- await currentHead(this.receipt.repositoryRoot),
19963
+ head,
19964
+ declarations,
19965
+ this.receipt.paths,
19966
+ this.receipt.provider
19967
+ ) : [];
19968
+ if (this.receipt.provider && !recover) await assertHeadLease(
19969
+ this.receipt.repositoryRoot,
19970
+ this.receipt.expectedHead,
19971
+ head,
19848
19972
  this.receipt.noCommit ? [] : declarations,
19849
19973
  this.receipt.provider
19850
19974
  );
19851
- return collectIntroducedPaths(this.receipt.repositoryRoot, this.receipt.expectedHead, this.baseline, declarations);
19975
+ if (recover && this.receipt.noCommit) await assertHeadLease(
19976
+ this.receipt.repositoryRoot,
19977
+ this.receipt.expectedHead,
19978
+ head,
19979
+ [],
19980
+ this.receipt.provider
19981
+ );
19982
+ const introduced = await collectIntroducedPaths(this.receipt.repositoryRoot, this.receipt.expectedHead, this.baseline, declarations);
19983
+ return [...committed, ...introduced].sort((a, b) => a.path.localeCompare(b.path));
19852
19984
  }
19853
19985
  async close() {
19854
19986
  if (this.closed) return;
@@ -19864,12 +19996,12 @@ var init_coordinator = __esm({
19864
19996
  // src/transport/ownership-snapshot.ts
19865
19997
  import { createHash as createHash10 } from "crypto";
19866
19998
  import { lstat as lstat4, readdir as readdir6, readFile as readFile14 } from "fs/promises";
19867
- import { dirname as dirname11, join as join14, relative as relative9, resolve as resolve18 } from "path";
19999
+ import { dirname as dirname12, join as join14, relative as relative9, resolve as resolve18 } from "path";
19868
20000
  async function assertSnapshotAncestors(path) {
19869
20001
  let current = resolve18(path);
19870
20002
  while (true) {
19871
20003
  if ((await lstat4(current)).isSymbolicLink()) throw new Error(`Symlink path is unsupported: ${current}`);
19872
- const parent = dirname11(current);
20004
+ const parent = dirname12(current);
19873
20005
  if (parent === current) return;
19874
20006
  current = parent;
19875
20007
  }
@@ -19935,7 +20067,7 @@ __export(transaction_exports, {
19935
20067
  });
19936
20068
  import { createHash as createHash11 } from "crypto";
19937
20069
  import { cp as cp2, lstat as lstat5, mkdir as mkdir7, open as open4, rm as rm5, stat as stat4 } from "fs/promises";
19938
- import { dirname as dirname12, join as join15, relative as relative10, resolve as resolve19, sep as sep7 } from "path";
20070
+ import { dirname as dirname13, join as join15, relative as relative10, resolve as resolve19, sep as sep7 } from "path";
19939
20071
  async function assertNoSymlinkPath2(path) {
19940
20072
  const absolute = resolve19(path);
19941
20073
  let current = absolute;
@@ -19945,7 +20077,7 @@ async function assertNoSymlinkPath2(path) {
19945
20077
  } catch (error46) {
19946
20078
  if (error46.code !== "ENOENT") throw error46;
19947
20079
  }
19948
- const parent = dirname12(current);
20080
+ const parent = dirname13(current);
19949
20081
  if (parent === current) break;
19950
20082
  current = parent;
19951
20083
  }
@@ -20016,7 +20148,7 @@ async function syncMetadataPath(path, directoryOnly = false) {
20016
20148
  await file2.close();
20017
20149
  }
20018
20150
  }
20019
- const directory = await open4(directoryOnly ? path : dirname12(path), "r");
20151
+ const directory = await open4(directoryOnly ? path : dirname13(path), "r");
20020
20152
  try {
20021
20153
  await directory.sync();
20022
20154
  } finally {
@@ -20166,7 +20298,7 @@ async function removeApplyJournal(targetRoot, adapter, transport = localTranspor
20166
20298
  if (existing?.mutation) await beginResolveMutationRuntimeJournal(existing.mutation.operationId, path);
20167
20299
  await transport.rm(applyJournalPath(targetRoot, adapter, scope));
20168
20300
  await transport.rm(applyBackupDir(targetRoot, adapter, scope));
20169
- if ((existing?.metadataAudit || existing?.metadataGuards) && transport.kind === "local") await syncMetadataPath(dirname12(path), true);
20301
+ if ((existing?.metadataAudit || existing?.metadataGuards) && transport.kind === "local") await syncMetadataPath(dirname13(path), true);
20170
20302
  if (existing?.mutation) await resolveMutationRuntimeJournal(existing.mutation.operationId, path);
20171
20303
  }
20172
20304
  async function abortApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
@@ -20317,7 +20449,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
20317
20449
  await transport.copyWithin(operation.destPath, backupPath, operation.kind);
20318
20450
  } else {
20319
20451
  await rm5(backupPath, { recursive: true, force: true });
20320
- await mkdir7(dirname12(backupPath), { recursive: true });
20452
+ await mkdir7(dirname13(backupPath), { recursive: true });
20321
20453
  await cp2(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
20322
20454
  }
20323
20455
  return {
@@ -20415,20 +20547,54 @@ var init_transaction = __esm({
20415
20547
 
20416
20548
  // src/install/toml-merge.ts
20417
20549
  import { mkdir as mkdir8, readFile as readFile16, writeFile as writeFile8 } from "fs/promises";
20418
- import { dirname as dirname13 } from "path";
20550
+ import { parse as parseToml } from "smol-toml";
20551
+ import { dirname as dirname14 } from "path";
20419
20552
  async function mergeCodexTomlMcp(sourcePath, destPath) {
20420
20553
  const source = JSON.parse(await readFile16(sourcePath, "utf8"));
20421
20554
  const servers = extractMcpServers(source);
20422
20555
  const current = await pathExists(destPath) ? await readFile16(destPath, "utf8") : "";
20423
20556
  const withoutManaged = removeManagedMcpSections(current, Object.keys(servers));
20424
20557
  const merged = appendMcpServers(withoutManaged, servers);
20425
- await mkdir8(dirname13(destPath), { recursive: true });
20558
+ await mkdir8(dirname14(destPath), { recursive: true });
20426
20559
  await writeFile8(destPath, merged, "utf8");
20427
20560
  }
20428
20561
  function mismatchedCodexTomlMcpServers(source, currentContent) {
20429
20562
  const servers = extractMcpServers(source);
20430
20563
  return Object.entries(servers).filter(([name, server]) => extractMcpServerBlock(currentContent, name) !== formatMcpServer(name, server)).map(([name]) => name);
20431
20564
  }
20565
+ function mismatchedManagedCodexTomlMcpServers(source, currentContent) {
20566
+ const servers = extractMcpServers(source);
20567
+ let current;
20568
+ try {
20569
+ const parsed = parseToml(currentContent, { integersAsBigInt: "asNeeded" });
20570
+ current = isPlainRecord(parsed.mcp_servers) ? parsed.mcp_servers : void 0;
20571
+ } catch {
20572
+ return Object.keys(servers);
20573
+ }
20574
+ return Object.entries(servers).filter(([name, server]) => {
20575
+ try {
20576
+ const rendered = parseToml(formatMcpServer(name, server), { integersAsBigInt: "asNeeded" });
20577
+ const expected = isPlainRecord(rendered.mcp_servers) ? rendered.mcp_servers[name] : void 0;
20578
+ return !current || !Object.hasOwn(current, name) || !managedValueMatches(current[name], expected);
20579
+ } catch {
20580
+ return true;
20581
+ }
20582
+ }).map(([name]) => name);
20583
+ }
20584
+ function managedValueMatches(actual, expected) {
20585
+ if (Array.isArray(expected)) {
20586
+ return Array.isArray(actual) && actual.length === expected.length && expected.every((value, index) => managedValueMatches(actual[index], value));
20587
+ }
20588
+ if (isPlainRecord(expected)) {
20589
+ return isPlainRecord(actual) && Object.entries(expected).every(([key, value]) => Object.hasOwn(actual, key) && managedValueMatches(actual[key], value));
20590
+ }
20591
+ return actual === expected;
20592
+ }
20593
+ function isPlainRecord(value) {
20594
+ if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
20595
+ const prototype = Object.getPrototypeOf(value);
20596
+ return prototype === null || prototype === Object.prototype;
20597
+ }
20432
20598
  function extractMcpServers(source) {
20433
20599
  const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
20434
20600
  const servers = {};
@@ -20539,13 +20705,13 @@ var init_toml_merge = __esm({
20539
20705
 
20540
20706
  // src/install/yaml-merge.ts
20541
20707
  import { mkdir as mkdir9, readFile as readFile17, writeFile as writeFile9 } from "fs/promises";
20542
- import { dirname as dirname14 } from "path";
20708
+ import { dirname as dirname15 } from "path";
20543
20709
  import { parse as parse7, stringify } from "yaml";
20544
20710
  async function mergeYamlFile(sourcePath, destPath) {
20545
20711
  const source = parseYamlValue(await readFile17(sourcePath, "utf8"));
20546
20712
  const current = await pathExists(destPath) ? parseYamlValue(await readFile17(destPath, "utf8")) : {};
20547
20713
  const merged = deepMerge2(current, source);
20548
- await mkdir9(dirname14(destPath), { recursive: true });
20714
+ await mkdir9(dirname15(destPath), { recursive: true });
20549
20715
  await writeFile9(destPath, stringify(merged), "utf8");
20550
20716
  }
20551
20717
  function parseYamlValue(content) {
@@ -20604,7 +20770,7 @@ var init_yaml_merge = __esm({
20604
20770
 
20605
20771
  // src/install/merge-removal.ts
20606
20772
  import { mkdir as mkdir10, readFile as readFile18, writeFile as writeFile10 } from "fs/promises";
20607
- import { dirname as dirname15 } from "path";
20773
+ import { dirname as dirname16 } from "path";
20608
20774
  import { parse as parse8, stringify as stringify2 } from "yaml";
20609
20775
  function assertExactMergeContribution(contribution, strategy, currentContent) {
20610
20776
  if (strategy === "codex-toml-mcp") {
@@ -20617,8 +20783,18 @@ function assertExactMergeContribution(contribution, strategy, currentContent) {
20617
20783
  throw new MergeAdoptionMismatchError(`exact merge contribution differs or is missing at ${mismatch}`);
20618
20784
  }
20619
20785
  }
20786
+ function assertManagedMergeContribution(contribution, strategy, currentContent) {
20787
+ if (strategy !== "codex-toml-mcp") {
20788
+ assertExactMergeContribution(contribution, strategy, currentContent);
20789
+ return;
20790
+ }
20791
+ const mismatched = mismatchedManagedCodexTomlMcpServers(contribution, currentContent);
20792
+ if (mismatched.length > 0) {
20793
+ throw new MergeAdoptionMismatchError(`managed MCP fields differ or are missing for ${mismatched.join(", ")}`);
20794
+ }
20795
+ }
20620
20796
  async function assertMergedSourceContribution(sourcePath, strategy, currentContent) {
20621
- assertExactMergeContribution(await readMergeSource(sourcePath, strategy), strategy, currentContent);
20797
+ assertManagedMergeContribution(await readMergeSource(sourcePath, strategy), strategy, currentContent);
20622
20798
  }
20623
20799
  function assertExactMcpMergeContribution(removal, strategy, currentContent) {
20624
20800
  if (strategy === "codex-toml-mcp") {
@@ -20679,7 +20855,7 @@ async function removeMergeContribution(destPath, strategy, removal) {
20679
20855
  }
20680
20856
  const current = parseMergeDestination(content, strategy);
20681
20857
  removeIntroducedContent(current, removal);
20682
- await mkdir10(dirname15(destPath), { recursive: true });
20858
+ await mkdir10(dirname16(destPath), { recursive: true });
20683
20859
  await writeFile10(destPath, strategy === "yaml-deep" ? stringify2(current) : `${JSON.stringify(current, null, 2)}
20684
20860
  `, "utf8");
20685
20861
  }
@@ -20949,7 +21125,7 @@ var init_desired = __esm({
20949
21125
  import { createHash as createHash12 } from "crypto";
20950
21126
  import { mkdtemp as mkdtemp2, readFile as readFile19, realpath as realpath2, rm as rm6, writeFile as writeFile11 } from "fs/promises";
20951
21127
  import { tmpdir as tmpdir3 } from "os";
20952
- import { basename as basename2, dirname as dirname16, join as join16, relative as relative11 } from "path";
21128
+ import { basename as basename2, dirname as dirname17, join as join16, relative as relative11 } from "path";
20953
21129
  function exactManagedInstructionBlockSnapshot(content, selector) {
20954
21130
  const beginCount = [...content.matchAll(new RegExp(`<!-- BEGIN openpack:include ${escapeRegex2(selector)}(?=\\s|-->)`, "gu"))].length;
20955
21131
  const endCount = [...content.matchAll(new RegExp(`<!-- END openpack:include ${escapeRegex2(selector)}(?=\\s|-->)`, "gu"))].length;
@@ -21132,7 +21308,7 @@ function assertCleanBlock(block, selector) {
21132
21308
  }
21133
21309
  }
21134
21310
  function referencesAgentsMd(content, claudePath, agentsPath) {
21135
- const claudeDir = dirname16(claudePath);
21311
+ const claudeDir = dirname17(claudePath);
21136
21312
  for (const rawLine of content.split(/\r?\n/)) {
21137
21313
  const line = rawLine.trim();
21138
21314
  const atImport = /^@import\s+(.+)$/i.exec(line);
@@ -21142,7 +21318,7 @@ function referencesAgentsMd(content, claudePath, agentsPath) {
21142
21318
  const cleaned = referenced.trim().replace(/^["']|["']$/g, "");
21143
21319
  if (!/AGENTS\.md$/i.test(cleaned)) continue;
21144
21320
  const candidate = cleaned.startsWith("/") ? cleaned : join16(claudeDir, cleaned);
21145
- if (relative11(dirname16(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
21321
+ if (relative11(dirname17(agentsPath), candidate).replaceAll("\\", "/") === "AGENTS.md") return true;
21146
21322
  if (candidate === agentsPath) return true;
21147
21323
  }
21148
21324
  return false;
@@ -21202,7 +21378,7 @@ import { execFile as execFile5 } from "child_process";
21202
21378
  import { createHash as createHash13, randomUUID as randomUUID3 } from "crypto";
21203
21379
  import { chmod as chmod3, link as link2, lstat as lstat6, mkdir as mkdir11, mkdtemp as mkdtemp3, open as open5, readFile as readFile20, rm as rm7, writeFile as writeFile12 } from "fs/promises";
21204
21380
  import { tmpdir as tmpdir4 } from "os";
21205
- import { basename as basename3, dirname as dirname17, join as join17, resolve as resolve20 } from "path";
21381
+ import { basename as basename3, dirname as dirname18, join as join17, resolve as resolve20 } from "path";
21206
21382
  import { promisify as promisify5 } from "util";
21207
21383
  async function applyInstallPlan(plan, sourceLock, options = {}) {
21208
21384
  return applyPlanTransactionally(plan, { ...options, sourceLock });
@@ -21670,7 +21846,7 @@ async function createPrivateAuditDirectory(path) {
21670
21846
  } catch (error46) {
21671
21847
  if (error46.code !== "ENOENT") throw error46;
21672
21848
  }
21673
- await createPrivateAuditDirectory(dirname17(path));
21849
+ await createPrivateAuditDirectory(dirname18(path));
21674
21850
  try {
21675
21851
  await mkdir11(path, { mode: 448 });
21676
21852
  } catch (error46) {
@@ -21681,14 +21857,14 @@ async function createPrivateAuditDirectory(path) {
21681
21857
  }
21682
21858
  await chmod3(path, 448);
21683
21859
  await syncMetadataPath(path, true);
21684
- await syncMetadataPath(dirname17(path), true);
21860
+ await syncMetadataPath(dirname18(path), true);
21685
21861
  }
21686
21862
  async function writeAuditFileExclusive(path, content, validateCallerPath) {
21687
21863
  await assertNoSymlinkPath2(path);
21688
- await createPrivateAuditDirectory(dirname17(path));
21864
+ await createPrivateAuditDirectory(dirname18(path));
21689
21865
  await assertNoSymlinkPath2(path);
21690
21866
  await validateCallerPath();
21691
- await syncMetadataPath(dirname17(dirname17(path)), true);
21867
+ await syncMetadataPath(dirname18(dirname18(path)), true);
21692
21868
  const temporary = `${path}.pending-${randomUUID3()}`;
21693
21869
  try {
21694
21870
  const handle = await open5(temporary, "wx", 384);
@@ -21704,7 +21880,7 @@ async function writeAuditFileExclusive(path, content, validateCallerPath) {
21704
21880
  } catch (error46) {
21705
21881
  if (error46.code !== "EEXIST" || await readFile20(path, "utf8") !== content) throw error46;
21706
21882
  }
21707
- const directory = await open5(dirname17(path), "r");
21883
+ const directory = await open5(dirname18(path), "r");
21708
21884
  try {
21709
21885
  await directory.sync();
21710
21886
  } finally {
@@ -21813,7 +21989,9 @@ async function applyOperation(operation, context) {
21813
21989
  if (!await transport.pathExists(operation.destPath)) {
21814
21990
  throw new Error(`Merge skip destination disappeared after planning: ${operation.relativeDestPath}`);
21815
21991
  }
21816
- assertExactMergeContribution(operation.mergeRemoval, operation.mergeStrategy, await transport.readFile(operation.destPath));
21992
+ const exactAdoption = operation.exactMergeAdoption === true || operation.reason === "force adopting exact unmanaged merge contribution" || operation.reason === "repairing exact incomplete merge ownership";
21993
+ const verify = exactAdoption ? assertExactMergeContribution : assertManagedMergeContribution;
21994
+ verify(operation.mergeRemoval, operation.mergeStrategy, await transport.readFile(operation.destPath));
21817
21995
  verifiedCurrentHash = await transport.hashPath(operation.destPath);
21818
21996
  } else if (!operation.semanticPlugin && !operation.programmaticOperation) {
21819
21997
  if (!await transport.pathExists(operation.destPath)) {
@@ -22361,7 +22539,7 @@ async function validateOpenClawConfig(configPath, destPath, transport) {
22361
22539
  if (!transport.execFile) {
22362
22540
  throw new Error(`Cannot validate OpenClaw config over ${transport.description}: transport does not support command execution.`);
22363
22541
  }
22364
- const openClawHome = dirname17(destPath);
22542
+ const openClawHome = dirname18(destPath);
22365
22543
  const bundledBin = join17(openClawHome, "npm", "node_modules", ".bin", "openclaw");
22366
22544
  const script = String.raw`
22367
22545
  set -euo pipefail
@@ -22448,7 +22626,7 @@ import { createHash as createHash32 } from "crypto";
22448
22626
  import { existsSync } from "fs";
22449
22627
  import { mkdir as mkdir28, mkdtemp as mkdtemp6, readFile as readFile45, realpath as realpath4, rm as rm16, writeFile as writeFile24 } from "fs/promises";
22450
22628
  import { homedir as homedir18, tmpdir as tmpdir7 } from "os";
22451
- import { basename as basename24, dirname as dirname41, isAbsolute as isAbsolute17, join as join66, relative as relative22, resolve as resolve48 } from "path";
22629
+ import { basename as basename24, dirname as dirname42, isAbsolute as isAbsolute17, join as join66, relative as relative22, resolve as resolve48, sep as sep12 } from "path";
22452
22630
  import { fileURLToPath as fileURLToPath4 } from "url";
22453
22631
  import { Command } from "commander";
22454
22632
 
@@ -22541,7 +22719,7 @@ import { basename as basename8, join as join28, relative as relative12 } from "p
22541
22719
  // src/staging/codex-subagents.ts
22542
22720
  init_fs();
22543
22721
  import { mkdir as mkdir12, readFile as readFile21, writeFile as writeFile13 } from "fs/promises";
22544
- import { basename as basename4, dirname as dirname18, join as join18 } from "path";
22722
+ import { basename as basename4, dirname as dirname19, join as join18 } from "path";
22545
22723
  import { parse as parseYaml } from "yaml";
22546
22724
  var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
22547
22725
  async function renderCodexSubagents(artifacts, stageRoot, adapter) {
@@ -22587,7 +22765,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
22587
22765
  }
22588
22766
  const markdown = await readFile21(markdownPath, "utf8");
22589
22767
  const toml = markdownToCodexAgentToml(agentName, markdown);
22590
- await mkdir12(dirname18(renderedPath), { recursive: true });
22768
+ await mkdir12(dirname19(renderedPath), { recursive: true });
22591
22769
  await writeFile13(renderedPath, toml, "utf8");
22592
22770
  return {
22593
22771
  ...artifact,
@@ -22670,7 +22848,7 @@ function escapeRegExp2(value) {
22670
22848
  // src/staging/copilot-artifacts.ts
22671
22849
  init_fs();
22672
22850
  import { mkdir as mkdir13, readFile as readFile22, writeFile as writeFile14 } from "fs/promises";
22673
- import { basename as basename5, dirname as dirname19, join as join19 } from "path";
22851
+ import { basename as basename5, dirname as dirname20, join as join19 } from "path";
22674
22852
  async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
22675
22853
  if (adapter?.name !== "copilot") return artifacts;
22676
22854
  const names = /* @__PURE__ */ new Set();
@@ -22701,7 +22879,7 @@ async function renderCopilotSubagent(artifact, stageRoot) {
22701
22879
  throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
22702
22880
  }
22703
22881
  const markdown = await readFile22(markdownPath, "utf8");
22704
- await mkdir13(dirname19(renderedPath), { recursive: true });
22882
+ await mkdir13(dirname20(renderedPath), { recursive: true });
22705
22883
  await writeFile14(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
22706
22884
  return {
22707
22885
  ...artifact,
@@ -22757,7 +22935,7 @@ function yamlString(value) {
22757
22935
  // src/staging/openclaw-subagents.ts
22758
22936
  init_fs();
22759
22937
  import { mkdir as mkdir14, readFile as readFile23, writeFile as writeFile15 } from "fs/promises";
22760
- import { basename as basename6, dirname as dirname20, join as join20 } from "path";
22938
+ import { basename as basename6, dirname as dirname21, join as join20 } from "path";
22761
22939
  async function renderOpenClawSubagents(artifacts, stageRoot, adapter) {
22762
22940
  if (adapter?.name !== "openclaw") return artifacts;
22763
22941
  const names = /* @__PURE__ */ new Set();
@@ -22791,10 +22969,10 @@ async function renderOpenClawSubagent(artifact, stageRoot) {
22791
22969
 
22792
22970
  ${parsed.description ?? `OpenClaw subagent ${agentId}.`}`;
22793
22971
  const renderedPath = join20(stageRoot, ".agentwheel-rendered", "openclaw-subagents", agentId, "AGENTS.md");
22794
- await mkdir14(dirname20(renderedPath), { recursive: true });
22972
+ await mkdir14(dirname21(renderedPath), { recursive: true });
22795
22973
  await writeFile15(renderedPath, `${body}
22796
22974
  `, "utf8");
22797
- const renderedDir = dirname20(renderedPath);
22975
+ const renderedDir = dirname21(renderedPath);
22798
22976
  return {
22799
22977
  ...artifact,
22800
22978
  name: agentId,
@@ -23690,6 +23868,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
23690
23868
  mergeCreatedDestination: existing2?.mergeCreatedDestination,
23691
23869
  currentHash: currentHash2,
23692
23870
  manifestHash: existing2?.hash,
23871
+ exactMergeAdoption: true,
23693
23872
  reason: existing2 ? "repairing exact incomplete merge ownership" : "force adopting exact unmanaged merge contribution"
23694
23873
  });
23695
23874
  continue;
@@ -25344,7 +25523,7 @@ function errorMessage2(error46) {
25344
25523
 
25345
25524
  // src/source/clawhub.ts
25346
25525
  import { mkdir as mkdir15, rm as rm8, writeFile as writeFile16 } from "fs/promises";
25347
- import { basename as basename10, dirname as dirname21, join as join32, resolve as resolve22 } from "path";
25526
+ import { basename as basename10, dirname as dirname22, join as join32, resolve as resolve22 } from "path";
25348
25527
 
25349
25528
  // src/model/package.ts
25350
25529
  init_main();
@@ -25917,7 +26096,7 @@ async function writeGeneratedPackage(root, packageInfo) {
25917
26096
  };
25918
26097
  const pluginPath = join32(root, "plugins", pluginId, "clawhub.json");
25919
26098
  await rm8(root, { recursive: true, force: true });
25920
- await mkdir15(dirname21(pluginPath), { recursive: true });
26099
+ await mkdir15(dirname22(pluginPath), { recursive: true });
25921
26100
  await writeFile16(join32(root, "openpack.json"), `${JSON.stringify({
25922
26101
  schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
25923
26102
  name: `clawhub/${name}`,
@@ -25950,7 +26129,7 @@ function cachePathFor(packageName, cacheRoot) {
25950
26129
  import { execFile as execFile6 } from "child_process";
25951
26130
  import { mkdir as mkdir17, rename as rename6, rm as rm10, stat as stat7 } from "fs/promises";
25952
26131
  import { homedir as homedir8 } from "os";
25953
- import { basename as basename11, dirname as dirname23, join as join35, resolve as resolve23, sep as sep8 } from "path";
26132
+ import { basename as basename11, dirname as dirname24, join as join35, resolve as resolve23, sep as sep8 } from "path";
25954
26133
  import { promisify as promisify6 } from "util";
25955
26134
  init_fs();
25956
26135
 
@@ -26033,7 +26212,7 @@ function isMissingFile(error46) {
26033
26212
  init_fs();
26034
26213
  import { randomUUID as randomUUID4 } from "crypto";
26035
26214
  import { mkdir as mkdir16, readdir as readdir9, readFile as readFile29, rm as rm9, stat as stat6, writeFile as writeFile17 } from "fs/promises";
26036
- import { dirname as dirname22, join as join34 } from "path";
26215
+ import { dirname as dirname23, join as join34 } from "path";
26037
26216
  var snapshotNamePattern = /^(.*)-([0-9a-f]{12})$/i;
26038
26217
  var leaseMarker = ".agentwheel-lease-";
26039
26218
  async function pruneGitCache(cacheRoot, options = {}) {
@@ -26101,7 +26280,7 @@ async function removeGeneratedEntries(root, preserveGit) {
26101
26280
  }
26102
26281
  }
26103
26282
  async function pruneGitCacheUnlocked(cacheRoot, options) {
26104
- const referencedCommits = await referencedGraphLockCommits(join34(dirname22(cacheRoot), "locks"));
26283
+ const referencedCommits = await referencedGraphLockCommits(join34(dirname23(cacheRoot), "locks"));
26105
26284
  const entries = await readdir9(cacheRoot, { withFileTypes: true });
26106
26285
  const groups = /* @__PURE__ */ new Map();
26107
26286
  for (const entry of entries) {
@@ -26168,7 +26347,7 @@ async function walkGraphLocks(root, visit2) {
26168
26347
  }
26169
26348
  }
26170
26349
  async function hasLiveSnapshotLease(snapshotPath, dryRun) {
26171
- const directory = dirname22(snapshotPath);
26350
+ const directory = dirname23(snapshotPath);
26172
26351
  const prefix = `${snapshotPath.slice(directory.length + 1)}${leaseMarker}`;
26173
26352
  const entries = await readdir9(directory, { withFileTypes: true });
26174
26353
  let live = false;
@@ -26307,7 +26486,7 @@ var GitSourceDriver = class {
26307
26486
  async fetch(resolved) {
26308
26487
  return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
26309
26488
  const parsed = parseGitSource(resolved.source);
26310
- const cacheRoot = dirname23(resolved.resolvedPath);
26489
+ const cacheRoot = dirname24(resolved.resolvedPath);
26311
26490
  await mkdir17(cacheRoot, { recursive: true });
26312
26491
  await assertOwnedByCurrentUser(cacheRoot);
26313
26492
  if (await pathExists(resolved.resolvedPath)) await assertOwnedByCurrentUser(resolved.resolvedPath);
@@ -26402,9 +26581,9 @@ async function resolveCommit(checkoutPath, ref) {
26402
26581
  throw lastError;
26403
26582
  }
26404
26583
  async function snapshotCommit(checkoutPath, commit) {
26405
- const snapshotPath = join35(dirname23(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
26584
+ const snapshotPath = join35(dirname24(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
26406
26585
  if (await pathExists(snapshotPath)) return snapshotPath;
26407
- const tempPath = join35(dirname23(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
26586
+ const tempPath = join35(dirname24(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
26408
26587
  const tempIndexPath = `${tempPath}.index`;
26409
26588
  await rm10(tempPath, { recursive: true, force: true });
26410
26589
  await rm10(tempIndexPath, { force: true });
@@ -26442,7 +26621,7 @@ async function assertOwnedByCurrentUser(path) {
26442
26621
 
26443
26622
  // src/source/mcp-registry.ts
26444
26623
  import { mkdir as mkdir18, writeFile as writeFile18 } from "fs/promises";
26445
- import { basename as basename12, dirname as dirname24, join as join36, resolve as resolve24 } from "path";
26624
+ import { basename as basename12, dirname as dirname25, join as join36, resolve as resolve24 } from "path";
26446
26625
  init_fs();
26447
26626
  var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
26448
26627
  var sourcePrefix2 = "mcp-registry:";
@@ -26550,7 +26729,7 @@ function isSafeHttpUrl(value) {
26550
26729
  async function writeGeneratedPackage2(root, server) {
26551
26730
  const serverId = installNameFor3(server.serverName);
26552
26731
  const mcpPath = join36(root, "mcp", `${serverId}.json`);
26553
- await mkdir18(dirname24(mcpPath), { recursive: true });
26732
+ await mkdir18(dirname25(mcpPath), { recursive: true });
26554
26733
  await writeFile18(join36(root, "openpack.json"), `${JSON.stringify({
26555
26734
  schemaVersion: CURRENT_OPENPACK_SCHEMA_VERSION,
26556
26735
  name: `mcp-registry/${server.serverName}`,
@@ -26584,14 +26763,14 @@ import { createHash as createHash16, randomUUID as randomUUID5 } from "crypto";
26584
26763
  import { execFile as execFile7 } from "child_process";
26585
26764
  import { cp as cp3, mkdir as mkdir19, readFile as readFile30, rename as rename7, rm as rm11 } from "fs/promises";
26586
26765
  import { homedir as homedir9 } from "os";
26587
- import { basename as basename14, dirname as dirname26, join as join38, resolve as resolve25 } from "path";
26766
+ import { basename as basename14, dirname as dirname27, join as join38, resolve as resolve25 } from "path";
26588
26767
  import { promisify as promisify7 } from "util";
26589
26768
  import * as defaultSkillKit from "@skillkit/core";
26590
26769
 
26591
26770
  // src/source/skill-artifacts.ts
26592
26771
  init_fs();
26593
26772
  import { readdir as readdir10, stat as stat8 } from "fs/promises";
26594
- import { basename as basename13, dirname as dirname25, extname as extname2, join as join37 } from "path";
26773
+ import { basename as basename13, dirname as dirname26, extname as extname2, join as join37 } from "path";
26595
26774
  async function artifactsFromSkillPaths(paths, packageName) {
26596
26775
  const artifacts = [];
26597
26776
  const seen = /* @__PURE__ */ new Set();
@@ -26628,7 +26807,7 @@ async function artifactFromSkillPath(item, packageName) {
26628
26807
  };
26629
26808
  }
26630
26809
  if (stats.isFile() && basename13(item.path).toLowerCase() === "skill.md") {
26631
- const dir = dirname25(item.path);
26810
+ const dir = dirname26(item.path);
26632
26811
  const name = sanitizeSkillName(item.name ?? basename13(dir));
26633
26812
  return {
26634
26813
  type: "skills",
@@ -26736,7 +26915,7 @@ var SkillKitSourceDriver = class {
26736
26915
  sourceHash: await hashPath(resolved.resolvedPath)
26737
26916
  };
26738
26917
  }
26739
- const specCachePath = cachePathFor4(spec, dirname26(dirname26(resolved.resolvedPath)));
26918
+ const specCachePath = cachePathFor4(spec, dirname27(dirname27(resolved.resolvedPath)));
26740
26919
  const refIdentity = resolved.requestedRef ?? "default";
26741
26920
  const lockPath = `${specCachePath}.ref-${createHash16("sha256").update(refIdentity).digest("hex")}.lock`;
26742
26921
  const inflightKey = `${lockPath}\0${requestedCommit ?? "movable"}`;
@@ -26757,11 +26936,11 @@ var SkillKitSourceDriver = class {
26757
26936
  throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
26758
26937
  }
26759
26938
  const materialized = await withFilesystemLock(lockPath, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
26760
- const immutablePath = requestedCommit ? cachePathFor4(spec, dirname26(dirname26(specCachePath)), requestedCommit) : void 0;
26939
+ const immutablePath = requestedCommit ? cachePathFor4(spec, dirname27(dirname27(specCachePath)), requestedCommit) : void 0;
26761
26940
  if (immutablePath && await pathExists(immutablePath)) {
26762
26941
  return { path: immutablePath, commit: requestedCommit, cacheIdentity: requestedCommit };
26763
26942
  }
26764
- await mkdir19(dirname26(specCachePath), { recursive: true });
26943
+ await mkdir19(dirname27(specCachePath), { recursive: true });
26765
26944
  const candidatePath = `${specCachePath}.agentwheel-tmp-${process.pid}-${randomUUID5()}`;
26766
26945
  const publishPath = `${candidatePath}.publish`;
26767
26946
  let result;
@@ -26783,7 +26962,7 @@ var SkillKitSourceDriver = class {
26783
26962
  `SkillKit provider resolved ${resolvedIdentity.commit ?? "non-Git content"} instead of requested commit ${requestedCommit}`
26784
26963
  );
26785
26964
  }
26786
- const cachePath = cachePathFor4(spec, dirname26(dirname26(specCachePath)), resolvedIdentity.cacheKey);
26965
+ const cachePath = cachePathFor4(spec, dirname27(dirname27(specCachePath)), resolvedIdentity.cacheKey);
26787
26966
  if (await pathExists(cachePath)) {
26788
26967
  return { path: cachePath, commit: resolvedIdentity.commit, cacheIdentity: resolvedIdentity.cacheKey };
26789
26968
  }
@@ -27054,7 +27233,7 @@ init_identify();
27054
27233
 
27055
27234
  // src/staging/staging.ts
27056
27235
  import { chmod as chmod4, cp as cp5, mkdir as mkdir22, mkdtemp as mkdtemp4, readdir as readdir13, stat as stat12 } from "fs/promises";
27057
- import { basename as basename19, dirname as dirname30, join as join43, relative as relative16, resolve as resolve29, sep as sep10 } from "path";
27236
+ import { basename as basename19, dirname as dirname31, join as join43, relative as relative16, resolve as resolve29, sep as sep10 } from "path";
27058
27237
  import { tmpdir as tmpdir5 } from "os";
27059
27238
  init_fs();
27060
27239
 
@@ -27063,7 +27242,7 @@ init_artifact();
27063
27242
  init_fs();
27064
27243
  import { createHash as createHash17 } from "crypto";
27065
27244
  import { readdir as readdir11, readFile as readFile31, stat as stat10, writeFile as writeFile19 } from "fs/promises";
27066
- import { basename as basename16, dirname as dirname27, extname as extname3, join as join40, relative as relative15, resolve as resolve28, sep as sep9 } from "path";
27245
+ import { basename as basename16, dirname as dirname28, extname as extname3, join as join40, relative as relative15, resolve as resolve28, sep as sep9 } from "path";
27067
27246
  var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
27068
27247
  var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
27069
27248
  var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
@@ -27082,7 +27261,8 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
27082
27261
  entry,
27083
27262
  packageRoot,
27084
27263
  artifactPaths,
27085
- nodeId: options.nodeId
27264
+ nodeId: options.nodeId,
27265
+ packageName: options.packageName
27086
27266
  }));
27087
27267
  const externalEntries = isPrimaryMarkdownFile(artifact, file2) ? options.additionalComposeEntries?.(artifact) ?? [] : [];
27088
27268
  const result = await expandFile(file2, packageRoot, [...localEntries, ...externalEntries], artifactPaths, options);
@@ -27114,12 +27294,13 @@ async function expandFile(file2, packageRoot, appendEntries, artifactPaths, opti
27114
27294
  const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
27115
27295
  let content = expanded.content;
27116
27296
  const composedFrom = [...expanded.composedFrom];
27117
- const appliedComposeIdentities = /* @__PURE__ */ new Set();
27297
+ const appliedComposeIdentities = /* @__PURE__ */ new Map();
27118
27298
  for (const external of appendEntries) {
27119
27299
  const { entry } = external;
27120
27300
  const included = await expandInclude(entry.include, external.packageRoot, external.artifactPaths, {
27121
27301
  ...options,
27122
27302
  nodeId: external.nodeId,
27303
+ packageName: external.packageName,
27123
27304
  optional: entry.optional === true,
27124
27305
  markers: entry.markers !== false,
27125
27306
  chain: [owner]
@@ -27127,10 +27308,15 @@ async function expandFile(file2, packageRoot, appendEntries, artifactPaths, opti
27127
27308
  if (!included) continue;
27128
27309
  const markerMode = entry.markers === false ? "plain" : "markers";
27129
27310
  const identity = `${markerMode}\0${included.identity}`;
27130
- if (external.deduplicate) {
27131
- if (appliedComposeIdentities.has(identity)) continue;
27311
+ const previous = appliedComposeIdentities.get(identity);
27312
+ if (external.deduplicate && previous) {
27313
+ if (previous.hash !== included.hash) {
27314
+ throw new Error(`Conflicting OpenPack composition revisions for ${included.selector}: ${previous.selector} (${previous.hash}) and ${included.selector} (${included.hash}). Select one revision explicitly.`);
27315
+ }
27316
+ composedFrom.push(...included.composedFrom);
27317
+ continue;
27132
27318
  }
27133
- appliedComposeIdentities.add(identity);
27319
+ appliedComposeIdentities.set(identity, { hash: included.hash, selector: included.selector });
27134
27320
  content = `${content.trimEnd()}
27135
27321
 
27136
27322
  ${included.rendered}
@@ -27150,6 +27336,7 @@ async function expandContent(content, packageRoot, chain, artifactPaths, options
27150
27336
  }
27151
27337
  const composedFrom = [];
27152
27338
  const replacements = [];
27339
+ const semanticReplacements = [];
27153
27340
  for (const match of content.matchAll(includePattern)) {
27154
27341
  const selector = cleanSelector(match[2] ?? "");
27155
27342
  const included = await expandInclude(selector, packageRoot, artifactPaths, {
@@ -27163,13 +27350,14 @@ async function expandContent(content, packageRoot, chain, artifactPaths, options
27163
27350
  end: (match.index ?? 0) + match[0].length,
27164
27351
  value: included?.rendered ?? ""
27165
27352
  });
27353
+ semanticReplacements.push({ start: match.index ?? 0, end: (match.index ?? 0) + match[0].length, value: included?.semanticContent ?? "" });
27166
27354
  if (included) composedFrom.push(...included.composedFrom);
27167
27355
  }
27168
27356
  let expanded = applyReplacements(content, replacements);
27169
27357
  expanded = expanded.replace(escapedIncludePattern, (_match, optional2, selector) => {
27170
27358
  return `<!-- openpack:include${optional2 ?? ""} ${cleanSelector(selector)} -->`;
27171
27359
  });
27172
- return { content: expanded, composedFrom: uniqueComposedFrom(composedFrom) };
27360
+ return { content: expanded, semanticContent: applyReplacements(content, semanticReplacements), composedFrom: uniqueComposedFrom(composedFrom) };
27173
27361
  }
27174
27362
  async function expandInclude(selector, packageRoot, artifactPaths, options) {
27175
27363
  const parsed = parseOpenPackIncludeSelector(selector);
@@ -27178,6 +27366,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
27178
27366
  let includePackageRoot = packageRoot;
27179
27367
  let includeArtifactPaths = artifactPaths;
27180
27368
  let includeNodeId = options.nodeId;
27369
+ let includePackageName = options.packageName;
27181
27370
  let displaySelector = displaySelectorForInclude(parsed.selector, options.nodeId, options.originNodeId);
27182
27371
  if (parsed.alias) {
27183
27372
  if (!options.nodeId || !options.resolveCrossPackageInclude) {
@@ -27196,6 +27385,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
27196
27385
  includePackageRoot = resolved.packageRoot;
27197
27386
  includeArtifactPaths = resolved.artifactPaths;
27198
27387
  includeNodeId = resolved.toNodeId;
27388
+ includePackageName = resolved.packageName;
27199
27389
  displaySelector = `${resolved.toNodeId}:${parsed.selector}`;
27200
27390
  } else {
27201
27391
  sourcePath = artifactPaths.get(parsed.selector) ?? resolvePackageSelector(packageRoot, parsed.selector);
@@ -27215,13 +27405,14 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
27215
27405
  const { optional: _optional2, markers: _markers, chain: _chain, ...childOptions } = options;
27216
27406
  const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
27217
27407
  ...childOptions,
27218
- nodeId: includeNodeId
27408
+ nodeId: includeNodeId,
27409
+ packageName: includePackageName
27219
27410
  });
27220
27411
  const contentHash = sha2563(expanded.content);
27221
- const identity = `${includeNodeId ?? resolve28(includePackageRoot)}\0${parsed.selector}\0${contentHash}`;
27412
+ const identity = `${includePackageName ?? includeNodeId ?? resolve28(includePackageRoot)}\0${parsed.selector}`;
27222
27413
  const ownEntry = { selector: displaySelector, hash: contentHash };
27223
27414
  const composedFrom = uniqueComposedFrom([ownEntry, ...expanded.composedFrom]);
27224
- if (!options.markers) return { rendered: expanded.content, composedFrom, identity };
27415
+ if (!options.markers) return { rendered: expanded.content, composedFrom, identity, hash: sha2563(expanded.semanticContent), selector: displaySelector, semanticContent: expanded.semanticContent };
27225
27416
  return {
27226
27417
  rendered: [
27227
27418
  `<!-- BEGIN openpack:include ${displaySelector} sha256:${contentHash.slice(0, markerHashLength)} -->`,
@@ -27229,7 +27420,10 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
27229
27420
  `<!-- END openpack:include ${displaySelector} -->`
27230
27421
  ].join("\n"),
27231
27422
  composedFrom,
27232
- identity
27423
+ identity,
27424
+ hash: sha2563(expanded.semanticContent),
27425
+ selector: displaySelector,
27426
+ semanticContent: expanded.semanticContent
27233
27427
  };
27234
27428
  }
27235
27429
  function parseOpenPackIncludeSelector(selector) {
@@ -27303,11 +27497,11 @@ async function listMarkdownFiles(root) {
27303
27497
  function composeEntriesForFile(artifact, file2) {
27304
27498
  if (!artifact.compose?.length) return [];
27305
27499
  if (artifact.kind === "file") return [resolve28(artifact.stagedPath ?? artifact.sourcePath), resolve28(file2)].every(Boolean) && resolve28(artifact.stagedPath ?? artifact.sourcePath) === resolve28(file2) ? artifact.compose : [];
27306
- return basename16(file2) === "SKILL.md" && dirname27(file2) === resolve28(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
27500
+ return basename16(file2) === "SKILL.md" && dirname28(file2) === resolve28(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
27307
27501
  }
27308
27502
  function isPrimaryMarkdownFile(artifact, file2) {
27309
27503
  if (artifact.kind === "file") return resolve28(artifact.stagedPath ?? artifact.sourcePath) === resolve28(file2);
27310
- return basename16(file2) === "SKILL.md" && dirname27(file2) === resolve28(artifact.stagedPath ?? artifact.sourcePath);
27504
+ return basename16(file2) === "SKILL.md" && dirname28(file2) === resolve28(artifact.stagedPath ?? artifact.sourcePath);
27311
27505
  }
27312
27506
  function orderedForExpansion(artifacts) {
27313
27507
  return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
@@ -27355,7 +27549,7 @@ function artifactPathMap(artifacts) {
27355
27549
  // src/staging/customize.ts
27356
27550
  init_fs();
27357
27551
  import { cp as cp4, mkdir as mkdir20, readdir as readdir12, readFile as readFile32, writeFile as writeFile20 } from "fs/promises";
27358
- import { dirname as dirname28, join as join41 } from "path";
27552
+ import { dirname as dirname29, join as join41 } from "path";
27359
27553
  async function applyCustomizations(artifacts, options) {
27360
27554
  let next = [...artifacts];
27361
27555
  next = await applyReplacements2(next, options, "override", installableArtifactTypes());
@@ -27379,7 +27573,7 @@ async function applyInstructionOverlay(artifacts, options) {
27379
27573
  const managed = await readFile32(artifact.stagedPath ?? artifact.sourcePath, "utf8");
27380
27574
  const local = await readFile32(overlayPath, "utf8");
27381
27575
  const composedPath = join41(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
27382
- await mkdir20(dirname28(composedPath), { recursive: true });
27576
+ await mkdir20(dirname29(composedPath), { recursive: true });
27383
27577
  await writeFile20(
27384
27578
  composedPath,
27385
27579
  [
@@ -27452,7 +27646,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
27452
27646
  const artifactKind = entry.isDirectory() ? "dir" : "file";
27453
27647
  const existing = byKey.get(artifactMapKey);
27454
27648
  const stagedPath = join41(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
27455
- await mkdir20(dirname28(stagedPath), { recursive: true });
27649
+ await mkdir20(dirname29(stagedPath), { recursive: true });
27456
27650
  await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
27457
27651
  byKey.set(artifactMapKey, {
27458
27652
  ...existing,
@@ -27498,7 +27692,7 @@ async function sortedDirEntries2(path) {
27498
27692
  // src/staging/claude-subagents.ts
27499
27693
  init_fs();
27500
27694
  import { mkdir as mkdir21, readFile as readFile33, writeFile as writeFile21 } from "fs/promises";
27501
- import { basename as basename18, dirname as dirname29, join as join42 } from "path";
27695
+ import { basename as basename18, dirname as dirname30, join as join42 } from "path";
27502
27696
  async function renderClaudeSubagents(artifacts, stageRoot, adapter) {
27503
27697
  if (adapter?.name !== "claude") return artifacts;
27504
27698
  const names = /* @__PURE__ */ new Set();
@@ -27529,7 +27723,7 @@ async function renderClaudeSubagent(artifact, stageRoot) {
27529
27723
  }
27530
27724
  const content = await readFile33(markdownPath, "utf8");
27531
27725
  const renderedPath = join42(stageRoot, ".agentwheel-rendered", "claude-subagents", `${agentName}.md`);
27532
- await mkdir21(dirname29(renderedPath), { recursive: true });
27726
+ await mkdir21(dirname30(renderedPath), { recursive: true });
27533
27727
  await writeFile21(renderedPath, content.endsWith("\n") ? content : `${content}
27534
27728
  `, "utf8");
27535
27729
  return {
@@ -27572,7 +27766,7 @@ async function stageResolvedArtifactsRaw(resolved, artifacts, parentDir = tmpdir
27572
27766
  await removeOnFailure(root, async () => {
27573
27767
  for (const artifact of artifacts) {
27574
27768
  const stagedPath = join43(root, artifact.relativePath);
27575
- await mkdir22(dirname30(stagedPath), { recursive: true });
27769
+ await mkdir22(dirname31(stagedPath), { recursive: true });
27576
27770
  await cp5(artifact.sourcePath, stagedPath, {
27577
27771
  recursive: artifact.kind === "dir",
27578
27772
  dereference: true,
@@ -27678,7 +27872,7 @@ async function copyAsset(asset, source, dest) {
27678
27872
  throw new Error(`Asset include source is not a file or directory: ${source}`);
27679
27873
  }
27680
27874
  if (!asset.include?.length) {
27681
- await mkdir22(dirname30(dest), { recursive: true });
27875
+ await mkdir22(dirname31(dest), { recursive: true });
27682
27876
  await cp5(source, dest, { recursive: true, dereference: true });
27683
27877
  if (asset.mode === "copy") await normalizeCopiedModes(dest);
27684
27878
  return;
@@ -27690,7 +27884,7 @@ async function copyAsset(asset, source, dest) {
27690
27884
  }
27691
27885
  }
27692
27886
  async function copyAssetFile(source, dest, asset) {
27693
- await mkdir22(dirname30(dest), { recursive: true });
27887
+ await mkdir22(dirname31(dest), { recursive: true });
27694
27888
  await cp5(source, dest, { dereference: true });
27695
27889
  if (asset.mode === "copy") await chmod4(dest, 420);
27696
27890
  }
@@ -27746,7 +27940,7 @@ init_adapter();
27746
27940
  init_artifact();
27747
27941
  init_workspace();
27748
27942
  import { appendFile, cp as cp6, lstat as lstat7, mkdir as mkdir23, readdir as readdir15, rm as rm13 } from "fs/promises";
27749
- import { dirname as dirname32, join as join46, relative as relative17 } from "path";
27943
+ import { dirname as dirname33, join as join46, relative as relative17 } from "path";
27750
27944
 
27751
27945
  // src/resolve/graph.ts
27752
27946
  import { createHash as createHash19 } from "crypto";
@@ -27884,7 +28078,7 @@ import { resolve as resolve31 } from "path";
27884
28078
  // src/registry/client.ts
27885
28079
  import { readFile as readFile35, rm as rm12, stat as stat13 } from "fs/promises";
27886
28080
  import { homedir as homedir11 } from "os";
27887
- import { dirname as dirname31, join as join44, resolve as resolve30 } from "path";
28081
+ import { dirname as dirname32, join as join44, resolve as resolve30 } from "path";
27888
28082
  import { fileURLToPath as fileURLToPath2 } from "url";
27889
28083
 
27890
28084
  // src/model/registry.ts
@@ -28005,7 +28199,7 @@ var RegistryClient = class {
28005
28199
  const stats = await stat13(fullPath);
28006
28200
  return readFile35(stats.isDirectory() ? join44(fullPath, "index.json") : fullPath, "utf8");
28007
28201
  }
28008
- const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join44(dirname31(this.cachePath), "registry-repos") }));
28202
+ const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join44(dirname32(this.cachePath), "registry-repos") }));
28009
28203
  return readFile35(join44(resolved.resolvedPath, "index.json"), "utf8");
28010
28204
  }
28011
28205
  warnCompatibility(entries) {
@@ -29132,7 +29326,7 @@ init_declarations();
29132
29326
  async function remember(workspaceRoot, runtime, text) {
29133
29327
  const overlayPath = join46(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
29134
29328
  declareMutationPath(overlayPath);
29135
- await mkdir23(dirname32(overlayPath), { recursive: true });
29329
+ await mkdir23(dirname33(overlayPath), { recursive: true });
29136
29330
  await appendFile(overlayPath, `${text.trim()}
29137
29331
  `, "utf8");
29138
29332
  return { overlayPath };
@@ -29165,7 +29359,7 @@ async function ejectArtifact(workspaceRoot, item) {
29165
29359
  } else {
29166
29360
  declareMutationPath(ejectedPath);
29167
29361
  }
29168
- await mkdir23(dirname32(ejectedPath), { recursive: true });
29362
+ await mkdir23(dirname33(ejectedPath), { recursive: true });
29169
29363
  await rm13(ejectedPath, { recursive: true, force: true });
29170
29364
  await cp6(sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
29171
29365
  return {
@@ -29284,7 +29478,7 @@ init_adapter();
29284
29478
  init_graph_lock();
29285
29479
  import { createHash as createHash21 } from "crypto";
29286
29480
  import { mkdir as mkdir25, readdir as readdir16 } from "fs/promises";
29287
- import { basename as basename21, dirname as dirname34, join as join49, posix as posix7, resolve as resolve34 } from "path";
29481
+ import { basename as basename21, dirname as dirname35, join as join49, posix as posix7, resolve as resolve34 } from "path";
29288
29482
  init_paths();
29289
29483
  init_merge_removal();
29290
29484
  init_transaction();
@@ -29497,6 +29691,7 @@ async function renderGraphIntoRoot(root, graph, targetContext) {
29497
29691
  const compositionArtifacts = runtimeArtifacts;
29498
29692
  const expandedArtifacts = await expandMarkdownIncludes(compositionArtifacts, staged.root, {
29499
29693
  nodeId: rawNode.node.id,
29694
+ packageName: rawNode.node.name,
29500
29695
  originNodeId: rawNode.node.id,
29501
29696
  additionalComposeEntries: (artifact) => matchingCompositionEntries(
29502
29697
  artifact,
@@ -29534,6 +29729,7 @@ async function renderGraphIntoRoot(root, graph, targetContext) {
29534
29729
  includeEdges.set(`${includeEdge.fromNodeId}\0${includeEdge.alias}\0${includeEdge.toNodeId}\0${includeEdge.selector}\0${includeEdge.sourceHash}`, includeEdge);
29535
29730
  return {
29536
29731
  toNodeId: edge.to,
29732
+ packageName: target.rawNode.node.name,
29537
29733
  packageRoot: target.root,
29538
29734
  artifactPaths: target.artifactPaths,
29539
29735
  sourcePath,
@@ -29621,6 +29817,7 @@ function matchingCompositionEntries(artifact, targetNodeId, runtime, rules) {
29621
29817
  packageRoot: candidate.packageRoot,
29622
29818
  artifactPaths: candidate.artifactPaths,
29623
29819
  nodeId: candidate.ownerNodeId,
29820
+ packageName: candidate.ownerPackageName,
29624
29821
  deduplicate: true
29625
29822
  });
29626
29823
  }
@@ -29969,7 +30166,7 @@ init_zod();
29969
30166
  init_fs();
29970
30167
  import { mkdir as mkdir24, readFile as readFile38 } from "fs/promises";
29971
30168
  import { homedir as homedir13 } from "os";
29972
- import { dirname as dirname33, join as join48 } from "path";
30169
+ import { dirname as dirname34, join as join48 } from "path";
29973
30170
  var trustStoreSchema = external_exports.object({
29974
30171
  version: external_exports.literal(1),
29975
30172
  acceptedSources: external_exports.array(external_exports.string().min(1)).default([])
@@ -30045,7 +30242,7 @@ async function readTrustStore(path) {
30045
30242
  return trustStoreSchema.parse(JSON.parse(await readFile38(path, "utf8")));
30046
30243
  }
30047
30244
  async function writeTrustStore(path, store) {
30048
- await mkdir24(dirname33(path), { recursive: true });
30245
+ await mkdir24(dirname34(path), { recursive: true });
30049
30246
  await writeJsonAtomic(path, trustStoreSchema.parse(store));
30050
30247
  }
30051
30248
  function defaultTrustStorePath() {
@@ -30545,7 +30742,7 @@ async function discoverTargetApplyJournal(options) {
30545
30742
  continue;
30546
30743
  }
30547
30744
  if (!journal.graphLockPath || !journal.graphLock || !journal.graphLockDigest) continue;
30548
- if (resolve34(dirname34(journal.graphLockPath)) !== resolve34(dirname34(options.graphLockPath))) continue;
30745
+ if (resolve34(dirname35(journal.graphLockPath)) !== resolve34(dirname35(options.graphLockPath))) continue;
30549
30746
  if (options.transport.kind === "ssh") {
30550
30747
  throw new Error(
30551
30748
  `Legacy SSH apply journal at ${item.path} does not record its original endpoint; Agentwheel cannot prove endpoint identity, so the journal was preserved.`
@@ -30687,7 +30884,7 @@ async function resolvePriorTargetState(options) {
30687
30884
  return {};
30688
30885
  }
30689
30886
  async function discoverLegacyStateCandidates(options) {
30690
- const directory = dirname34(options.graphLockPath);
30887
+ const directory = dirname35(options.graphLockPath);
30691
30888
  let names;
30692
30889
  try {
30693
30890
  names = await readdir16(directory);
@@ -30975,7 +31172,7 @@ function mcpServerNames(strategy, removal) {
30975
31172
  return new Set(Object.keys(servers));
30976
31173
  }
30977
31174
  function globalConfigRoot(globalRoot) {
30978
- return dirname34(dirname34(globalWorkspaceConfigPath(globalRoot)));
31175
+ return dirname35(dirname35(globalWorkspaceConfigPath(globalRoot)));
30979
31176
  }
30980
31177
  function ownedByWorkspace(owner, desiredOwner, workspaceRoot, ownsSubWorkspaces) {
30981
31178
  if (owner === desiredOwner) return true;
@@ -31123,7 +31320,7 @@ function targetLabel(target) {
31123
31320
  init_workspace();
31124
31321
  init_run_as_identity();
31125
31322
  init_fs();
31126
- import { basename as basename22, dirname as dirname35, join as join50, resolve as resolve35 } from "path";
31323
+ import { basename as basename22, dirname as dirname36, join as join50, resolve as resolve35 } from "path";
31127
31324
  var runtimeMarkers = [
31128
31325
  { adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
31129
31326
  { adapter: "claude", dirs: [".claude"] },
@@ -31249,7 +31446,7 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
31249
31446
  if (adapterFilter && marker.adapter !== adapterFilter) continue;
31250
31447
  for (const dir of marker.dirs) {
31251
31448
  if (basename22(root) === dir) {
31252
- matches.push({ adapter: marker.adapter, targetRoot: dirname35(root) });
31449
+ matches.push({ adapter: marker.adapter, targetRoot: dirname36(root) });
31253
31450
  } else if (await pathExists(join50(root, dir))) {
31254
31451
  matches.push({ adapter: marker.adapter, targetRoot: root });
31255
31452
  }
@@ -31296,7 +31493,7 @@ function dedupeTargets(matches) {
31296
31493
  function runtimeScanRoot(request) {
31297
31494
  const root = resolve35(request.targetRoot ?? request.cwd ?? process.cwd());
31298
31495
  if (request.targetRoot) return root;
31299
- return runtimeMarkers.some((marker) => marker.dirs.includes(basename22(root))) ? dirname35(root) : root;
31496
+ return runtimeMarkers.some((marker) => marker.dirs.includes(basename22(root))) ? dirname36(root) : root;
31300
31497
  }
31301
31498
 
31302
31499
  // src/lifecycle/profile.ts
@@ -31630,7 +31827,7 @@ init_selection();
31630
31827
  // src/cli/update-check.ts
31631
31828
  import { mkdir as mkdir26, readFile as readFile39, writeFile as writeFile22 } from "fs/promises";
31632
31829
  import { homedir as homedir14 } from "os";
31633
- import { dirname as dirname36, join as join51 } from "path";
31830
+ import { dirname as dirname37, join as join51 } from "path";
31634
31831
  var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
31635
31832
  var DEFAULT_TIMEOUT_MS = 300;
31636
31833
  var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
@@ -31683,7 +31880,7 @@ async function readCache(path) {
31683
31880
  }
31684
31881
  }
31685
31882
  async function writeCache(path, cache) {
31686
- await mkdir26(dirname36(path), { recursive: true });
31883
+ await mkdir26(dirname37(path), { recursive: true });
31687
31884
  await writeFile22(path, `${JSON.stringify(cache, null, 2)}
31688
31885
  `, "utf8");
31689
31886
  }
@@ -31919,11 +32116,11 @@ init_graph_lock();
31919
32116
 
31920
32117
  // src/cli/version.ts
31921
32118
  import { readFileSync as readFileSync2 } from "fs";
31922
- import { dirname as dirname37, join as join54 } from "path";
32119
+ import { dirname as dirname38, join as join54 } from "path";
31923
32120
  import { fileURLToPath as fileURLToPath3 } from "url";
31924
32121
  var FALLBACK_VERSION = "0.0.0";
31925
32122
  function resolveCliVersion() {
31926
- let dir = dirname37(fileURLToPath3(import.meta.url));
32123
+ let dir = dirname38(fileURLToPath3(import.meta.url));
31927
32124
  while (true) {
31928
32125
  try {
31929
32126
  const pkg = JSON.parse(readFileSync2(join54(dir, "package.json"), "utf8"));
@@ -31932,7 +32129,7 @@ function resolveCliVersion() {
31932
32129
  }
31933
32130
  } catch {
31934
32131
  }
31935
- const parent = dirname37(dir);
32132
+ const parent = dirname38(dir);
31936
32133
  if (parent === dir) return FALLBACK_VERSION;
31937
32134
  dir = parent;
31938
32135
  }
@@ -32841,7 +33038,7 @@ init_graph_lock();
32841
33038
  init_manifest();
32842
33039
  import { createHash as createHash26 } from "crypto";
32843
33040
  import { lstat as lstat8, readdir as readdir17 } from "fs/promises";
32844
- import { basename as basename23, dirname as dirname38, join as join57, posix as posix8, relative as relative18, resolve as resolve42 } from "path";
33041
+ import { basename as basename23, dirname as dirname39, join as join57, posix as posix8, relative as relative18, resolve as resolve42 } from "path";
32845
33042
  async function inventoryHistoricalLocks(fleetRoot) {
32846
33043
  const root = join57(fleetRoot, ".agentwheel", "locks");
32847
33044
  const found = [];
@@ -32888,7 +33085,7 @@ async function planLegacyRecovery(input) {
32888
33085
  const { graphPlan, installRoot, targetKey: targetKey2, adapter, installationType, historicalLocks, transport } = input;
32889
33086
  assertCompleteGraphPlan(graphPlan);
32890
33087
  const binding = {
32891
- targetLockDir: dirname38(graphPlan.graphLockPath),
33088
+ targetLockDir: dirname39(graphPlan.graphLockPath),
32892
33089
  stableLockPath: graphPlan.graphLockPath,
32893
33090
  stableStateKey: graphPlan.plan.stateKey,
32894
33091
  currentFingerprint: graphPlan.targetFingerprint,
@@ -33000,7 +33197,7 @@ function claimFor(item, entry, locks, binding) {
33000
33197
  const evidence = [];
33001
33198
  const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
33002
33199
  const sameRoot = binding.transportKind === "ssh" ? posix8.normalize(item.manifest.targetRoot) === posix8.normalize(binding.installRoot) : resolve42(item.manifest.targetRoot) === resolve42(binding.installRoot);
33003
- const inTargetDir = matches.filter((candidate) => resolve42(dirname38(candidate.path)) === resolve42(binding.targetLockDir));
33200
+ const inTargetDir = matches.filter((candidate) => resolve42(dirname39(candidate.path)) === resolve42(binding.targetLockDir));
33004
33201
  const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
33005
33202
  if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
33006
33203
  if (item.manifest.version !== 2 || item.manifest.installationType !== binding.installationType) evidence.push("installation type differs or manifest is legacy v1");
@@ -35049,7 +35246,7 @@ init_fleet();
35049
35246
  // src/lifecycle/fleet-normalize.ts
35050
35247
  import { createHash as createHash31 } from "crypto";
35051
35248
  import { homedir as homedir17 } from "os";
35052
- import { dirname as dirname40, isAbsolute as isAbsolute16, join as join65, relative as relative21, resolve as resolve47 } from "path";
35249
+ import { dirname as dirname41, isAbsolute as isAbsolute16, join as join65, relative as relative21, resolve as resolve47 } from "path";
35053
35250
  init_manifest2();
35054
35251
  init_paths();
35055
35252
  init_merge_removal();
@@ -35075,7 +35272,7 @@ init_caller_write_trust();
35075
35272
  import { AsyncLocalStorage } from "async_hooks";
35076
35273
  import { readFile as readLocalFile, readdir as readLocalDir, rm as removeLocal, mkdir as mkdir27, open as open6, rename as rename9 } from "fs/promises";
35077
35274
  import { randomUUID as randomUUID8 } from "crypto";
35078
- import { dirname as dirname39, isAbsolute as isAbsolute15, relative as relative20, resolve as resolve46, sep as sep11 } from "path";
35275
+ import { dirname as dirname40, isAbsolute as isAbsolute15, relative as relative20, resolve as resolve46, sep as sep11 } from "path";
35079
35276
  var contexts = new AsyncLocalStorage();
35080
35277
  var inside = (root, path) => {
35081
35278
  const rel = relative20(root, resolve46(path));
@@ -35137,7 +35334,7 @@ async function normalizationRemove(path, options) {
35137
35334
  }
35138
35335
  await assertNormalizationCallerWrite(path);
35139
35336
  await removeLocal(path, options);
35140
- if (normalizationIdentities().length) await syncMetadataPath(dirname39(path), true);
35337
+ if (normalizationIdentities().length) await syncMetadataPath(dirname40(path), true);
35141
35338
  }
35142
35339
  async function normalizationWriteJson(path, data) {
35143
35340
  const transport = normalizationTransport(path);
@@ -35151,7 +35348,7 @@ async function normalizationWriteJson(path, data) {
35151
35348
  }
35152
35349
  await assertNormalizationCallerWrite(path);
35153
35350
  await assertNoSymlinkPath2(path);
35154
- await mkdir27(dirname39(path), { recursive: true, mode: 448 });
35351
+ await mkdir27(dirname40(path), { recursive: true, mode: 448 });
35155
35352
  await assertNormalizationCallerWrite(path);
35156
35353
  const temporary = `${path}.pending-${randomUUID8()}`;
35157
35354
  try {
@@ -35165,8 +35362,8 @@ async function normalizationWriteJson(path, data) {
35165
35362
  await file2.close();
35166
35363
  }
35167
35364
  await rename9(temporary, path);
35168
- await syncMetadataPath(dirname39(path), true);
35169
- await syncMetadataPath(dirname39(dirname39(path)), true);
35365
+ await syncMetadataPath(dirname40(path), true);
35366
+ await syncMetadataPath(dirname40(dirname40(path)), true);
35170
35367
  } finally {
35171
35368
  await removeLocal(temporary, { force: true });
35172
35369
  }
@@ -36217,7 +36414,7 @@ async function releasedTargetStateForGraph(scope, graph, identityFleetId = scope
36217
36414
  ...current,
36218
36415
  stateKey,
36219
36416
  targetFingerprint,
36220
- graphLockPath: join65(dirname40(graph.path), `${targetFingerprint}.graph-lock.json`),
36417
+ graphLockPath: join65(dirname41(graph.path), `${targetFingerprint}.graph-lock.json`),
36221
36418
  manifestPath: installManifestPath(current.installRoot, current.adapter, {
36222
36419
  installationType: current.installationType,
36223
36420
  stateKey
@@ -37471,7 +37668,7 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
37471
37668
  const adapter = await resolveAdapterForTarget(target, {});
37472
37669
  const transport = transportForTarget(target);
37473
37670
  const requestedReport = normalizeCliPath(options.report);
37474
- const reportPath = join66(await realpath4(dirname41(requestedReport)), basename24(requestedReport));
37671
+ const reportPath = join66(await realpath4(dirname42(requestedReport)), basename24(requestedReport));
37475
37672
  const reportInsideTarget = relative22(resolve48(target.targetRoot), reportPath);
37476
37673
  if (reportInsideTarget === "" || !reportInsideTarget.startsWith("..") && !isAbsolute17(reportInsideTarget)) {
37477
37674
  throw new Error("Recovery report must be outside the runtime target root.");
@@ -37499,20 +37696,43 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
37499
37696
  console.log(JSON.stringify(report.counts));
37500
37697
  });
37501
37698
  });
37502
- ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").option("--from-fleet-id <id>", "exact stale Fleet owner id for same-Fleet duplicate state").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").requiredOption("--fleet <id>", "registered destination Fleet").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
37699
+ ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").option("--from-fleet-id <id>", "exact stale Fleet owner id for same-Fleet duplicate state").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").option("--to-fleet <id>", "registered destination Fleet owner, independent of target workspace scope").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet (also the destination owner unless --to-fleet is set)").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
37503
37700
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
37504
- const fleet = await showRegisteredFleet(options.fleet);
37701
+ const destinationFleet = options.toFleet ?? options.fleet;
37702
+ if (!destinationFleet) throw new Error("Stale ownership retirement requires --to-fleet <id> or the legacy --fleet <id> scope.");
37703
+ const fleet = await showRegisteredFleet(destinationFleet);
37505
37704
  const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
37506
37705
  if (resolve48(fleet.root) !== resolve48(toWorkspaceRoot)) {
37507
- throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
37706
+ throw new Error(`Destination Fleet '${destinationFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
37707
+ }
37708
+ const assertDestinationWorkspace = async (workspaceRoot, fleetId) => {
37709
+ const fleetRoot = resolve48(fleet.root);
37710
+ const canonicalFleetRoot = await realpath4(fleetRoot);
37711
+ const within = (root, path) => {
37712
+ const suffix = relative22(root, path);
37713
+ return suffix === "" || suffix !== ".." && !suffix.startsWith(`..${sep12}`) && !isAbsolute17(suffix);
37714
+ };
37715
+ if (canonicalFleetRoot !== fleetRoot || fleetId !== void 0 && fleetId !== fleet.id || !within(fleetRoot, resolve48(workspaceRoot)) || !within(canonicalFleetRoot, await realpath4(workspaceRoot))) {
37716
+ throw new Error(`Resolved target workspace does not belong to destination Fleet '${fleet.id}'.`);
37717
+ }
37718
+ };
37719
+ if (options.toFleet) {
37720
+ await resolveWorkspaceScope({ fleet: fleet.id });
37721
+ if (normalizedOptions.targetRoot && !normalizedOptions.workspaceTargetDerived) {
37722
+ await assertDestinationWorkspace(normalizedOptions.targetRoot);
37723
+ }
37724
+ const scope = await resolveCliWorkspaceScope(normalizedOptions);
37725
+ await assertDestinationWorkspace(scope.root, scope.fleetId);
37508
37726
  }
37509
37727
  const targets = await resolveCliTargets(normalizedOptions);
37510
37728
  if (targets.length !== 1) {
37511
37729
  throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
37512
37730
  }
37513
37731
  const target = targets[0];
37514
- if (target.fleetId !== options.fleet || resolve48(target.workspaceRoot) !== resolve48(fleet.root)) {
37515
- throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
37732
+ if (options.toFleet) {
37733
+ await assertDestinationWorkspace(target.workspaceRoot, target.fleetId);
37734
+ } else if (target.fleetId !== fleet.id || resolve48(target.workspaceRoot) !== resolve48(fleet.root)) {
37735
+ throw new Error(`Resolved target does not belong to destination Fleet '${fleet.id}'.`);
37516
37736
  }
37517
37737
  const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
37518
37738
  const adapter = await resolveAdapterForTarget(target, adapterOptions);
@@ -37536,7 +37756,8 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
37536
37756
  transport: transportForTarget(target)
37537
37757
  };
37538
37758
  const result = options.apply ? await applyRetireStaleOwnership(request) : await planRetireStaleOwnership(request);
37539
- const targetArgs = options.profile ? ["--profile", options.profile] : target.agentName ? ["--agent", target.agentName] : ["--adapter", adapter.name, "--target-root", target.targetRoot];
37759
+ const workspaceArgs = options.toFleet ? options.fleet ? ["--fleet", options.fleet] : options.user ? ["--user"] : options.targetRoot ? [] : ["--local"] : ["--fleet", fleet.id];
37760
+ const targetArgs = options.profile ? ["--profile", options.profile] : target.agentName ? ["--agent", target.agentName] : ["--adapter", adapter.name, ...workspaceArgs.length ? [] : ["--target-root", target.targetRoot]];
37540
37761
  const applyArgs = [
37541
37762
  "agentwheel",
37542
37763
  "ownership",
@@ -37550,8 +37771,9 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
37550
37771
  request.sourceStateKey,
37551
37772
  "--destination-state-key",
37552
37773
  request.destinationStateKey,
37553
- "--fleet",
37554
- request.toFleetId,
37774
+ ...options.toFleet ? ["--to-fleet", request.toFleetId] : [],
37775
+ ...workspaceArgs,
37776
+ ...(options.profile || target.agentName) && options.targetRoot ? ["--target-root", normalizedOptions.targetRoot] : [],
37555
37777
  ...targetArgs,
37556
37778
  "--installation-type",
37557
37779
  installationType,
@@ -38791,13 +39013,23 @@ function preserveUnrelatedGraphPackages(current, previous, selectedCurrentNodeId
38791
39013
  for (const root of selectedCurrentRoots) {
38792
39014
  if (!previous.canonical.roots.some((candidate) => candidate.rootId === root.rootId)) roots.push(root);
38793
39015
  }
39016
+ const preservedNodeIds = /* @__PURE__ */ new Set();
39017
+ if (selectedRootIds.size > 0) {
39018
+ for (const root of previous.canonical.roots) {
39019
+ if (!selectedRootIds.has(root.rootId)) {
39020
+ for (const id of graphRootClosure(previous, root.rootId)) preservedNodeIds.add(id);
39021
+ }
39022
+ }
39023
+ }
39024
+ const replacedOwners = new Set([...selectedPreviousNodeIds].filter((id) => !preservedNodeIds.has(id)));
39025
+ for (const id of selectedRootIds) replacedOwners.add(`workspace:${id}`);
38794
39026
  const currentNodes = current.canonical.nodes.filter((node) => selectedCurrentNodeIds.has(node.id));
38795
- const previousNodes = previous.canonical.nodes.filter((node) => !selectedPreviousNodeIds.has(node.id));
39027
+ const previousNodes = previous.canonical.nodes.filter((node) => !selectedPreviousNodeIds.has(node.id) || preservedNodeIds.has(node.id)).map((node) => ({ ...node, requiredBy: node.requiredBy.filter((owner) => !replacedOwners.has(owner)) }));
38796
39028
  const selectedCurrentArtifacts = current.canonical.artifacts.filter((artifact) => selectedCurrentNodeIds.has(artifact.graphNodeId));
38797
- const previousArtifacts = previous.canonical.artifacts.filter((artifact) => !selectedPreviousNodeIds.has(artifact.graphNodeId));
39029
+ const previousArtifacts = previous.canonical.artifacts.filter((artifact) => !selectedPreviousNodeIds.has(artifact.graphNodeId) || preservedNodeIds.has(artifact.graphNodeId)).map((artifact) => ({ ...artifact, owners: artifact.owners.filter((owner) => !replacedOwners.has(owner)) }));
38798
39030
  const edgeTouchesSelected = (from, to) => selectedCurrentNodeIds.has(from) || selectedCurrentNodeIds.has(to);
38799
- const previousEdge = (edge) => !selectedPreviousNodeIds.has(edge.from) && !selectedPreviousNodeIds.has(edge.to);
38800
- const previousIncludeEdge = (edge) => !selectedPreviousNodeIds.has(edge.fromNodeId) && !selectedPreviousNodeIds.has(edge.toNodeId);
39031
+ const previousEdge = (edge) => preservedNodeIds.has(edge.from) || !selectedPreviousNodeIds.has(edge.from) && !selectedPreviousNodeIds.has(edge.to);
39032
+ const previousIncludeEdge = (edge) => preservedNodeIds.has(edge.fromNodeId) || !selectedPreviousNodeIds.has(edge.fromNodeId) && !selectedPreviousNodeIds.has(edge.toNodeId);
38801
39033
  const selectedCurrentRootIds = new Set(selectedCurrentRoots.map((root) => root.graphNodeId));
38802
39034
  const currentRootOrNode = (id) => selectedCurrentNodeIds.has(id) || selectedCurrentRootIds.has(id);
38803
39035
  const previousRootOrNode = (id) => selectedPreviousNodeIds.has(id) || previous.canonical.roots.some((root) => selectedRootIds.has(root.rootId) && root.graphNodeId === id);
@@ -38806,28 +39038,38 @@ function preserveUnrelatedGraphPackages(current, previous, selectedCurrentNodeId
38806
39038
  canonical: {
38807
39039
  ...current.canonical,
38808
39040
  roots,
38809
- nodes: [...currentNodes, ...previousNodes],
38810
- edges: [
39041
+ nodes: uniqueBy([...currentNodes.map((node) => {
39042
+ const old = previousNodes.find((candidate) => candidate.id === node.id);
39043
+ return old ? {
39044
+ ...node,
39045
+ requiredBy: sortedUniqueValues([...node.requiredBy, ...old.requiredBy]),
39046
+ selected: sortedUniqueValues([...node.selected, ...old.selected])
39047
+ } : node;
39048
+ }), ...previousNodes], (node) => node.id),
39049
+ edges: uniqueBy([
38811
39050
  ...current.canonical.edges.filter((edge) => edgeTouchesSelected(edge.from, edge.to)),
38812
39051
  ...previous.canonical.edges.filter(previousEdge)
38813
- ],
38814
- includeEdges: [
39052
+ ], (edge) => `${edge.from}\0${edge.alias}\0${edge.to}`),
39053
+ includeEdges: uniqueBy([
38815
39054
  ...current.canonical.includeEdges.filter((edge) => edgeTouchesSelected(edge.fromNodeId, edge.toNodeId)),
38816
39055
  ...previous.canonical.includeEdges.filter(previousIncludeEdge)
38817
- ],
38818
- artifacts: [...selectedCurrentArtifacts, ...previousArtifacts],
38819
- namespacing: [
39056
+ ], graphIncludeEdgeIdentity),
39057
+ artifacts: uniqueBy([...selectedCurrentArtifacts.map((artifact) => {
39058
+ const old = previousArtifacts.find((candidate) => graphArtifactIdentity2(candidate) === graphArtifactIdentity2(artifact));
39059
+ return old ? { ...artifact, owners: sortedUniqueValues([...artifact.owners, ...old.owners]) } : artifact;
39060
+ }), ...previousArtifacts], graphArtifactIdentity2),
39061
+ namespacing: uniqueBy([
38820
39062
  ...current.canonical.namespacing.filter((entry) => currentRootOrNode(entry.graphNodeId)),
38821
- ...previous.canonical.namespacing.filter((entry) => !previousRootOrNode(entry.graphNodeId))
38822
- ],
38823
- overrides: [
39063
+ ...previous.canonical.namespacing.filter((entry) => !previousRootOrNode(entry.graphNodeId) || preservedNodeIds.has(entry.graphNodeId))
39064
+ ], (entry) => `${entry.graphNodeId}\0${entry.type}\0${entry.name}`),
39065
+ overrides: uniqueBy([
38824
39066
  ...current.canonical.overrides.filter((entry) => currentRootOrNode(entry.graphNodeId) || currentRootOrNode(entry.overriddenGraphNodeId)),
38825
- ...previous.canonical.overrides.filter((entry) => !previousRootOrNode(entry.graphNodeId) && !previousRootOrNode(entry.overriddenGraphNodeId))
38826
- ],
38827
- plainNameIncumbents: [
39067
+ ...previous.canonical.overrides.filter((entry) => !selectedRootIds.has(entry.rootId) && (!previousRootOrNode(entry.graphNodeId) && !previousRootOrNode(entry.overriddenGraphNodeId) || preservedNodeIds.has(entry.graphNodeId)))
39068
+ ], (entry) => `${entry.rootId}\0${entry.selector}`),
39069
+ plainNameIncumbents: uniqueBy([
38828
39070
  ...current.canonical.plainNameIncumbents.filter((entry) => currentRootOrNode(entry.graphNodeId)),
38829
- ...previous.canonical.plainNameIncumbents.filter((entry) => !previousRootOrNode(entry.graphNodeId))
38830
- ]
39071
+ ...previous.canonical.plainNameIncumbents.filter((entry) => !previousRootOrNode(entry.graphNodeId) || preservedNodeIds.has(entry.graphNodeId))
39072
+ ], (entry) => `${entry.adapter}\0${entry.targetFingerprint}\0${entry.type}\0${entry.name}`)
38831
39073
  }
38832
39074
  });
38833
39075
  }
@@ -39157,16 +39399,31 @@ function uniqueBy(values, key) {
39157
39399
  return true;
39158
39400
  });
39159
39401
  }
39160
- function scopeInstallPlanToRoot(result, rootId, manifest) {
39402
+ function scopeInstallPlanToRoot(result, rootId, manifest, previousLock) {
39161
39403
  const scopedOwners = scopedGraphOwnerKeys(result, rootId);
39404
+ const exclusiveOwners = new Set(scopedOwners);
39405
+ if (previousLock) {
39406
+ for (const id of graphRootClosure(previousLock, rootId)) exclusiveOwners.add(id);
39407
+ for (const root of previousLock.canonical.roots) {
39408
+ if (root.rootId === rootId) continue;
39409
+ for (const id of graphRootClosure(previousLock, root.rootId)) exclusiveOwners.delete(id);
39410
+ }
39411
+ }
39162
39412
  const manifestByPath = new Map((manifest?.entries ?? []).map((entry) => [entry.path, entry]));
39163
39413
  const preservedPaths = /* @__PURE__ */ new Set();
39164
39414
  const plannedPaths = /* @__PURE__ */ new Set();
39165
39415
  const operations = [];
39166
39416
  for (const operation of result.plan.operations) {
39167
39417
  plannedPaths.add(operation.relativeDestPath);
39168
- if (operationBelongsToScopedRoot(operation, scopedOwners)) {
39169
- operations.push(operation);
39418
+ const stale = operation.action === "remove" || operation.action === "drift" && !operation.desiredHash;
39419
+ const exclusivelyOwned = operation.owners?.length && operation.owners.every((owner) => exclusiveOwners.has(owner));
39420
+ if (stale ? exclusivelyOwned : operationBelongsToScopedRoot(operation, scopedOwners)) {
39421
+ const entry2 = manifestByPath.get(operation.relativeDestPath);
39422
+ const foreignOwners = entry2 && "owners" in entry2 && Array.isArray(entry2.owners) ? entry2.owners.filter((owner) => typeof owner === "string" && !exclusiveOwners.has(owner)) : [];
39423
+ operations.push(stale || !operation.owners ? operation : {
39424
+ ...operation,
39425
+ owners: sortedUniqueValues([...operation.owners, ...foreignOwners])
39426
+ });
39170
39427
  continue;
39171
39428
  }
39172
39429
  const entry = manifestByPath.get(operation.relativeDestPath);
@@ -39192,7 +39449,7 @@ function scopeInstallPlanToRoot(result, rootId, manifest) {
39192
39449
  };
39193
39450
  }
39194
39451
  function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
39195
- const scoped = scopeInstallPlanToRoot(result, rootId, manifest);
39452
+ const scoped = scopeInstallPlanToRoot(result, rootId, manifest, previousLock);
39196
39453
  const selectedCurrentNodeIds = graphRootClosure(scoped.bundle.graphLock, rootId);
39197
39454
  const selectedPreviousNodeIds = graphRootClosure(previousLock, rootId);
39198
39455
  const graphLock = preserveUnrelatedGraphPackages(
@@ -40434,7 +40691,7 @@ async function initPackage(root) {
40434
40691
  await writeFile24(instructionsPath, "# Agent Instructions\n", "utf8");
40435
40692
  }
40436
40693
  async function defaultBootstrapPackage(_root) {
40437
- const packageRoot = await findAgentwheelPackageRoot(dirname41(fileURLToPath4(import.meta.url)));
40694
+ const packageRoot = await findAgentwheelPackageRoot(dirname42(fileURLToPath4(import.meta.url)));
40438
40695
  if (!packageRoot) return void 0;
40439
40696
  return {
40440
40697
  name: "agentwheel",
@@ -40481,7 +40738,7 @@ async function findAgentwheelPackageRoot(start) {
40481
40738
  let current = resolve48(start);
40482
40739
  while (true) {
40483
40740
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
40484
- const parent = dirname41(current);
40741
+ const parent = dirname42(current);
40485
40742
  if (parent === current) return void 0;
40486
40743
  current = parent;
40487
40744
  }