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 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 !== 1) {
10493
- throw new Error("Exact MCP retirement requires exactly one MCP server and no non-MCP configuration.");
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 transport.hashPath(destPath);
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 assertEquivalentRuntimeBytes(sourceEntry.renderedPath, destinationRenderedPath, sourceEntry.entry.hash);
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 assertEquivalentRuntimeBytes(entry.renderedPath, entry.renderedPath, entry.entry.hash);
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 || entry.mergeStrategy || entry.mode) {
14452
- throw new Error(`Installed-state normalization cannot byte-verify semantic, merge, or managed-block entry ${path}.`);
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: normalizeCliPath(options.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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "name": "NestDevLab/agentwheel",
4
- "version": "0.19.0",
4
+ "version": "0.19.2",
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.19.0",
3
+ "version": "0.19.2",
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.19.0"
8
+ version: "0.19.2"
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.19.0"
8
+ version: "0.19.2"
9
9
  ---
10
10
 
11
11
  # Agentwheel Discovery