agentwheel 0.20.1 → 0.20.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.
@@ -2,14 +2,16 @@
2
2
  import {
3
3
  applyCombinedInstallPlan,
4
4
  applyInstallPlan,
5
+ commitManifestMetadataJournal,
5
6
  recoverPendingApply,
6
7
  uninstall
7
- } from "./chunk-AFVGRRZW.js";
8
- import "./chunk-BJEZXIXX.js";
8
+ } from "./chunk-BAJEWUI6.js";
9
+ import "./chunk-Z4N7TB6Q.js";
9
10
  import "./chunk-7VPI5J5Y.js";
10
11
  export {
11
12
  applyCombinedInstallPlan,
12
13
  applyInstallPlan,
14
+ commitManifestMetadataJournal,
13
15
  recoverPendingApply,
14
16
  uninstall
15
17
  };
@@ -24,7 +24,7 @@ import {
24
24
  rollbackCompletedOperations,
25
25
  sourceLockPath,
26
26
  writeApplyJournal
27
- } from "./chunk-BJEZXIXX.js";
27
+ } from "./chunk-Z4N7TB6Q.js";
28
28
  import {
29
29
  pathExists,
30
30
  writeJsonAtomic
@@ -1607,6 +1607,12 @@ function shouldPreserveKeptOperationWhenKeepingFiles(operation) {
1607
1607
  function isForceRemovableKeep(operation) {
1608
1608
  return operation.action === "keep" && operation.preserveInManifest !== true;
1609
1609
  }
1610
+ async function commitManifestMetadataJournal(journal, transport = localTransport) {
1611
+ if (journal.mode !== "uninstall" || journal.operations.length !== 0) {
1612
+ throw new Error("Manifest metadata journal must contain no runtime operations.");
1613
+ }
1614
+ return commitJournalState(journal, transport, void 0, journal.createdAt);
1615
+ }
1610
1616
  async function commitJournalState(journal, transport, entries, now) {
1611
1617
  const manifest = withManifestRevision({
1612
1618
  ...journal.manifest,
@@ -2291,6 +2297,7 @@ export {
2291
2297
  combineMergeRemovals,
2292
2298
  hasMergeRemovalContent,
2293
2299
  mergeRemovalForInstall,
2300
+ mergeContributionAbsent,
2294
2301
  normalizeOwners,
2295
2302
  managedInstructionBlockMode,
2296
2303
  desiredManagedInstructionBlockHash,
@@ -2301,5 +2308,6 @@ export {
2301
2308
  applyInstallPlan,
2302
2309
  applyCombinedInstallPlan,
2303
2310
  recoverPendingApply,
2304
- uninstall
2311
+ uninstall,
2312
+ commitManifestMetadataJournal
2305
2313
  };
@@ -2624,8 +2624,8 @@ async function recoverMutationRuntime(operationId, options = {}) {
2624
2624
  }
2625
2625
  mutation = GovernedMutation.activateExisting(receipt, baseline, lock);
2626
2626
  const [{ recoverPendingApply }, { readLinkedLocalApplyJournal: readLinkedLocalApplyJournal2, removeApplyJournal: removeApplyJournal2, localPathExists: localPathExists2 }] = await Promise.all([
2627
- import("./apply-R5VHCIUN.js"),
2628
- import("./transaction-ITXC7FFV.js")
2627
+ import("./apply-OGC6E2Y4.js"),
2628
+ import("./transaction-7CNV5MAQ.js")
2629
2629
  ]);
2630
2630
  let missingReservation = false;
2631
2631
  for (const entry of pending) {
@@ -2870,6 +2870,17 @@ async function listApplyJournals(targetRoot, adapter, transport = localTransport
2870
2870
  }
2871
2871
  return found.sort((a, b) => a.path.localeCompare(b.path));
2872
2872
  }
2873
+ async function listAllApplyJournals(targetRoot, adapter, transport = localTransport) {
2874
+ const suffix = ".apply-journal.json";
2875
+ const found = [];
2876
+ for (const fileName of await transport.listDir(metadataDir(targetRoot))) {
2877
+ if (!fileName.endsWith(suffix)) continue;
2878
+ const path = join6(metadataDir(targetRoot), fileName);
2879
+ const journal = parseApplyJournal(JSON.parse(await transport.readFile(path)));
2880
+ if (journal.adapter === adapter) found.push({ path, journal });
2881
+ }
2882
+ return found.sort((a, b) => a.path.localeCompare(b.path));
2883
+ }
2873
2884
  async function readLinkedLocalApplyJournal(path, operationId, expectedDigest, expectedTransportDescription) {
2874
2885
  const absolutePath = resolve9(path);
2875
2886
  const journal = parseApplyJournal(JSON.parse(await readFile7(absolutePath, "utf8")));
@@ -3150,6 +3161,7 @@ export {
3150
3161
  writeApplyJournal,
3151
3162
  readApplyJournal,
3152
3163
  listApplyJournals,
3164
+ listAllApplyJournals,
3153
3165
  readLinkedLocalApplyJournal,
3154
3166
  removeApplyJournal,
3155
3167
  abortApplyJournal,
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import {
9
9
  canonicalizeGraphLock,
10
10
  claudeInstructionBridgesAgents,
11
11
  combineMergeRemovals,
12
+ commitManifestMetadataJournal,
12
13
  computeInstallManifestInventoryRevision,
13
14
  computeManifestRevision,
14
15
  computeTargetFingerprint,
@@ -20,6 +21,7 @@ import {
20
21
  managedInstructionBlockMode,
21
22
  managedInstructionPhysicalKey,
22
23
  managedInstructionSelector,
24
+ mergeContributionAbsent,
23
25
  mergeRemovalForInstall,
24
26
  normalizeImmutableCacheIdentity,
25
27
  normalizeOwners,
@@ -31,7 +33,7 @@ import {
31
33
  uninstall,
32
34
  withManifestRevision,
33
35
  writeInstallManifest
34
- } from "./chunk-AFVGRRZW.js";
36
+ } from "./chunk-BAJEWUI6.js";
35
37
  import {
36
38
  CURRENT_WORKSPACE_SCHEMA_VERSION,
37
39
  GovernedMutation,
@@ -55,10 +57,12 @@ import {
55
57
  installRootForAdapterInstallationType,
56
58
  installRootForArtifacts,
57
59
  isCompositeWorkspaceProfile,
60
+ listAllApplyJournals,
58
61
  listApplyJournals,
59
62
  listMutationReceipts,
60
63
  loadAdapterConfig,
61
64
  localTransport,
65
+ mutationMetadataForApplyJournal,
62
66
  mutationPolicyForWorkspace,
63
67
  packageAssetSchema,
64
68
  packageComposeEntrySchema,
@@ -88,8 +92,9 @@ import {
88
92
  workspaceConfigSchema,
89
93
  workspaceExportsSchema,
90
94
  workspaceSelectionImportSchema,
95
+ writeApplyJournal,
91
96
  writeWorkspaceConfig
92
- } from "./chunk-BJEZXIXX.js";
97
+ } from "./chunk-Z4N7TB6Q.js";
93
98
  import {
94
99
  inferSourceDriverName
95
100
  } from "./chunk-EMDIG24I.js";
@@ -104,11 +109,11 @@ import {
104
109
  } from "./chunk-7VPI5J5Y.js";
105
110
 
106
111
  // src/cli/index.ts
107
- import { createHash as createHash13 } from "crypto";
112
+ import { createHash as createHash14 } from "crypto";
108
113
  import { existsSync } from "fs";
109
114
  import { mkdir as mkdir16, rm as rm10, writeFile as writeFile13 } from "fs/promises";
110
115
  import { homedir as homedir12 } from "os";
111
- import { dirname as dirname22, join as join45, resolve as resolve23 } from "path";
116
+ import { dirname as dirname22, join as join45, resolve as resolve24 } from "path";
112
117
  import { fileURLToPath as fileURLToPath3 } from "url";
113
118
  import { Command } from "commander";
114
119
 
@@ -3102,14 +3107,14 @@ async function writeMermaidAsset(response) {
3102
3107
  "content-type": "application/javascript; charset=utf-8",
3103
3108
  "cache-control": "public, max-age=3600"
3104
3109
  });
3105
- await new Promise((resolve24) => {
3110
+ await new Promise((resolve25) => {
3106
3111
  const stream = createReadStream(assetPath);
3107
3112
  stream.on("error", () => {
3108
3113
  if (!response.headersSent) writeText(response, 404, "Mermaid asset unavailable.\n");
3109
3114
  else response.destroy();
3110
- resolve24();
3115
+ resolve25();
3111
3116
  });
3112
- stream.on("end", resolve24);
3117
+ stream.on("end", resolve25);
3113
3118
  stream.pipe(response);
3114
3119
  });
3115
3120
  }
@@ -3136,14 +3141,14 @@ function escapeHtml2(value) {
3136
3141
  return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;");
3137
3142
  }
3138
3143
  function listen(server, port, bind) {
3139
- return new Promise((resolve24, reject) => {
3144
+ return new Promise((resolve25, reject) => {
3140
3145
  const onError = (error) => {
3141
3146
  server.off("listening", onListening);
3142
3147
  reject(error);
3143
3148
  };
3144
3149
  const onListening = () => {
3145
3150
  server.off("error", onError);
3146
- resolve24();
3151
+ resolve25();
3147
3152
  };
3148
3153
  server.once("error", onError);
3149
3154
  server.once("listening", onListening);
@@ -3151,7 +3156,7 @@ function listen(server, port, bind) {
3151
3156
  });
3152
3157
  }
3153
3158
  function waitForShutdown(server, beforeClose) {
3154
- return new Promise((resolve24) => {
3159
+ return new Promise((resolve25) => {
3155
3160
  let closing = false;
3156
3161
  const shutdown = () => {
3157
3162
  if (closing) return;
@@ -3159,7 +3164,7 @@ function waitForShutdown(server, beforeClose) {
3159
3164
  process.off("SIGINT", shutdown);
3160
3165
  process.off("SIGTERM", shutdown);
3161
3166
  beforeClose();
3162
- server.close(() => resolve24());
3167
+ server.close(() => resolve25());
3163
3168
  };
3164
3169
  process.once("SIGINT", shutdown);
3165
3170
  process.once("SIGTERM", shutdown);
@@ -3169,7 +3174,7 @@ function waitForShutdown(server, beforeClose) {
3169
3174
  process.off("SIGINT", shutdown);
3170
3175
  process.off("SIGTERM", shutdown);
3171
3176
  beforeClose();
3172
- resolve24();
3177
+ resolve25();
3173
3178
  });
3174
3179
  });
3175
3180
  }
@@ -3722,7 +3727,7 @@ async function fetchClawHubPackage(fetchImpl, packageName) {
3722
3727
  throw new Error(`ClawHub lookup failed for ${packageName}`);
3723
3728
  }
3724
3729
  async function delay(ms) {
3725
- await new Promise((resolve24) => setTimeout(resolve24, ms));
3730
+ await new Promise((resolve25) => setTimeout(resolve25, ms));
3726
3731
  }
3727
3732
  function parseClawHubSource(source) {
3728
3733
  if (!source.startsWith(sourcePrefix)) {
@@ -3893,7 +3898,7 @@ async function withGitCacheMaintenanceLock(cacheRoot, timeoutMs, fn) {
3893
3898
  if (Date.now() - started > timeoutMs) {
3894
3899
  throw new Error(`Timed out waiting for git cache maintenance lock at ${lockPath}`);
3895
3900
  }
3896
- await new Promise((resolve24) => setTimeout(resolve24, 50));
3901
+ await new Promise((resolve25) => setTimeout(resolve25, 50));
3897
3902
  }
3898
3903
  }
3899
3904
  try {
@@ -6684,8 +6689,8 @@ function verifyIntegrity(integrity, sourceHash, label) {
6684
6689
  async function withCachePathLock(path, fn) {
6685
6690
  const previous = cacheLocks.get(path) ?? Promise.resolve();
6686
6691
  let release = () => void 0;
6687
- const current = previous.then(() => new Promise((resolve24) => {
6688
- release = resolve24;
6692
+ const current = previous.then(() => new Promise((resolve25) => {
6693
+ release = resolve25;
6689
6694
  }));
6690
6695
  cacheLocks.set(path, current);
6691
6696
  await previous;
@@ -6761,8 +6766,8 @@ function detectDirectCollisions(nodes) {
6761
6766
  }
6762
6767
  }
6763
6768
  function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
6764
- const digest = createHash6("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
6765
- return `${name}@${version}+${digest}`;
6769
+ const digest2 = createHash6("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
6770
+ return `${name}@${version}+${digest2}`;
6766
6771
  }
6767
6772
  function sortedUnique2(values) {
6768
6773
  return [...new Set(values)].sort((a, b) => a.localeCompare(b));
@@ -9246,6 +9251,11 @@ async function verifiedEntryHash(entry, destPath, transport) {
9246
9251
  }
9247
9252
  return currentHash;
9248
9253
  }
9254
+ async function verifyManifestEntryRuntime(targetRoot, entry, transport = localTransport) {
9255
+ const path = containedArtifactPath(targetRoot, entry.path);
9256
+ if (!await transport.pathExists(path)) throw new Error(`Managed artifact is missing: ${entry.path}`);
9257
+ return verifiedEntryHash(entry, path, transport);
9258
+ }
9249
9259
  function containedArtifactPath(targetRoot, relativePath) {
9250
9260
  if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\0")) {
9251
9261
  throw new Error(`Unsafe managed artifact path: ${relativePath}`);
@@ -9282,10 +9292,259 @@ function assertManifestIdentity(manifest, request) {
9282
9292
  }
9283
9293
  }
9284
9294
 
9295
+ // src/lifecycle/ownership-retire-stale.ts
9296
+ import { createHash as createHash9 } from "crypto";
9297
+ import { resolve as resolve20 } from "path";
9298
+ async function planRetireStaleOwnership(request) {
9299
+ return observe(request, request.transport ?? localTransport);
9300
+ }
9301
+ async function applyRetireStaleOwnership(request) {
9302
+ assertApplyPreconditions(request);
9303
+ const transport = request.transport ?? localTransport;
9304
+ const lock = await acquireApplyLock(request.targetRoot, request.adapter, transport, {}, {
9305
+ installationType: request.installationType,
9306
+ stateKey: request.destinationStateKey
9307
+ });
9308
+ try {
9309
+ const pending = await listAllApplyJournals(request.targetRoot, request.adapter, transport);
9310
+ if (pending.length > 0) {
9311
+ throw new Error(`Cannot retire stale ownership while runtime apply journal(s) are pending: ${pending.map((item) => item.path).join(", ")}`);
9312
+ }
9313
+ const current = await observe(request, transport);
9314
+ assertExpected(request.planDigest, current.planDigest, "plan digest");
9315
+ assertExpected(request.expectedSourceRevision, current.source.revision, "source manifest revision");
9316
+ assertExpected(request.expectedDestinationRevision, current.destination.revision, "destination manifest revision");
9317
+ assertExpected(request.expectedInventoryRevision, current.manifestInventoryRevision, "manifest inventory revision");
9318
+ const source = await requireManifest(request, request.sourceStateKey, transport, "source");
9319
+ const selectedDigests = new Set(current.selected.map((entry) => entry.sourceEntryDigest));
9320
+ const retained = source.entries.filter((entry) => !selectedDigests.has(entryDigest(entry)));
9321
+ if (source.entries.length - retained.length !== current.selected.length) {
9322
+ throw new Error("Source manifest selection changed while locked; replan required.");
9323
+ }
9324
+ declareMutationPath(current.source.manifestPath);
9325
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9326
+ const mutation = mutationMetadataForApplyJournal();
9327
+ const journal = {
9328
+ version: mutation ? 2 : 1,
9329
+ ...mutation ? { mutation } : {},
9330
+ mode: "uninstall",
9331
+ adapter: request.adapter,
9332
+ installationType: request.installationType,
9333
+ stateKey: request.sourceStateKey,
9334
+ targetRoot: request.targetRoot,
9335
+ baseRevision: source.revision,
9336
+ createdAt: now,
9337
+ updatedAt: now,
9338
+ operations: [],
9339
+ completed: [],
9340
+ manifest: { ...source, entries: retained }
9341
+ };
9342
+ await writeApplyJournal(journal, transport);
9343
+ await commitManifestMetadataJournal(journal, transport);
9344
+ return {
9345
+ ...current,
9346
+ applied: true,
9347
+ sourceManifestRemoved: retained.length === 0,
9348
+ retainedSourceEntries: retained.length
9349
+ };
9350
+ } finally {
9351
+ await lock.release();
9352
+ }
9353
+ }
9354
+ async function observe(request, transport) {
9355
+ const normalized = normalizeRequest(request);
9356
+ const source = await requireManifest(normalized, normalized.sourceStateKey, transport, "source");
9357
+ const destination = await requireManifest(normalized, normalized.destinationStateKey, transport, "destination");
9358
+ const sourceOwner = workspaceOwnerForRoot(normalized.fromWorkspaceRoot);
9359
+ const destinationOwner = workspaceOwnerForRoot(normalized.toWorkspaceRoot, normalized.toFleetId);
9360
+ const destinationByPath = /* @__PURE__ */ new Map();
9361
+ for (const entry of destination.entries) {
9362
+ if (entry.workspaceOwner !== destinationOwner) continue;
9363
+ const entries = destinationByPath.get(entry.path) ?? [];
9364
+ entries.push(entry);
9365
+ destinationByPath.set(entry.path, entries);
9366
+ }
9367
+ const selected = [];
9368
+ for (const sourceEntry of source.entries) {
9369
+ if (sourceEntry.workspaceOwner !== sourceOwner) continue;
9370
+ const candidates = destinationByPath.get(sourceEntry.path) ?? [];
9371
+ if (candidates.length === 0) continue;
9372
+ if (candidates.length !== 1) {
9373
+ throw new Error(`Destination Fleet manifest has ambiguous ownership for ${sourceEntry.path}.`);
9374
+ }
9375
+ assertRetirableSourceEntry(sourceEntry, normalized.abandonIncompleteMergeOwner === true);
9376
+ const destinationEntry = candidates[0];
9377
+ const coverage = await assertContributionCoverage(
9378
+ sourceEntry,
9379
+ destinationEntry,
9380
+ normalized.targetRoot,
9381
+ transport,
9382
+ normalized.abandonIncompleteMergeOwner === true
9383
+ );
9384
+ const runtimeHash = coverage === "abandoned-incomplete-merge" ? await transport.hashPath(containedArtifactPath(normalized.targetRoot, destinationEntry.path)) : await verifyManifestEntryRuntime(normalized.targetRoot, destinationEntry, transport);
9385
+ selected.push({
9386
+ path: sourceEntry.path,
9387
+ sourceEntryDigest: entryDigest(sourceEntry),
9388
+ destinationEntryDigest: entryDigest(destinationEntry),
9389
+ runtimeHash,
9390
+ coverage
9391
+ });
9392
+ }
9393
+ selected.sort((a, b) => a.path.localeCompare(b.path) || a.sourceEntryDigest.localeCompare(b.sourceEntryDigest) || a.destinationEntryDigest.localeCompare(b.destinationEntryDigest));
9394
+ if (selected.length === 0) {
9395
+ throw new Error("No stale source ownership entries match an exact path in the destination Fleet manifest.");
9396
+ }
9397
+ const withoutDigest = {
9398
+ adapter: normalized.adapter,
9399
+ installationType: normalized.installationType,
9400
+ targetRoot: normalized.targetRoot,
9401
+ source: {
9402
+ stateKey: normalized.sourceStateKey,
9403
+ manifestPath: installManifestPath(normalized.targetRoot, normalized.adapter, {
9404
+ installationType: normalized.installationType,
9405
+ stateKey: normalized.sourceStateKey
9406
+ }),
9407
+ revision: source.revision,
9408
+ owner: sourceOwner
9409
+ },
9410
+ destination: {
9411
+ stateKey: normalized.destinationStateKey,
9412
+ manifestPath: installManifestPath(normalized.targetRoot, normalized.adapter, {
9413
+ installationType: normalized.installationType,
9414
+ stateKey: normalized.destinationStateKey
9415
+ }),
9416
+ revision: destination.revision,
9417
+ owner: destinationOwner,
9418
+ fleetId: normalized.toFleetId
9419
+ },
9420
+ manifestInventoryRevision: await computeInstallManifestInventoryRevision(
9421
+ normalized.targetRoot,
9422
+ normalized.adapter,
9423
+ transport
9424
+ ),
9425
+ selected
9426
+ };
9427
+ return { ...withoutDigest, planDigest: digest(withoutDigest) };
9428
+ }
9429
+ function normalizeRequest(request) {
9430
+ const targetRoot = resolve20(request.targetRoot);
9431
+ const sourceStateKey = exactStateKey(request.adapter, request.sourceStateKey, request.installationType, "source");
9432
+ const destinationStateKey = exactStateKey(request.adapter, request.destinationStateKey, request.installationType, "destination");
9433
+ if (sourceStateKey === destinationStateKey) throw new Error("Source and destination state keys must differ.");
9434
+ if (!request.toFleetId.trim()) throw new Error("Destination Fleet id is required.");
9435
+ return {
9436
+ ...request,
9437
+ targetRoot,
9438
+ installationType: request.installationType.trim(),
9439
+ sourceStateKey,
9440
+ destinationStateKey,
9441
+ fromWorkspaceRoot: resolve20(request.fromWorkspaceRoot),
9442
+ toWorkspaceRoot: resolve20(request.toWorkspaceRoot),
9443
+ toFleetId: request.toFleetId.trim()
9444
+ };
9445
+ }
9446
+ function exactStateKey(adapter, value, installationType, label) {
9447
+ if (!value || stateKeyFor(adapter, { installationType, stateKey: value }) !== value) {
9448
+ throw new Error(`The ${label} state key must be an exact canonical state key.`);
9449
+ }
9450
+ return value;
9451
+ }
9452
+ async function requireManifest(request, stateKey, transport, label) {
9453
+ const manifest = await readInstallManifest(request.targetRoot, request.adapter, transport, {
9454
+ installationType: request.installationType,
9455
+ stateKey
9456
+ });
9457
+ if (!manifest) throw new Error(`No ${label} install manifest for state key ${stateKey}.`);
9458
+ if (manifest.version !== 2) throw new Error(`${label} ownership retirement requires an Agentwheel v2 install manifest.`);
9459
+ const actualStateKey = stateKeyFor(manifest.adapter, {
9460
+ installationType: manifest.installationType,
9461
+ stateKey: manifest.stateKey
9462
+ });
9463
+ if (manifest.adapter !== request.adapter || manifest.installationType !== request.installationType || resolve20(manifest.targetRoot) !== request.targetRoot || actualStateKey !== stateKey) {
9464
+ throw new Error(`${label} install manifest identity does not match the requested adapter, target, installation type, and state key.`);
9465
+ }
9466
+ return manifest;
9467
+ }
9468
+ function assertRetirableSourceEntry(entry, abandonIncompleteMergeOwner) {
9469
+ if (entry.semanticPlugin || entry.semanticCommand || entry.executed) {
9470
+ throw new Error(`Cannot retire semantic source ownership at ${entry.path}.`);
9471
+ }
9472
+ if (entry.mergeStrategy && !hasMergeRemovalContent(entry.mergeRemoval)) {
9473
+ if (abandonIncompleteMergeOwner) return;
9474
+ throw new Error(`Cannot retire incomplete merge ownership at ${entry.path}.`);
9475
+ }
9476
+ }
9477
+ async function assertContributionCoverage(source, destination, targetRoot, transport, abandonIncompleteMergeOwner) {
9478
+ const sourceManagedBlock = source.mode === "managed-block";
9479
+ const destinationManagedBlock = destination.mode === "managed-block";
9480
+ const sourceMerge = source.mergeStrategy !== void 0;
9481
+ const destinationMerge = destination.mergeStrategy !== void 0;
9482
+ const sourceCategory = sourceManagedBlock ? "managed-block" : sourceMerge ? "merge" : "plain";
9483
+ const destinationCategory = destinationManagedBlock ? "managed-block" : destinationMerge ? "merge" : "plain";
9484
+ if (sourceCategory !== destinationCategory) {
9485
+ throw new Error(
9486
+ `Destination Fleet ownership category ${destinationCategory} cannot replace stale source category ${sourceCategory} at ${source.path}.`
9487
+ );
9488
+ }
9489
+ if (sourceCategory === "plain") return "exact";
9490
+ if (sourceManagedBlock) {
9491
+ const sourceSelector = managedInstructionSelector(source.logicalSelector, source.artifactType, source.artifactName);
9492
+ const destinationSelector = managedInstructionSelector(destination.logicalSelector, destination.artifactType, destination.artifactName);
9493
+ if (!sourceManagedBlock || !destinationManagedBlock || sourceSelector !== destinationSelector) {
9494
+ throw new Error(`Destination Fleet does not exactly cover stale source managed block contribution at ${source.path}.`);
9495
+ }
9496
+ return "exact";
9497
+ }
9498
+ if (source.mergeStrategy && destination.mergeStrategy) {
9499
+ if (!hasMergeRemovalContent(source.mergeRemoval)) {
9500
+ if (abandonIncompleteMergeOwner) return "abandoned-incomplete-merge";
9501
+ throw new Error(`Cannot retire incomplete merge ownership at ${source.path}.`);
9502
+ }
9503
+ const exactlyCovered = destination.mergeStrategy === source.mergeStrategy && hasMergeRemovalContent(destination.mergeRemoval) && contributionSelector(source) === contributionSelector(destination) && canonicalJson(source.mergeRemoval) === canonicalJson(destination.mergeRemoval);
9504
+ if (exactlyCovered) return "exact";
9505
+ const current = await transport.readFile(containedArtifactPath(targetRoot, source.path));
9506
+ if (!mergeContributionAbsent(source.mergeRemoval, source.mergeStrategy, current)) {
9507
+ throw new Error(`Destination Fleet does not exactly cover or replace stale source merge contribution at ${source.path}.`);
9508
+ }
9509
+ return "exact";
9510
+ }
9511
+ throw new Error(`Unsupported ownership coverage at ${source.path}.`);
9512
+ }
9513
+ function contributionSelector(entry) {
9514
+ return entry.logicalSelector ?? `${entry.artifactType}/${entry.artifactName}`;
9515
+ }
9516
+ function assertApplyPreconditions(request) {
9517
+ for (const [label, value] of [
9518
+ ["plan digest", request.planDigest],
9519
+ ["source manifest revision", request.expectedSourceRevision],
9520
+ ["destination manifest revision", request.expectedDestinationRevision],
9521
+ ["manifest inventory revision", request.expectedInventoryRevision]
9522
+ ]) {
9523
+ if (!value || !/^[a-f0-9]{64}$/.test(value)) {
9524
+ throw new Error(`Applying stale ownership retirement requires a reviewed lowercase SHA-256 ${label}.`);
9525
+ }
9526
+ }
9527
+ }
9528
+ function assertExpected(expected, current, label) {
9529
+ if (expected !== current) throw new Error(`Stale ${label}: expected ${expected}, found ${current}; replan required.`);
9530
+ }
9531
+ function entryDigest(entry) {
9532
+ return digest(entry);
9533
+ }
9534
+ function digest(value) {
9535
+ return createHash9("sha256").update(canonicalJson(value)).digest("hex");
9536
+ }
9537
+ function canonicalJson(value) {
9538
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
9539
+ if (!value || typeof value !== "object") return JSON.stringify(value);
9540
+ const record = value;
9541
+ return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
9542
+ }
9543
+
9285
9544
  // src/version/policy.ts
9286
9545
  import { execFile as execFile3 } from "child_process";
9287
9546
  import { readFile as readFile23 } from "fs/promises";
9288
- import { join as join39, resolve as resolve20 } from "path";
9547
+ import { join as join39, resolve as resolve21 } from "path";
9289
9548
  import { promisify as promisify3 } from "util";
9290
9549
  import { parse as parseJsonc } from "jsonc-parser";
9291
9550
  import { z as z5 } from "zod";
@@ -9379,7 +9638,7 @@ async function discoverVersionsFromSource(pkg, workspaceRoot) {
9379
9638
  if (tagged.length > 0) return tagged;
9380
9639
  }
9381
9640
  if (driverName === "local") {
9382
- const root = resolve20(workspaceRoot, pkg.source);
9641
+ const root = resolve21(workspaceRoot, pkg.source);
9383
9642
  const manifest2 = await readPackageManifest(root);
9384
9643
  const current = manifest2 ? [{ version: manifest2.version, ref: pkg.requestedRef ?? root }] : [];
9385
9644
  try {
@@ -9502,7 +9761,7 @@ async function readVersionCache(path) {
9502
9761
  // src/profile/members.ts
9503
9762
  import { execFile as execFile4 } from "child_process";
9504
9763
  import { readFile as readFile24 } from "fs/promises";
9505
- import { join as join40, resolve as resolve21 } from "path";
9764
+ import { join as join40, resolve as resolve22 } from "path";
9506
9765
  import { promisify as promisify4 } from "util";
9507
9766
  import { z as z7 } from "zod";
9508
9767
 
@@ -9677,7 +9936,7 @@ async function invokeMemberStatus(member, parentWorkspace, chain, options, cliEn
9677
9936
  let stderr = "";
9678
9937
  try {
9679
9938
  if (member.transport === "local") {
9680
- const workspace = resolve21(parentWorkspace, member.workspace);
9939
+ const workspace = resolve22(parentWorkspace, member.workspace);
9681
9940
  const result = await execFileAsync4(process.execPath, [cliEntry, ...args], {
9682
9941
  cwd: workspace,
9683
9942
  env: env2,
@@ -9723,7 +9982,7 @@ async function runMemberAgentwheel(member, parentWorkspace, args, chain) {
9723
9982
  process.execPath,
9724
9983
  [process.argv[1], "--no-update-check", ...args],
9725
9984
  {
9726
- cwd: resolve21(parentWorkspace, member.workspace),
9985
+ cwd: resolve22(parentWorkspace, member.workspace),
9727
9986
  env: env2,
9728
9987
  maxBuffer: 20 * 1024 * 1024
9729
9988
  }
@@ -9824,7 +10083,7 @@ function assertNoCompositeCycle(workspaceRoot, profileName, chain) {
9824
10083
  }
9825
10084
  }
9826
10085
  function compositeKey(workspaceRoot, profileName) {
9827
- return `${resolve21(workspaceRoot)}#${profileName}`;
10086
+ return `${resolve22(workspaceRoot)}#${profileName}`;
9828
10087
  }
9829
10088
 
9830
10089
  // src/status/repository.ts
@@ -9871,7 +10130,7 @@ function valueAfter(lines, prefix) {
9871
10130
  }
9872
10131
 
9873
10132
  // src/catalogue/client.ts
9874
- import { createHash as createHash9 } from "crypto";
10133
+ import { createHash as createHash10 } from "crypto";
9875
10134
  import { readFile as readFile25, rm as rm8 } from "fs/promises";
9876
10135
  import { homedir as homedir9 } from "os";
9877
10136
  import { join as join41 } from "path";
@@ -10146,7 +10405,7 @@ var CatalogueClient = class {
10146
10405
  }
10147
10406
  return {
10148
10407
  value: schema.parse(value),
10149
- digest: createHash9("sha256").update(bytes).digest("hex")
10408
+ digest: createHash10("sha256").update(bytes).digest("hex")
10150
10409
  };
10151
10410
  }
10152
10411
  };
@@ -10157,7 +10416,7 @@ function sameSources2(a, b) {
10157
10416
  return a.length === b.length && a.every((source, index) => source === b[index]);
10158
10417
  }
10159
10418
  function catalogueContentHash(enriched, vercel) {
10160
- return createHash9("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
10419
+ return createHash10("sha256").update(JSON.stringify({ enriched, vercel })).digest("hex");
10161
10420
  }
10162
10421
 
10163
10422
  // src/search/index.ts
@@ -10519,7 +10778,7 @@ function includesToken(normalizedText, token) {
10519
10778
  }
10520
10779
 
10521
10780
  // src/semantic/index.ts
10522
- import { createHash as createHash10 } from "crypto";
10781
+ import { createHash as createHash11 } from "crypto";
10523
10782
  import { homedir as homedir10 } from "os";
10524
10783
  import { join as join42 } from "path";
10525
10784
  import { env, pipeline } from "@huggingface/transformers";
@@ -10609,8 +10868,8 @@ var SemanticSearchClient = class {
10609
10868
  if (!response.ok) throw new Error(`Semantic index file failed (${response.status}): ${descriptor.path}`);
10610
10869
  const bytes = new Uint8Array(await response.arrayBuffer());
10611
10870
  if (bytes.byteLength !== descriptor.bytes) throw new Error(`Semantic index file size does not match: ${descriptor.path}`);
10612
- const digest = createHash10("sha256").update(bytes).digest("hex");
10613
- if (digest !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
10871
+ const digest2 = createHash11("sha256").update(bytes).digest("hex");
10872
+ if (digest2 !== descriptor.sha256) throw new Error(`Semantic index checksum does not match: ${descriptor.path}`);
10614
10873
  return bytes;
10615
10874
  }
10616
10875
  };
@@ -10699,7 +10958,7 @@ function ensureTrailingSlash(value) {
10699
10958
  }
10700
10959
 
10701
10960
  // src/trial/skill.ts
10702
- import { createHash as createHash11 } from "crypto";
10961
+ import { createHash as createHash12 } from "crypto";
10703
10962
  import { readFile as readFile26, stat as stat13 } from "fs/promises";
10704
10963
  import { join as join43 } from "path";
10705
10964
  import { parse as parseYaml2 } from "yaml";
@@ -10729,7 +10988,7 @@ async function createSkillTrial(driver, resolved, selectors) {
10729
10988
  skill: {
10730
10989
  name: artifact.name,
10731
10990
  relativePath: artifact.relativePath,
10732
- sha256: createHash11("sha256").update(content).digest("hex"),
10991
+ sha256: createHash12("sha256").update(content).digest("hex"),
10733
10992
  frontmatter,
10734
10993
  content
10735
10994
  }
@@ -10748,12 +11007,12 @@ function readSkillFrontmatter(content, artifact) {
10748
11007
  }
10749
11008
 
10750
11009
  // src/lifecycle/fleet-normalize.ts
10751
- import { createHash as createHash12 } from "crypto";
11010
+ import { createHash as createHash13 } from "crypto";
10752
11011
  import { readFile as readFile27, readdir as readdir9, rm as rm9 } from "fs/promises";
10753
11012
  import { homedir as homedir11 } from "os";
10754
- import { basename as basename19, isAbsolute as isAbsolute3, join as join44, relative as relative8, resolve as resolve22 } from "path";
11013
+ import { basename as basename19, isAbsolute as isAbsolute3, join as join44, relative as relative8, resolve as resolve23 } from "path";
10755
11014
  async function planFleetNormalization(request) {
10756
- const normalizedRequest = normalizeRequest(request);
11015
+ const normalizedRequest = normalizeRequest2(request);
10757
11016
  const destinationScope = await resolveWorkspaceScope({
10758
11017
  fleet: normalizedRequest.destinationFleet,
10759
11018
  globalRoot: normalizedRequest.globalRoot
@@ -10774,8 +11033,8 @@ async function planFleetNormalization(request) {
10774
11033
  if (!sourcePackage || !destinationPackage) {
10775
11034
  throw new Error(`Package '${name}' must be an existing duplicate in both source and destination fleets.`);
10776
11035
  }
10777
- const sourceDeclaration = canonicalJson(sourcePackage);
10778
- const destinationDeclaration = canonicalJson(destinationPackage);
11036
+ const sourceDeclaration = canonicalJson2(sourcePackage);
11037
+ const destinationDeclaration = canonicalJson2(destinationPackage);
10779
11038
  if (sourceDeclaration !== destinationDeclaration) {
10780
11039
  throw new Error(
10781
11040
  `Package '${name}' has divergent declarations. Source, driver, adapter, adapter configuration, installation type, version policy, selections, aliases, overrides, and suggestions must match before normalization.`
@@ -10812,13 +11071,13 @@ async function planFleetNormalization(request) {
10812
11071
  packages,
10813
11072
  installedState
10814
11073
  };
10815
- return { ...planWithoutDigest, planDigest: sha2564(canonicalJson(planWithoutDigest)) };
11074
+ return { ...planWithoutDigest, planDigest: sha2564(canonicalJson2(planWithoutDigest)) };
10816
11075
  }
10817
11076
  async function applyFleetNormalization(request, hooks = {}) {
10818
11077
  if (!request.apply) throw new Error("Fleet normalization is dry-run by default; pass --apply with --plan-digest <digest>.");
10819
11078
  if (!request.planDigest) throw new Error("Applying fleet normalization requires --plan-digest <digest> from a reviewed dry-run.");
10820
11079
  assertDigest(request.planDigest);
10821
- const normalizedRequest = normalizeRequest(request);
11080
+ const normalizedRequest = normalizeRequest2(request);
10822
11081
  const roots = await normalizationRoots(normalizedRequest);
10823
11082
  const ordered = [.../* @__PURE__ */ new Set([roots.source, roots.destination])].sort((a, b) => a.localeCompare(b));
10824
11083
  return withOrderedLocks(ordered, async () => {
@@ -10950,14 +11209,14 @@ async function assertNormalizationPostconditions(journal) {
10950
11209
  }
10951
11210
  for (const graphLock of journal.graphLocks) {
10952
11211
  const raw = await readOptionalRecord(graphLock.path);
10953
- const digest = raw ? sha2564(canonicalJson(await readGraphLock(graphLock.path))) : null;
10954
- if (digest !== graphLock.afterDigest) {
11212
+ const digest2 = raw ? sha2564(canonicalJson2(await readGraphLock(graphLock.path))) : null;
11213
+ if (digest2 !== graphLock.afterDigest) {
10955
11214
  throw new Error(`Fleet normalization graph-lock postcheck failed: ${graphLock.path}`);
10956
11215
  }
10957
11216
  }
10958
11217
  }
10959
11218
  async function recoverFleetNormalization(request) {
10960
- const normalizedRequest = normalizeRequest(request);
11219
+ const normalizedRequest = normalizeRequest2(request);
10961
11220
  const roots = await normalizationRoots(normalizedRequest);
10962
11221
  const ordered = [.../* @__PURE__ */ new Set([roots.source, roots.destination])].sort((a, b) => a.localeCompare(b));
10963
11222
  return withOrderedLocks(ordered, async () => {
@@ -11000,7 +11259,7 @@ async function assertSourceFleetPostcondition(request, source, selectedPackages)
11000
11259
  );
11001
11260
  }
11002
11261
  }
11003
- function normalizeRequest(request) {
11262
+ function normalizeRequest2(request) {
11004
11263
  const packages = request.packages === void 0 ? void 0 : sortedUnique6(request.packages);
11005
11264
  if (request.packages !== void 0 && packages?.length === 0) throw new Error("--package requires at least one package name.");
11006
11265
  const artifacts = request.artifacts === void 0 ? void 0 : sortedUnique6(request.artifacts.map((artifact) => normalizeArtifactSelector(artifact)));
@@ -11022,17 +11281,17 @@ function normalizeRequest(request) {
11022
11281
  ...profile ? { profile } : {},
11023
11282
  ...agent ? { agent } : {},
11024
11283
  ...orphanedOwnerRoots ? { orphanedOwnerRoots } : {},
11025
- ...request.globalRoot ? { globalRoot: resolve22(request.globalRoot) } : {}
11284
+ ...request.globalRoot ? { globalRoot: resolve23(request.globalRoot) } : {}
11026
11285
  };
11027
11286
  }
11028
11287
  function normalizeOrphanedOwnerRoot(value) {
11029
11288
  const root = value.trim();
11030
11289
  if (!root || !isAbsolute3(root)) throw new Error(`Invalid orphaned owner root '${value}'; expected an absolute workspace path.`);
11031
- return resolve22(root);
11290
+ return resolve23(root);
11032
11291
  }
11033
11292
  async function normalizationRoots(request) {
11034
11293
  const destination = await showRegisteredFleet(request.destinationFleet, { globalRoot: request.globalRoot });
11035
- const source = request.from === "user" ? resolve22(request.globalRoot ?? homedir11()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
11294
+ const source = request.from === "user" ? resolve23(request.globalRoot ?? homedir11()) : (await showRegisteredFleet(request.from.slice("fleet:".length), { globalRoot: request.globalRoot })).root;
11036
11295
  return { source, destination: destination.root };
11037
11296
  }
11038
11297
  async function withOrderedLocks(roots, fn) {
@@ -11123,10 +11382,10 @@ async function inspectInstalledState(source, destination, packageNames) {
11123
11382
  const destinationGraphAfter = destinationGraph?.lock ?? destinationGraphFromSource(sourceGraph.lock, destinationState.targetFingerprint);
11124
11383
  graphTransfers.push({
11125
11384
  sourceGraphLockPath: sourceGraph.path,
11126
- sourceGraphLockDigest: sha2564(canonicalJson(sourceGraph.lock)),
11385
+ sourceGraphLockDigest: sha2564(canonicalJson2(sourceGraph.lock)),
11127
11386
  destinationGraphLockPath: destinationState.graphLockPath,
11128
- destinationGraphLockDigest: destinationGraph ? sha2564(canonicalJson(destinationGraph.lock)) : null,
11129
- destinationGraphLockAfterDigest: sha2564(canonicalJson(destinationGraphAfter)),
11387
+ destinationGraphLockDigest: destinationGraph ? sha2564(canonicalJson2(destinationGraph.lock)) : null,
11388
+ destinationGraphLockAfterDigest: sha2564(canonicalJson2(destinationGraphAfter)),
11130
11389
  targetKey: sourceGraph.targetKey,
11131
11390
  adapter: destinationState.adapter,
11132
11391
  targetFingerprint: destinationState.targetFingerprint
@@ -11339,7 +11598,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
11339
11598
  )
11340
11599
  });
11341
11600
  if (!selectedArtifacts) {
11342
- const fullDigest = sha2564(canonicalJson(graph.lock));
11601
+ const fullDigest = sha2564(canonicalJson2(graph.lock));
11343
11602
  const existingDestinationGraph = graphCandidates.find((candidate) => candidate.path === destinationState.graphLockPath);
11344
11603
  if (existingDestinationGraph) assertGraphIsSubset(graph, existingDestinationGraph);
11345
11604
  const destinationGraph = destinationGraphFromSource(graph.lock, destinationState.targetFingerprint);
@@ -11347,8 +11606,8 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
11347
11606
  sourceGraphLockPath: graph.path,
11348
11607
  sourceGraphLockDigest: fullDigest,
11349
11608
  destinationGraphLockPath: destinationState.graphLockPath,
11350
- destinationGraphLockDigest: existingDestinationGraph ? sha2564(canonicalJson(existingDestinationGraph.lock)) : null,
11351
- destinationGraphLockAfterDigest: sha2564(canonicalJson(destinationGraph)),
11609
+ destinationGraphLockDigest: existingDestinationGraph ? sha2564(canonicalJson2(existingDestinationGraph.lock)) : null,
11610
+ destinationGraphLockAfterDigest: sha2564(canonicalJson2(destinationGraph)),
11352
11611
  targetKey: graph.targetKey,
11353
11612
  adapter: graph.adapter,
11354
11613
  targetFingerprint: destinationState.targetFingerprint
@@ -11406,7 +11665,7 @@ function targetKeysForAgent(scope, name) {
11406
11665
  async function runtimeRoots(scope) {
11407
11666
  const roots = /* @__PURE__ */ new Set([scope.root]);
11408
11667
  if (scope.config.packages.some((pkg) => pkg.installationType === "user") || Object.values(scope.config.agents).some((agent) => agent.installationType === "user")) {
11409
- roots.add(resolve22(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir11()));
11668
+ roots.add(resolve23(process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir11()));
11410
11669
  }
11411
11670
  for (const [name, agent] of Object.entries(scope.config.agents)) {
11412
11671
  if (agent.transport === "ssh") throw new Error(`Installed-state normalization cannot inspect SSH agent '${name}' locally.`);
@@ -11443,14 +11702,14 @@ async function collectManifests(roots) {
11443
11702
  }
11444
11703
  continue;
11445
11704
  }
11446
- if (resolve22(parsed.targetRoot) !== resolve22(root)) {
11705
+ if (resolve23(parsed.targetRoot) !== resolve23(root)) {
11447
11706
  throw new Error(`Install manifest target root does not match its enumerated runtime root: ${path}`);
11448
11707
  }
11449
11708
  const expectedPath = installManifestPath(parsed.targetRoot, parsed.adapter, {
11450
11709
  installationType: parsed.installationType,
11451
11710
  stateKey: parsed.stateKey
11452
11711
  });
11453
- if (resolve22(expectedPath) !== resolve22(path)) {
11712
+ if (resolve23(expectedPath) !== resolve23(path)) {
11454
11713
  throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
11455
11714
  }
11456
11715
  found.set(path, { path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
@@ -11471,7 +11730,7 @@ async function collectManifestPaths(paths) {
11471
11730
  installationType: parsed.installationType,
11472
11731
  stateKey: parsed.stateKey
11473
11732
  });
11474
- if (resolve22(expectedPath) !== resolve22(path)) {
11733
+ if (resolve23(expectedPath) !== resolve23(path)) {
11475
11734
  throw new Error(`Install manifest state identity does not match its canonical path: ${path}`);
11476
11735
  }
11477
11736
  found.push({ path, raw, manifest: { ...parsed, revision: computeManifestRevision(raw), legacy: false } });
@@ -11518,7 +11777,7 @@ async function relevantGraphLocks(workspaceRoot, selected, targetKeys, include)
11518
11777
  const projection = relevantGraphProjection(lock, selected);
11519
11778
  results.push({
11520
11779
  ...candidate,
11521
- digest: sha2564(canonicalJson(projection)),
11780
+ digest: sha2564(canonicalJson2(projection)),
11522
11781
  artifactIdentities: lock.canonical.artifacts.filter((artifact) => artifact.owners.some((owner) => ownerMatchesGraphPackage(lock, owner, selected))).map(graphArtifactIdentity).sort()
11523
11782
  });
11524
11783
  }
@@ -11554,7 +11813,7 @@ async function legacyManifestPathForConfiguredTarget(scope, graph) {
11554
11813
  baseDir: scope.root
11555
11814
  });
11556
11815
  const targetRoot = resolveConfigPath(agent.root, scope.root);
11557
- const installRoot = resolve22(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, false));
11816
+ const installRoot = resolve23(installRootForAdapterInstallationType(adapter, targetRoot, agent.installationType, false));
11558
11817
  const stateKey = stateKeyFor(adapter.name, {
11559
11818
  installationType: agent.installationType,
11560
11819
  targetFingerprint: graph.lock.canonical.targetFingerprint
@@ -11628,7 +11887,7 @@ async function targetStateForGraph(scope, graph, identityFleetId = scope.fleetId
11628
11887
  targetFingerprint,
11629
11888
  ...identityFleetId ? { fleetId: identityFleetId } : {}
11630
11889
  });
11631
- const installRoot = resolve22(installRootForAdapterInstallationType(adapter, targetRoot, installationType, false));
11890
+ const installRoot = resolve23(installRootForAdapterInstallationType(adapter, targetRoot, installationType, false));
11632
11891
  return {
11633
11892
  adapter: adapter.name,
11634
11893
  installationType,
@@ -11659,9 +11918,9 @@ async function legacyTargetStateForGraph(scope, graph) {
11659
11918
  };
11660
11919
  }
11661
11920
  function assertGraphIsSubset(source, destination) {
11662
- const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson(root)]));
11921
+ const sourceRoots = new Map(source.lock.canonical.roots.map((root) => [root.rootId, canonicalJson2(root)]));
11663
11922
  for (const root of destination.lock.canonical.roots) {
11664
- if (sourceRoots.get(root.rootId) !== canonicalJson(root)) {
11923
+ if (sourceRoots.get(root.rootId) !== canonicalJson2(root)) {
11665
11924
  throw new Error(
11666
11925
  `Partially normalized destination graph diverges at root '${root.rootId}': ${destination.path}`
11667
11926
  );
@@ -11691,7 +11950,7 @@ function destinationGraphFromSource(source, targetFingerprint) {
11691
11950
  function mergeDestinationManifest(existing, state, generatedAt, desiredEntries, replaceOwners = /* @__PURE__ */ new Set()) {
11692
11951
  if (existing) {
11693
11952
  const manifest2 = existing.manifest;
11694
- if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve22(manifest2.targetRoot) !== state.installRoot) {
11953
+ if (manifest2.adapter !== state.adapter || manifest2.installationType !== state.installationType || manifest2.stateKey !== state.stateKey || resolve23(manifest2.targetRoot) !== state.installRoot) {
11695
11954
  throw new Error(`Destination manifest identity diverges from the derived target: ${existing.path}`);
11696
11955
  }
11697
11956
  }
@@ -11751,7 +12010,7 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
11751
12010
  const content = await readFile27(path, "utf8");
11752
12011
  if (hasMergeRemovalContent(entry.mergeRemoval)) {
11753
12012
  assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
11754
- } else if (resolve22(sourcePath) === resolve22(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
12013
+ } else if (resolve23(sourcePath) === resolve23(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
11755
12014
  assertExactMergeContribution({}, entry.mergeStrategy, content);
11756
12015
  } else {
11757
12016
  throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
@@ -11762,8 +12021,8 @@ async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath)
11762
12021
  await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
11763
12022
  }
11764
12023
  function renderedEntryPathForRoot(root, entryPath) {
11765
- const normalizedRoot = resolve22(root);
11766
- const candidate = resolve22(normalizedRoot, entryPath);
12024
+ const normalizedRoot = resolve23(root);
12025
+ const candidate = resolve23(normalizedRoot, entryPath);
11767
12026
  if (candidate !== normalizedRoot && !candidate.startsWith(`${normalizedRoot}/`)) {
11768
12027
  throw new Error(`Install manifest entry escapes its target root: ${entryPath}`);
11769
12028
  }
@@ -11854,8 +12113,8 @@ function ownerMatchesGraphPackage(lock, owner, selected) {
11854
12113
  return ownerMatchesPackage(owner, selected) || lock.canonical.roots.some((root) => selected.has(root.rootId) && root.graphNodeId === owner);
11855
12114
  }
11856
12115
  function renderedEntryPath(manifest, entry) {
11857
- const root = resolve22(manifest.targetRoot);
11858
- const candidate = resolve22(root, entry.path);
12116
+ const root = resolve23(manifest.targetRoot);
12117
+ const candidate = resolve23(root, entry.path);
11859
12118
  if (candidate !== root && !candidate.startsWith(`${root}/`)) {
11860
12119
  throw new Error(`Install manifest entry escapes its target root: ${entry.path}`);
11861
12120
  }
@@ -11874,7 +12133,7 @@ function assertSimpleVerifiableEntry(entry, path) {
11874
12133
  if (entry.kind !== "file" && entry.kind !== "dir") throw new Error(`Unsupported installed entry kind at ${path}.`);
11875
12134
  }
11876
12135
  function entryIdentity(entry) {
11877
- return canonicalJson({
12136
+ return canonicalJson2({
11878
12137
  path: entry.path,
11879
12138
  artifactType: entry.artifactType,
11880
12139
  artifactName: entry.artifactName,
@@ -11892,7 +12151,7 @@ function entryIdentity(entry) {
11892
12151
  });
11893
12152
  }
11894
12153
  function graphArtifactIdentity(artifact) {
11895
- return canonicalJson({
12154
+ return canonicalJson2({
11896
12155
  graphNodeId: artifact.graphNodeId,
11897
12156
  type: artifact.type,
11898
12157
  name: artifact.name,
@@ -11906,7 +12165,7 @@ function graphArtifactIdentity(artifact) {
11906
12165
  });
11907
12166
  }
11908
12167
  function graphEntryIdentity(entry) {
11909
- return canonicalJson({
12168
+ return canonicalJson2({
11910
12169
  graphNodeId: entry.graphNodeId,
11911
12170
  type: entry.artifactType,
11912
12171
  name: entry.artifactName,
@@ -12010,7 +12269,7 @@ async function buildJournalGraphLockStates(plan) {
12010
12269
  for (const transfer of plan.installedState.graphTransfers) {
12011
12270
  const sourceBefore = JSON.parse(await readFile27(transfer.sourceGraphLockPath, "utf8"));
12012
12271
  const sourceLock = await readGraphLock(transfer.sourceGraphLockPath);
12013
- const sourceDigest = sha2564(canonicalJson(sourceLock));
12272
+ const sourceDigest = sha2564(canonicalJson2(sourceLock));
12014
12273
  if (sourceDigest !== transfer.sourceGraphLockDigest) {
12015
12274
  throw new Error(`Source graph lock changed after planning: ${transfer.sourceGraphLockPath}`);
12016
12275
  }
@@ -12028,9 +12287,9 @@ async function buildJournalGraphLockStates(plan) {
12028
12287
  continue;
12029
12288
  }
12030
12289
  const destinationBefore = await readOptionalRecord(transfer.destinationGraphLockPath);
12031
- const destinationBeforeDigest = destinationBefore ? sha2564(canonicalJson(await readGraphLock(transfer.destinationGraphLockPath))) : null;
12290
+ const destinationBeforeDigest = destinationBefore ? sha2564(canonicalJson2(await readGraphLock(transfer.destinationGraphLockPath))) : null;
12032
12291
  const destinationAfter = destinationBefore ? await readGraphLock(transfer.destinationGraphLockPath) : destinationGraphFromSource(sourceLock, transfer.targetFingerprint);
12033
- const destinationAfterDigest = sha2564(canonicalJson(destinationAfter));
12292
+ const destinationAfterDigest = sha2564(canonicalJson2(destinationAfter));
12034
12293
  if (destinationBeforeDigest !== transfer.destinationGraphLockDigest || destinationAfterDigest !== transfer.destinationGraphLockAfterDigest) {
12035
12294
  throw new Error(`Destination graph lock changed after planning: ${transfer.destinationGraphLockPath}`);
12036
12295
  }
@@ -12140,8 +12399,8 @@ async function assertJournalRecoverable(journal) {
12140
12399
  }
12141
12400
  for (const graphLock of journal.graphLocks) {
12142
12401
  const raw = await readOptionalRecord(graphLock.path);
12143
- const digest = raw ? sha2564(canonicalJson(await readGraphLock(graphLock.path))) : null;
12144
- if (digest !== graphLock.beforeDigest && digest !== graphLock.afterDigest) {
12402
+ const digest2 = raw ? sha2564(canonicalJson2(await readGraphLock(graphLock.path))) : null;
12403
+ if (digest2 !== graphLock.beforeDigest && digest2 !== graphLock.afterDigest) {
12145
12404
  throw new Error(`Cannot recover fleet normalization because graph lock changed outside the transaction: ${graphLock.path}`);
12146
12405
  }
12147
12406
  }
@@ -12193,12 +12452,12 @@ function packageMap(config) {
12193
12452
  return result;
12194
12453
  }
12195
12454
  function configRevision(config) {
12196
- return sha2564(canonicalJson(config));
12455
+ return sha2564(canonicalJson2(config));
12197
12456
  }
12198
12457
  function normalizationJournalPath(sourceRoot, destinationFleet) {
12199
12458
  return join44(sourceRoot, ".agentwheel", `fleet-normalize-${destinationFleet}.journal.json`);
12200
12459
  }
12201
- function canonicalJson(value) {
12460
+ function canonicalJson2(value) {
12202
12461
  return JSON.stringify(sortValue(value));
12203
12462
  }
12204
12463
  function sortValue(value) {
@@ -12209,7 +12468,7 @@ function sortValue(value) {
12209
12468
  return value;
12210
12469
  }
12211
12470
  function sha2564(value) {
12212
- return createHash12("sha256").update(value).digest("hex");
12471
+ return createHash13("sha256").update(value).digest("hex");
12213
12472
  }
12214
12473
  function assertDigest(value) {
12215
12474
  if (!/^[a-f0-9]{64}$/.test(value)) throw new Error(`Invalid plan digest: ${value}`);
@@ -12247,6 +12506,7 @@ var commandGovernance = {
12247
12506
  "package migrate": "governed-always",
12248
12507
  remember: "governed-always",
12249
12508
  "ownership handoff": "governed-unless-dry-run",
12509
+ "ownership retire-stale": "governed-apply",
12250
12510
  "mcp retire": "governed-apply",
12251
12511
  eject: "governed-always",
12252
12512
  uninstall: "governed-unless-dry-run",
@@ -12663,7 +12923,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
12663
12923
  const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
12664
12924
  if (options.toFleet) {
12665
12925
  const fleet = await showRegisteredFleet(options.toFleet);
12666
- if (resolve23(fleet.root) !== resolve23(toWorkspaceRoot)) {
12926
+ if (resolve24(fleet.root) !== resolve24(toWorkspaceRoot)) {
12667
12927
  throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
12668
12928
  }
12669
12929
  }
@@ -12688,6 +12948,86 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
12688
12948
  console.log(`Manifest revision: ${result.manifestRevision}`);
12689
12949
  console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
12690
12950
  });
12951
+ 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").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").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) => {
12952
+ const normalizedOptions = normalizeRuntimeScopeOptions(options);
12953
+ const fleet = await showRegisteredFleet(options.fleet);
12954
+ const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
12955
+ if (resolve24(fleet.root) !== resolve24(toWorkspaceRoot)) {
12956
+ throw new Error(`Destination Fleet '${options.fleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
12957
+ }
12958
+ const targets = await resolveCliTargets(normalizedOptions);
12959
+ if (targets.length !== 1) {
12960
+ throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
12961
+ }
12962
+ const target = targets[0];
12963
+ if (target.fleetId !== options.fleet || resolve24(target.workspaceRoot) !== resolve24(fleet.root)) {
12964
+ throw new Error(`Resolved target does not belong to destination Fleet '${options.fleet}'.`);
12965
+ }
12966
+ const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
12967
+ const adapter = await resolveAdapterForTarget(target, adapterOptions);
12968
+ const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
12969
+ const state = installStateForTarget(target, adapter, adapterOptions, installationType);
12970
+ const request = {
12971
+ targetRoot: state.installRoot,
12972
+ adapter: adapter.name,
12973
+ installationType,
12974
+ sourceStateKey: options.sourceStateKey,
12975
+ destinationStateKey: state.stateKey,
12976
+ fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
12977
+ toWorkspaceRoot,
12978
+ toFleetId: fleet.id,
12979
+ planDigest: options.planDigest,
12980
+ expectedSourceRevision: options.expectedSourceRevision,
12981
+ expectedDestinationRevision: options.expectedDestinationRevision,
12982
+ expectedInventoryRevision: options.expectedInventoryRevision,
12983
+ abandonIncompleteMergeOwner: options.abandonIncompleteMergeOwner,
12984
+ transport: transportForTarget(target)
12985
+ };
12986
+ const result = options.apply ? await applyRetireStaleOwnership(request) : await planRetireStaleOwnership(request);
12987
+ const targetArgs = options.profile ? ["--profile", options.profile] : target.agentName ? ["--agent", target.agentName] : ["--adapter", adapter.name, "--target-root", target.targetRoot];
12988
+ const applyArgs = [
12989
+ "agentwheel",
12990
+ "ownership",
12991
+ "retire-stale",
12992
+ "--from-workspace-root",
12993
+ request.fromWorkspaceRoot,
12994
+ "--to-workspace-root",
12995
+ request.toWorkspaceRoot,
12996
+ "--source-state-key",
12997
+ request.sourceStateKey,
12998
+ "--fleet",
12999
+ request.toFleetId,
13000
+ ...targetArgs,
13001
+ "--installation-type",
13002
+ installationType,
13003
+ ...options.adapterConfig ? ["--adapter-config", options.adapterConfig] : [],
13004
+ ...options.adapterModule ? ["--adapter-module", options.adapterModule] : [],
13005
+ ...options.allowAdapterCode ? ["--allow-adapter-code"] : [],
13006
+ ...options.abandonIncompleteMergeOwner ? ["--abandon-incomplete-merge-owner"] : [],
13007
+ "--plan-digest",
13008
+ result.planDigest,
13009
+ "--expected-source-revision",
13010
+ result.source.revision,
13011
+ "--expected-destination-revision",
13012
+ result.destination.revision,
13013
+ "--expected-inventory-revision",
13014
+ result.manifestInventoryRevision,
13015
+ "--apply"
13016
+ ];
13017
+ const applyCommand = applyArgs.map(shellQuoteArgument).join(" ");
13018
+ if (options.json) {
13019
+ console.log(JSON.stringify({ ...result, applyCommand }, null, 2));
13020
+ return;
13021
+ }
13022
+ console.log(`${options.apply ? "Retired stale ownership" : "Stale ownership retirement plan"}: ${result.selected.length} entr${result.selected.length === 1 ? "y" : "ies"}`);
13023
+ console.log(`Target: ${result.adapter}/${result.installationType} at ${result.targetRoot}`);
13024
+ console.log(`Source: ${result.source.stateKey} revision ${result.source.revision}`);
13025
+ console.log(`Destination: ${result.destination.stateKey} revision ${result.destination.revision}`);
13026
+ console.log(`Manifest inventory revision: ${result.manifestInventoryRevision}`);
13027
+ for (const entry of result.selected) console.log(`- ${entry.path}: ${entry.sourceEntryDigest} -> ${entry.destinationEntryDigest}`);
13028
+ console.log(`Plan digest: ${result.planDigest}`);
13029
+ if (!options.apply) console.log(`Apply: ${applyCommand}`);
13030
+ });
12691
13031
  var mcpCommand = program.command("mcp").description("operate on MCP runtime configuration");
12692
13032
  mcpCommand.command("retire").description("remove one exact legacy MCP contribution with explicit state ownership").argument("<package>", "configured package containing exactly one legacy MCP artifact").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").option("-t, --target-root <path>", "explicit runtime/project root").option("--agent <name>", "one named agent from merged config").option("--profile <name>", "workspace runtime profile resolving to one target").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--from-workspace-root <path>", "required previous owner when managed legacy state exists").option("--dry-run", "show the retirement plan without writing (default)", false).option("--apply", "apply the exact reviewed retirement plan", false).option("--json", "print the install plan as JSON", false).action(async (packageName, options) => {
12693
13033
  if (options.apply && options.dryRun) throw new Error("--apply cannot be combined with --dry-run.");
@@ -13678,7 +14018,7 @@ function scopeUpdatePlanToDependencies(result, selectors, previousLock, manifest
13678
14018
  selectedPreviousNodeIds,
13679
14019
  selectedRootIds
13680
14020
  );
13681
- const graphLockDigest = createHash13("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14021
+ const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
13682
14022
  return {
13683
14023
  ...result,
13684
14024
  bundle: { ...result.bundle, graphLock },
@@ -13804,7 +14144,7 @@ function operationBelongsToDependencyUpdate(operation, allowedNodeIds, allowedNa
13804
14144
  function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
13805
14145
  const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest);
13806
14146
  const graphLock = focusedGraphLockForInstall(scoped.bundle.graphLock, rootId, focused);
13807
- const graphLockDigest = createHash13("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14147
+ const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
13808
14148
  return {
13809
14149
  ...scoped,
13810
14150
  bundle: { ...scoped.bundle, graphLock },
@@ -13815,7 +14155,7 @@ function scopeInstallPlanToArtifact(result, rootId, focused, manifest) {
13815
14155
  function scopeUpdatePlanToArtifact(result, rootId, focused, previousLock, manifest) {
13816
14156
  const scoped = scopePlanOperationsToArtifact(result, rootId, focused, manifest, previousLock);
13817
14157
  const graphLock = mergeFocusedArtifactGraphLock(scoped.bundle.graphLock, previousLock, rootId, focused);
13818
- const graphLockDigest = createHash13("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14158
+ const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
13819
14159
  return {
13820
14160
  ...scoped,
13821
14161
  bundle: { ...scoped.bundle, graphLock },
@@ -14110,7 +14450,7 @@ function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
14110
14450
  selectedPreviousNodeIds,
14111
14451
  /* @__PURE__ */ new Set([rootId])
14112
14452
  );
14113
- const graphLockDigest = createHash13("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14453
+ const graphLockDigest = createHash14("sha256").update(canonicalGraphLockJson(graphLock)).digest("hex");
14114
14454
  return {
14115
14455
  ...scoped,
14116
14456
  bundle: { ...scoped.bundle, graphLock },
@@ -14666,7 +15006,7 @@ async function assertCompositeMutationSupported(workspaceRoot, profile, readOnly
14666
15006
  `Composite-profile mutation is refused before member execution because ${member.id} uses ${member.transport}; durable remote policy and recovery preflight is not implemented.`
14667
15007
  );
14668
15008
  }
14669
- const memberRoot = resolve23(workspaceRoot, member.workspace);
15009
+ const memberRoot = resolve24(workspaceRoot, member.workspace);
14670
15010
  const memberPolicy = await mutationPolicyForWorkspace(memberRoot);
14671
15011
  if (memberPolicy?.revisioning.mode === "commit-after-verify") {
14672
15012
  throw new Error(
@@ -15117,11 +15457,14 @@ function looksLikeSourceSpecifier(value) {
15117
15457
  }
15118
15458
  function normalizeCliPath(value) {
15119
15459
  if (value === "~") return homedir12();
15120
- if (value.startsWith("~/")) return resolve23(homedir12(), value.slice(2));
15121
- return resolve23(value);
15460
+ if (value.startsWith("~/")) return resolve24(homedir12(), value.slice(2));
15461
+ return resolve24(value);
15462
+ }
15463
+ function shellQuoteArgument(value) {
15464
+ return /^[a-z0-9_./:@%+=,-]+$/i.test(value) ? value : `'${value.replace(/'/g, `'"'"'`)}'`;
15122
15465
  }
15123
15466
  function isHomePath(path) {
15124
- return resolve23(path) === resolve23(homedir12());
15467
+ return resolve24(path) === resolve24(homedir12());
15125
15468
  }
15126
15469
  function adapterListFromOption(adapter) {
15127
15470
  if (!adapter) return [];
@@ -15242,7 +15585,7 @@ function withFleetExample(config) {
15242
15585
  };
15243
15586
  }
15244
15587
  async function findAgentwheelPackageRoot(start) {
15245
- let current = resolve23(start);
15588
+ let current = resolve24(start);
15246
15589
  while (true) {
15247
15590
  if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
15248
15591
  const parent = dirname22(current);
@@ -8,6 +8,7 @@ import {
8
8
  applyLockPath,
9
9
  assertApplyJournalRecoveryAllowed,
10
10
  assertGovernedRuntimeTransportSupported,
11
+ listAllApplyJournals,
11
12
  listApplyJournals,
12
13
  localPathExists,
13
14
  mutationMetadataForApplyJournal,
@@ -17,7 +18,7 @@ import {
17
18
  removeApplyJournal,
18
19
  rollbackCompletedOperations,
19
20
  writeApplyJournal
20
- } from "./chunk-BJEZXIXX.js";
21
+ } from "./chunk-Z4N7TB6Q.js";
21
22
  import "./chunk-7VPI5J5Y.js";
22
23
  export {
23
24
  abortApplyJournal,
@@ -28,6 +29,7 @@ export {
28
29
  applyLockPath,
29
30
  assertApplyJournalRecoveryAllowed,
30
31
  assertGovernedRuntimeTransportSupported,
32
+ listAllApplyJournals,
31
33
  listApplyJournals,
32
34
  localPathExists,
33
35
  mutationMetadataForApplyJournal,
package/openpack.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "name": "NestDevLab/agentwheel",
4
- "version": "0.20.1",
4
+ "version": "0.20.3",
5
5
  "provides": [
6
6
  { "type": "skills", "path": "skills" }
7
7
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentwheel",
3
- "version": "0.20.1",
3
+ "version": "0.20.3",
4
4
  "description": "Weave skills, rules, and instructions across every AI agent.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -5,7 +5,7 @@ allowed-tools: [Bash]
5
5
  license: MIT
6
6
  metadata:
7
7
  author: NestDevLab
8
- version: "0.20.1"
8
+ version: "0.20.3"
9
9
  ---
10
10
 
11
11
  # agentwheel
@@ -5,7 +5,7 @@ allowed-tools: [Bash]
5
5
  license: MIT
6
6
  metadata:
7
7
  author: NestDevLab
8
- version: "0.20.1"
8
+ version: "0.20.3"
9
9
  ---
10
10
 
11
11
  # Agentwheel Discovery