agentwheel 0.19.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +100 -15
- 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/dist/index.js
CHANGED
|
@@ -1674,6 +1674,16 @@ import { dirname as dirname9 } from "path";
|
|
|
1674
1674
|
import { parse as parse3, stringify as stringify2 } from "yaml";
|
|
1675
1675
|
var MergeAdoptionMismatchError = class extends Error {
|
|
1676
1676
|
};
|
|
1677
|
+
function assertExactMergeContribution(contribution, strategy, currentContent) {
|
|
1678
|
+
if (strategy === "codex-toml-mcp") {
|
|
1679
|
+
assertExactMcpMergeContribution(contribution, strategy, currentContent);
|
|
1680
|
+
return;
|
|
1681
|
+
}
|
|
1682
|
+
const mismatch = firstMergeContributionMismatch(parseMergeDestination(currentContent, strategy), contribution);
|
|
1683
|
+
if (mismatch) {
|
|
1684
|
+
throw new MergeAdoptionMismatchError(`exact merge contribution differs or is missing at ${mismatch}`);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1677
1687
|
function assertExactMcpMergeContribution(removal, strategy, currentContent) {
|
|
1678
1688
|
if (strategy === "codex-toml-mcp") {
|
|
1679
1689
|
const mismatched = mismatchedCodexTomlMcpServers(removal, currentContent);
|
|
@@ -1780,6 +1790,25 @@ function firstMcpContributionMismatch(current, incoming) {
|
|
|
1780
1790
|
}
|
|
1781
1791
|
return void 0;
|
|
1782
1792
|
}
|
|
1793
|
+
function firstMergeContributionMismatch(current, contribution, path = "$") {
|
|
1794
|
+
if (isRecord4(contribution)) {
|
|
1795
|
+
if (!isRecord4(current)) return path;
|
|
1796
|
+
for (const [key, value] of Object.entries(contribution)) {
|
|
1797
|
+
if (!(key in current)) return `${path}.${key}`;
|
|
1798
|
+
const mismatch = firstMergeContributionMismatch(current[key], value, `${path}.${key}`);
|
|
1799
|
+
if (mismatch) return mismatch;
|
|
1800
|
+
}
|
|
1801
|
+
return void 0;
|
|
1802
|
+
}
|
|
1803
|
+
if (Array.isArray(contribution)) {
|
|
1804
|
+
if (!Array.isArray(current)) return path;
|
|
1805
|
+
for (const value of contribution) {
|
|
1806
|
+
if (!current.some((candidate) => sameMcpValue(candidate, value))) return path;
|
|
1807
|
+
}
|
|
1808
|
+
return void 0;
|
|
1809
|
+
}
|
|
1810
|
+
return current === contribution ? void 0 : path;
|
|
1811
|
+
}
|
|
1783
1812
|
function combineMergeValues(existing, incoming) {
|
|
1784
1813
|
if (isRecord4(existing) && isRecord4(incoming)) {
|
|
1785
1814
|
const combined = { ...existing };
|
|
@@ -10489,8 +10518,8 @@ async function createExactMcpRetirementPlan(desiredArtifacts, adapter, targetRoo
|
|
|
10489
10518
|
}
|
|
10490
10519
|
const removalKeys = Object.keys(operation.mergeRemoval);
|
|
10491
10520
|
const servers = operation.mergeRemoval.mcpServers;
|
|
10492
|
-
if (removalKeys.length !== 1 || removalKeys[0] !== "mcpServers" || !servers || typeof servers !== "object" || Array.isArray(servers) || Object.keys(servers).length
|
|
10493
|
-
throw new Error("Exact MCP retirement requires
|
|
10521
|
+
if (removalKeys.length !== 1 || removalKeys[0] !== "mcpServers" || !servers || typeof servers !== "object" || Array.isArray(servers) || Object.keys(servers).length === 0) {
|
|
10522
|
+
throw new Error("Exact MCP retirement requires one or more MCP servers and no non-MCP configuration.");
|
|
10494
10523
|
}
|
|
10495
10524
|
const entry = manifest?.entries[0];
|
|
10496
10525
|
if (entry) {
|
|
@@ -11729,7 +11758,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11729
11758
|
}
|
|
11730
11759
|
const entry = matches[0];
|
|
11731
11760
|
const fromOwner = workspaceOwnerForRoot(request.fromWorkspaceRoot);
|
|
11732
|
-
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot);
|
|
11761
|
+
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot, request.toFleetId);
|
|
11733
11762
|
if (fromOwner === toOwner) throw new Error("Ownership handoff requires different workspace roots.");
|
|
11734
11763
|
if (entry.workspaceOwner !== fromOwner) {
|
|
11735
11764
|
throw new Error(`Old owner precondition failed for ${entry.path}: expected ${fromOwner}, found ${entry.workspaceOwner}`);
|
|
@@ -11739,10 +11768,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11739
11768
|
}
|
|
11740
11769
|
const destPath = containedArtifactPath(request.targetRoot, entry.path);
|
|
11741
11770
|
if (!await transport.pathExists(destPath)) throw new Error(`Managed artifact is missing: ${entry.path}`);
|
|
11742
|
-
const currentHash = await
|
|
11743
|
-
if (currentHash !== entry.hash) {
|
|
11744
|
-
throw new Error(`Managed artifact is drifted at ${entry.path}: manifest ${entry.hash}, current ${currentHash}`);
|
|
11745
|
-
}
|
|
11771
|
+
const currentHash = await verifiedEntryHash(entry, destPath, transport);
|
|
11746
11772
|
if (request.expectedHash && currentHash !== request.expectedHash) {
|
|
11747
11773
|
throw new Error(`Current hash precondition failed for ${entry.path}: expected ${request.expectedHash}, found ${currentHash}`);
|
|
11748
11774
|
}
|
|
@@ -11759,6 +11785,29 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
11759
11785
|
toOwner
|
|
11760
11786
|
};
|
|
11761
11787
|
}
|
|
11788
|
+
async function verifiedEntryHash(entry, destPath, transport) {
|
|
11789
|
+
if (entry.semanticPlugin) throw new Error(`Ownership handoff cannot verify semantic plugin state at ${destPath}`);
|
|
11790
|
+
if (entry.mode === "managed-block") {
|
|
11791
|
+
const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
|
|
11792
|
+
const state = await readManagedInstructionBlockState(destPath, selector, transport);
|
|
11793
|
+
if (!state.hasBlock || state.drifted || state.hash !== entry.hash) {
|
|
11794
|
+
throw new Error(`Managed artifact is drifted at ${destPath}: managed block is missing or changed`);
|
|
11795
|
+
}
|
|
11796
|
+
return entry.hash;
|
|
11797
|
+
}
|
|
11798
|
+
if (entry.mergeStrategy) {
|
|
11799
|
+
if (!hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
11800
|
+
throw new Error(`Ownership handoff cannot verify incomplete merge ownership at ${destPath}`);
|
|
11801
|
+
}
|
|
11802
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, await transport.readFile(destPath));
|
|
11803
|
+
return entry.hash;
|
|
11804
|
+
}
|
|
11805
|
+
const currentHash = await transport.hashPath(destPath);
|
|
11806
|
+
if (currentHash !== entry.hash) {
|
|
11807
|
+
throw new Error(`Managed artifact is drifted at ${destPath}: manifest ${entry.hash}, current ${currentHash}`);
|
|
11808
|
+
}
|
|
11809
|
+
return currentHash;
|
|
11810
|
+
}
|
|
11762
11811
|
function containedArtifactPath(targetRoot, relativePath) {
|
|
11763
11812
|
if (!relativePath || relativePath.startsWith("/") || relativePath.includes("\0")) {
|
|
11764
11813
|
throw new Error(`Unsafe managed artifact path: ${relativePath}`);
|
|
@@ -13767,7 +13816,7 @@ async function inspectInstalledState(source, destination, packageNames) {
|
|
|
13767
13816
|
throw new Error(`Partial ownership at ${sourceEntry.renderedPath} includes packages outside the normalization selection.`);
|
|
13768
13817
|
}
|
|
13769
13818
|
const destinationRenderedPath = renderedEntryPathForRoot(destinationState.installRoot, sourceEntry.entry.path);
|
|
13770
|
-
await
|
|
13819
|
+
await assertEquivalentRuntimeState(sourceEntry.entry, sourceEntry.renderedPath, destinationRenderedPath);
|
|
13771
13820
|
plannedDestinationPaths.add(destinationRenderedPath);
|
|
13772
13821
|
sourceRenderedPaths.push(sourceEntry.renderedPath);
|
|
13773
13822
|
destinationRenderedPaths.push(destinationRenderedPath);
|
|
@@ -13931,7 +13980,7 @@ async function inspectLegacySelfInstalledState(fleet, packageNames, profileName,
|
|
|
13931
13980
|
if (!coveredByCurrentGraph && !matchesCurrentGraph && !orphanedOwners.has(entry.entry.workspaceOwner)) {
|
|
13932
13981
|
throw new Error(`Legacy manifest entry is not covered by its graph lock: ${entry.renderedPath}`);
|
|
13933
13982
|
}
|
|
13934
|
-
await
|
|
13983
|
+
await assertEquivalentRuntimeState(entry.entry, entry.renderedPath, entry.renderedPath);
|
|
13935
13984
|
renderedPaths.add(entry.renderedPath);
|
|
13936
13985
|
sourceRenderedPaths.push(entry.renderedPath);
|
|
13937
13986
|
if (coveredByCurrentGraph || matchesCurrentGraph) {
|
|
@@ -14341,6 +14390,33 @@ async function assertEquivalentRuntimeBytes(sourcePath, destinationPath, expecte
|
|
|
14341
14390
|
throw new Error(`Runtime content drift at ${destinationPath}; source and destination bytes are not equivalent.`);
|
|
14342
14391
|
}
|
|
14343
14392
|
}
|
|
14393
|
+
async function assertEquivalentRuntimeState(entry, sourcePath, destinationPath) {
|
|
14394
|
+
if (entry.mode === "managed-block") {
|
|
14395
|
+
const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
|
|
14396
|
+
for (const path of /* @__PURE__ */ new Set([sourcePath, destinationPath])) {
|
|
14397
|
+
const state = await readManagedInstructionBlockState(path, selector, localTransport);
|
|
14398
|
+
if (!state.exists || !state.hasBlock || state.drifted || state.hash !== entry.hash) {
|
|
14399
|
+
throw new Error(`Runtime managed-block drift at ${path}; the installed contribution is missing or changed.`);
|
|
14400
|
+
}
|
|
14401
|
+
}
|
|
14402
|
+
return;
|
|
14403
|
+
}
|
|
14404
|
+
if (entry.mergeStrategy) {
|
|
14405
|
+
for (const path of /* @__PURE__ */ new Set([sourcePath, destinationPath])) {
|
|
14406
|
+
if (!await pathExists(path)) throw new Error(`Runtime merge destination is missing: ${path}`);
|
|
14407
|
+
const content = await readFile37(path, "utf8");
|
|
14408
|
+
if (hasMergeRemovalContent(entry.mergeRemoval)) {
|
|
14409
|
+
assertExactMergeContribution(entry.mergeRemoval, entry.mergeStrategy, content);
|
|
14410
|
+
} else if (resolve25(sourcePath) === resolve25(destinationPath) && entry.mergeStrategy !== "codex-toml-mcp") {
|
|
14411
|
+
assertExactMergeContribution({}, entry.mergeStrategy, content);
|
|
14412
|
+
} else {
|
|
14413
|
+
throw new Error(`Installed-state normalization cannot prove incomplete merge ownership at ${path}.`);
|
|
14414
|
+
}
|
|
14415
|
+
}
|
|
14416
|
+
return;
|
|
14417
|
+
}
|
|
14418
|
+
await assertEquivalentRuntimeBytes(sourcePath, destinationPath, entry.hash);
|
|
14419
|
+
}
|
|
14344
14420
|
function renderedEntryPathForRoot(root, entryPath) {
|
|
14345
14421
|
const normalizedRoot = resolve25(root);
|
|
14346
14422
|
const candidate = resolve25(normalizedRoot, entryPath);
|
|
@@ -14448,8 +14524,8 @@ function workspaceOwners(scope) {
|
|
|
14448
14524
|
]);
|
|
14449
14525
|
}
|
|
14450
14526
|
function assertSimpleVerifiableEntry(entry, path) {
|
|
14451
|
-
if (entry.semanticPlugin
|
|
14452
|
-
throw new Error(`Installed-state normalization cannot byte-verify semantic
|
|
14527
|
+
if (entry.semanticPlugin) {
|
|
14528
|
+
throw new Error(`Installed-state normalization cannot byte-verify semantic plugin entry ${path}.`);
|
|
14453
14529
|
}
|
|
14454
14530
|
if (entry.kind !== "file" && entry.kind !== "dir") throw new Error(`Unsupported installed entry kind at ${path}.`);
|
|
14455
14531
|
}
|
|
@@ -14481,7 +14557,7 @@ function graphArtifactIdentity(artifact) {
|
|
|
14481
14557
|
dependencyRole: artifact.dependencyRole,
|
|
14482
14558
|
owners: artifact.owners,
|
|
14483
14559
|
kind: artifact.kind,
|
|
14484
|
-
sourceHash: artifact.hash,
|
|
14560
|
+
...artifact.composedFrom?.length ? { composedFrom: artifact.composedFrom } : { sourceHash: artifact.hash },
|
|
14485
14561
|
channel: artifact.channel
|
|
14486
14562
|
});
|
|
14487
14563
|
}
|
|
@@ -14495,7 +14571,7 @@ function graphEntryIdentity(entry) {
|
|
|
14495
14571
|
dependencyRole: entry.dependencyRole,
|
|
14496
14572
|
owners: entry.owners,
|
|
14497
14573
|
kind: entry.kind,
|
|
14498
|
-
sourceHash: entry.sourceHash,
|
|
14574
|
+
...entry.composedFrom?.length ? { composedFrom: entry.composedFrom } : { sourceHash: entry.sourceHash },
|
|
14499
14575
|
channel: entry.channel
|
|
14500
14576
|
});
|
|
14501
14577
|
}
|
|
@@ -15134,7 +15210,7 @@ program.command("remember").description("append text to the local instructions o
|
|
|
15134
15210
|
console.log(nextInstallNudge());
|
|
15135
15211
|
});
|
|
15136
15212
|
var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
|
|
15137
|
-
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").requiredOption("--from-workspace-root <path>", "current owning workspace root").requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--expected-hash <sha256>", "expected current artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--profile <name>", "workspace runtime profile (must resolve to one target)").option("--dry-run", "validate all preconditions without writing the manifest", false).action(async (selector, options) => {
|
|
15213
|
+
ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").requiredOption("--from-workspace-root <path>", "current owning workspace root").requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--to-fleet <id>", "qualify the new owner with a registered fleet id").option("--expected-hash <sha256>", "expected current artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet").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 agent from merged config").option("--profile <name>", "workspace runtime profile (must resolve to one target)").option("--dry-run", "validate all preconditions without writing the manifest", false).action(async (selector, options) => {
|
|
15138
15214
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
15139
15215
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
15140
15216
|
}
|
|
@@ -15152,6 +15228,13 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
15152
15228
|
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
15153
15229
|
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
15154
15230
|
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
15231
|
+
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
15232
|
+
if (options.toFleet) {
|
|
15233
|
+
const fleet = await showRegisteredFleet(options.toFleet);
|
|
15234
|
+
if (resolve26(fleet.root) !== resolve26(toWorkspaceRoot)) {
|
|
15235
|
+
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
15236
|
+
}
|
|
15237
|
+
}
|
|
15155
15238
|
const request = {
|
|
15156
15239
|
...state,
|
|
15157
15240
|
targetRoot: state.installRoot,
|
|
@@ -15159,7 +15242,8 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
15159
15242
|
artifactType,
|
|
15160
15243
|
artifactName,
|
|
15161
15244
|
fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
|
|
15162
|
-
toWorkspaceRoot
|
|
15245
|
+
toWorkspaceRoot,
|
|
15246
|
+
toFleetId: options.toFleet,
|
|
15163
15247
|
expectedHash: options.expectedHash,
|
|
15164
15248
|
expectedRevision: options.expectedRevision,
|
|
15165
15249
|
transport: transportForTarget(target)
|
|
@@ -15805,6 +15889,7 @@ async function runExactMcpRetirement(packageName, options) {
|
|
|
15805
15889
|
onlySource: true,
|
|
15806
15890
|
retireExactMcp: true,
|
|
15807
15891
|
expectedFromWorkspaceOwner,
|
|
15892
|
+
deferForeignStateCheck: true,
|
|
15808
15893
|
dryRun: true
|
|
15809
15894
|
}, { mode: "install" });
|
|
15810
15895
|
if (results.length !== 1) {
|
package/openpack.json
CHANGED
package/package.json
CHANGED