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 +9 -0
- package/dist/index.js +32 -3
- package/openpack.json +1 -1
- package/package.json +1 -1
- package/skills/agentwheel/SKILL.md +1 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
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
|
|
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
package/package.json
CHANGED