agentwheel 0.20.16 → 0.20.17

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/README.md CHANGED
@@ -461,6 +461,15 @@ agentwheel ownership retire-stale \
461
461
 
462
462
  The dry-run still proves exact destination coverage and runtime bytes. Apply only its emitted command,
463
463
  which includes the plan, source, destination, and manifest-inventory revisions.
464
+ For duplicate state owned by the same registered Fleet, set both workspace roots to the Fleet root
465
+ and add `--from-fleet-id <fleet-id>`. The planner selects only legacy paths covered by the stable
466
+ Fleet manifest. For JSON deep merges it also accepts a source contribution that is an exact
467
+ recursive subset of the destination contribution. Across package revisions, merge entries must
468
+ identify the same package and artifact with equal source hashes; other merge contributions must
469
+ match exactly. A source-only path stays in the legacy manifest;
470
+ when every source entry is covered, apply removes the old manifest and source lock without changing
471
+ runtime files or the stable manifest. Preserve copies of those old state files before applying a
472
+ retirement when they are needed as historical evidence.
464
473
 
465
474
  ## Core Ideas
466
475
 
package/dist/index.js CHANGED
@@ -10001,7 +10001,7 @@ async function observe(request, transport) {
10001
10001
  const normalized = normalizeRequest(request);
10002
10002
  const source = await requireManifest(normalized, normalized.sourceStateKey, transport, "source");
10003
10003
  const destination = await requireManifest(normalized, normalized.destinationStateKey, transport, "destination");
10004
- const sourceOwner = workspaceOwnerForRoot(normalized.fromWorkspaceRoot);
10004
+ const sourceOwner = workspaceOwnerForRoot(normalized.fromWorkspaceRoot, normalized.fromFleetId);
10005
10005
  const destinationOwner = workspaceOwnerForRoot(normalized.toWorkspaceRoot, normalized.toFleetId);
10006
10006
  const destinationByPath = /* @__PURE__ */ new Map();
10007
10007
  for (const entry of destination.entries) {
@@ -10078,6 +10078,11 @@ function normalizeRequest(request) {
10078
10078
  const destinationStateKey = exactStateKey(request.adapter, request.destinationStateKey, request.installationType, "destination");
10079
10079
  if (sourceStateKey === destinationStateKey) throw new Error("Source and destination state keys must differ.");
10080
10080
  if (!request.toFleetId.trim()) throw new Error("Destination Fleet id is required.");
10081
+ const fromFleetId = request.fromFleetId?.trim();
10082
+ if (request.fromFleetId !== void 0 && !fromFleetId) throw new Error("Source Fleet id cannot be empty.");
10083
+ if (fromFleetId && (fromFleetId !== request.toFleetId.trim() || resolve21(request.fromWorkspaceRoot) !== resolve21(request.toWorkspaceRoot))) {
10084
+ throw new Error("Fleet-qualified stale ownership retirement requires the same source and destination Fleet and workspace root.");
10085
+ }
10081
10086
  return {
10082
10087
  ...request,
10083
10088
  targetRoot,
@@ -10085,6 +10090,7 @@ function normalizeRequest(request) {
10085
10090
  sourceStateKey,
10086
10091
  destinationStateKey,
10087
10092
  fromWorkspaceRoot: resolve21(request.fromWorkspaceRoot),
10093
+ ...fromFleetId ? { fromFleetId } : {},
10088
10094
  toWorkspaceRoot: resolve21(request.toWorkspaceRoot),
10089
10095
  toFleetId: request.toFleetId.trim()
10090
10096
  };
@@ -10121,6 +10127,7 @@ function assertRetirableSourceEntry(entry, abandonIncompleteMergeOwner) {
10121
10127
  }
10122
10128
  }
10123
10129
  async function assertContributionCoverage(source, destination, targetRoot, transport, abandonIncompleteMergeOwner) {
10130
+ containedArtifactPath(targetRoot, source.path);
10124
10131
  const sourceManagedBlock = source.mode === "managed-block";
10125
10132
  const destinationManagedBlock = destination.mode === "managed-block";
10126
10133
  const sourceMerge = source.mergeStrategy !== void 0;
@@ -10146,8 +10153,19 @@ async function assertContributionCoverage(source, destination, targetRoot, trans
10146
10153
  if (abandonIncompleteMergeOwner) return "abandoned-incomplete-merge";
10147
10154
  throw new Error(`Cannot retire incomplete merge ownership at ${source.path}.`);
10148
10155
  }
10149
- const exactlyCovered = destination.mergeStrategy === source.mergeStrategy && hasMergeRemovalContent(destination.mergeRemoval) && contributionSelector(source) === contributionSelector(destination) && canonicalJson(source.mergeRemoval) === canonicalJson(destination.mergeRemoval);
10156
+ const selectorsDiffer = contributionSelector(source) !== contributionSelector(destination);
10157
+ if (selectorsDiffer && !sameContributionSelector(source, destination)) {
10158
+ throw new Error(`Destination Fleet does not identify the same source merge artifact at ${source.path}.`);
10159
+ }
10160
+ if (source.mergeStrategy === "json-deep" && destination.mergeStrategy === "json-deep" && hasMergeRemovalContent(destination.mergeRemoval) && sameContributionSelector(source, destination)) {
10161
+ if (canonicalJson(combineMergeRemovals(destination.mergeRemoval, source.mergeRemoval)) === canonicalJson(destination.mergeRemoval)) return "exact";
10162
+ throw new Error(`Destination Fleet does not exactly cover stale source merge contribution at ${source.path}.`);
10163
+ }
10164
+ const exactlyCovered = destination.mergeStrategy === source.mergeStrategy && hasMergeRemovalContent(destination.mergeRemoval) && sameContributionSelector(source, destination) && canonicalJson(source.mergeRemoval) === canonicalJson(destination.mergeRemoval);
10150
10165
  if (exactlyCovered) return "exact";
10166
+ if (selectorsDiffer) {
10167
+ throw new Error(`Destination Fleet does not exactly cover stale source merge contribution at ${source.path}.`);
10168
+ }
10151
10169
  const current = await transport.readFile(containedArtifactPath(targetRoot, source.path));
10152
10170
  if (!mergeContributionAbsent(source.mergeRemoval, source.mergeStrategy, current)) {
10153
10171
  throw new Error(`Destination Fleet does not exactly cover or replace stale source merge contribution at ${source.path}.`);
@@ -10159,6 +10177,15 @@ async function assertContributionCoverage(source, destination, targetRoot, trans
10159
10177
  function contributionSelector(entry) {
10160
10178
  return entry.logicalSelector ?? `${entry.artifactType}/${entry.artifactName}`;
10161
10179
  }
10180
+ function sameContributionSelector(source, destination) {
10181
+ if (contributionSelector(source) === contributionSelector(destination)) return true;
10182
+ if (!source.packageName || source.packageName !== destination.packageName || source.artifactType !== destination.artifactType || source.artifactName !== destination.artifactName || source.installName !== destination.installName || source.sourceHash !== destination.sourceHash || !source.graphNodeId || !destination.graphNodeId) return false;
10183
+ const selector = artifactSelectorKey({ type: source.artifactType, name: source.artifactName });
10184
+ if (source.logicalSelector !== `${source.graphNodeId}:${selector}` || destination.logicalSelector !== `${destination.graphNodeId}:${selector}`) return false;
10185
+ const sourceAt = source.graphNodeId.lastIndexOf("@");
10186
+ const destinationAt = destination.graphNodeId.lastIndexOf("@");
10187
+ return sourceAt > 0 && destinationAt > 0 && source.graphNodeId.slice(0, sourceAt) === source.packageName && destination.graphNodeId.slice(0, destinationAt) === destination.packageName && source.graphNodeId.indexOf("+", sourceAt) > sourceAt && destination.graphNodeId.indexOf("+", destinationAt) > destinationAt;
10188
+ }
10162
10189
  function assertApplyPreconditions(request) {
10163
10190
  for (const [label, value] of [
10164
10191
  ["plan digest", request.planDigest],
@@ -14035,7 +14062,7 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
14035
14062
  await rm10(stage, { recursive: true, force: true });
14036
14063
  }
14037
14064
  });
14038
- 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").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) => {
14065
+ 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) => {
14039
14066
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
14040
14067
  const fleet = await showRegisteredFleet(options.fleet);
14041
14068
  const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
@@ -14061,6 +14088,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
14061
14088
  sourceStateKey: options.sourceStateKey,
14062
14089
  destinationStateKey: options.destinationStateKey ?? stableState.stateKey,
14063
14090
  fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
14091
+ fromFleetId: options.fromFleetId,
14064
14092
  toWorkspaceRoot,
14065
14093
  toFleetId: fleet.id,
14066
14094
  planDigest: options.planDigest,
@@ -14078,6 +14106,7 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
14078
14106
  "retire-stale",
14079
14107
  "--from-workspace-root",
14080
14108
  request.fromWorkspaceRoot,
14109
+ ...request.fromFleetId ? ["--from-fleet-id", request.fromFleetId] : [],
14081
14110
  "--to-workspace-root",
14082
14111
  request.toWorkspaceRoot,
14083
14112
  "--source-state-key",
package/openpack.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "name": "NestDevLab/agentwheel",
4
- "version": "0.20.16",
4
+ "version": "0.20.17",
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.16",
3
+ "version": "0.20.17",
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.16"
8
+ version: "0.20.17"
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.16"
8
+ version: "0.20.17"
9
9
  ---
10
10
 
11
11
  # Agentwheel Discovery