agentwheel 0.20.18 → 0.20.19

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
@@ -132,6 +132,12 @@ Apply the same command with the reviewed values added as `--expected-hash <sha25
132
132
  journal, rechecks the old owner and the live artifact hash, then atomically updates only the install
133
133
  manifest. Run one target at a time; named SSH agents use the same contract through `--agent`.
134
134
 
135
+ Use `--from-unknown-owner` instead of `--from-workspace-root` for an entry recorded as
136
+ `workspace:unknown`. A plain file or directory whose runtime no longer matches the recorded hash is
137
+ refused unless `--carry-drift` is passed: the dry-run prints the runtime hash, the apply also needs
138
+ `--expected-runtime-hash <sha256>`, and the manifest keeps the recorded hash so the next install
139
+ still reports the drift. Merge and managed-block entries never carry drift.
140
+
135
141
  For a one-time MCP rename, keep the legacy artifact in a dedicated cutover workspace rather than
136
142
  the canonical desired-state profile. Set the legacy manifest `stateKey` on its named agent, then
137
143
  preview one runtime at a time:
@@ -5,9 +5,9 @@ import {
5
5
  commitManifestMetadataJournal,
6
6
  recoverPendingApply,
7
7
  uninstall
8
- } from "./chunk-A44XYLUT.js";
9
- import "./chunk-FP26KSBM.js";
10
- import "./chunk-GE7F76AC.js";
8
+ } from "./chunk-L37KQVRS.js";
9
+ import "./chunk-BHT57MK5.js";
10
+ import "./chunk-BNIF3FUR.js";
11
11
  export {
12
12
  applyCombinedInstallPlan,
13
13
  applyInstallPlan,
@@ -4,7 +4,7 @@ import {
4
4
  hashPath,
5
5
  pathExists,
6
6
  writeJsonAtomic
7
- } from "./chunk-GE7F76AC.js";
7
+ } from "./chunk-BNIF3FUR.js";
8
8
 
9
9
  // src/install/transaction.ts
10
10
  import { createHash as createHash6 } from "crypto";
@@ -2865,8 +2865,8 @@ async function recoverMutationRuntime(operationId, options = {}) {
2865
2865
  }
2866
2866
  mutation = GovernedMutation.activateExisting(receipt, baseline, lock);
2867
2867
  const [{ recoverPendingApply }, { readLinkedLocalApplyJournal: readLinkedLocalApplyJournal2, removeApplyJournal: removeApplyJournal2, localPathExists: localPathExists2 }] = await Promise.all([
2868
- import("./apply-ZGCJLZMZ.js"),
2869
- import("./transaction-4KBNHDQX.js")
2868
+ import("./apply-QJHTL7IK.js"),
2869
+ import("./transaction-JHZJWBII.js")
2870
2870
  ]);
2871
2871
  let missingReservation = false;
2872
2872
  for (const entry of pending) {
@@ -24,6 +24,14 @@ async function pathExists(path) {
24
24
  return false;
25
25
  }
26
26
  }
27
+ async function removeOnFailure(path, run) {
28
+ try {
29
+ return await run();
30
+ } catch (error) {
31
+ await rm(path, { recursive: true, force: true });
32
+ throw error;
33
+ }
34
+ }
27
35
  async function hashPath(path) {
28
36
  const stats = await stat(path);
29
37
  if (stats.isFile()) {
@@ -129,6 +137,7 @@ function isAlreadyExists(error) {
129
137
 
130
138
  export {
131
139
  pathExists,
140
+ removeOnFailure,
132
141
  hashPath,
133
142
  listFiles,
134
143
  isIgnoredGeneratedEntry,
@@ -24,11 +24,11 @@ import {
24
24
  rollbackCompletedOperations,
25
25
  sourceLockPath,
26
26
  writeApplyJournal
27
- } from "./chunk-FP26KSBM.js";
27
+ } from "./chunk-BHT57MK5.js";
28
28
  import {
29
29
  pathExists,
30
30
  writeJsonAtomic
31
- } from "./chunk-GE7F76AC.js";
31
+ } from "./chunk-BNIF3FUR.js";
32
32
 
33
33
  // src/install/apply.ts
34
34
  import { execFile } from "child_process";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  pathExists
4
- } from "./chunk-GE7F76AC.js";
4
+ } from "./chunk-BNIF3FUR.js";
5
5
 
6
6
  // src/source/identify.ts
7
7
  import { homedir } from "os";
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  inferSourceDriverName,
4
4
  isExplicitSource
5
- } from "./chunk-U2GRG4UI.js";
6
- import "./chunk-GE7F76AC.js";
5
+ } from "./chunk-QABA4WUP.js";
6
+ import "./chunk-BNIF3FUR.js";
7
7
  export {
8
8
  inferSourceDriverName,
9
9
  isExplicitSource
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ import {
37
37
  uninstall,
38
38
  withManifestRevision,
39
39
  writeInstallManifest
40
- } from "./chunk-A44XYLUT.js";
40
+ } from "./chunk-L37KQVRS.js";
41
41
  import {
42
42
  CURRENT_WORKSPACE_SCHEMA_VERSION,
43
43
  GovernedMutation,
@@ -98,25 +98,26 @@ import {
98
98
  workspaceSelectionImportSchema,
99
99
  writeApplyJournal,
100
100
  writeWorkspaceConfig
101
- } from "./chunk-FP26KSBM.js";
101
+ } from "./chunk-BHT57MK5.js";
102
102
  import {
103
103
  inferSourceDriverName
104
- } from "./chunk-U2GRG4UI.js";
104
+ } from "./chunk-QABA4WUP.js";
105
105
  import {
106
106
  hashPath,
107
107
  isAlreadyExists,
108
108
  isIgnoredGeneratedEntry,
109
109
  listFiles,
110
110
  pathExists,
111
+ removeOnFailure,
111
112
  withFilesystemLock,
112
113
  writeJsonAtomic
113
- } from "./chunk-GE7F76AC.js";
114
+ } from "./chunk-BNIF3FUR.js";
114
115
 
115
116
  // src/cli/index.ts
116
117
  import { createHash as createHash16 } from "crypto";
117
118
  import { existsSync } from "fs";
118
- import { mkdir as mkdir16, mkdtemp as mkdtemp4, realpath as realpath3, rm as rm10, writeFile as writeFile13 } from "fs/promises";
119
- import { homedir as homedir12, tmpdir as tmpdir5 } from "os";
119
+ import { mkdir as mkdir16, mkdtemp as mkdtemp3, realpath as realpath3, rm as rm10, writeFile as writeFile13 } from "fs/promises";
120
+ import { homedir as homedir12, tmpdir as tmpdir4 } from "os";
120
121
  import { basename as basename21, dirname as dirname23, isAbsolute as isAbsolute5, join as join46, relative as relative11, resolve as resolve27 } from "path";
121
122
  import { fileURLToPath as fileURLToPath3 } from "url";
122
123
  import { Command } from "commander";
@@ -4079,6 +4080,58 @@ function errorMessage3(error) {
4079
4080
  return error instanceof Error ? error.message : String(error);
4080
4081
  }
4081
4082
 
4083
+ // src/source/git-source.ts
4084
+ import { posix } from "path";
4085
+ var SUBPATH_SEPARATOR = "//";
4086
+ function parseGitSource(source) {
4087
+ if (source.startsWith("github:")) {
4088
+ const rest = source.slice("github:".length);
4089
+ const hashIndex = rest.indexOf("#");
4090
+ const repo = hashIndex >= 0 ? rest.slice(0, hashIndex) : rest;
4091
+ if (!repo.includes("/")) throw new Error(`Invalid GitHub source: ${source}`);
4092
+ return {
4093
+ url: `https://github.com/${repo.replace(/\.git$/i, "")}.git`,
4094
+ ...splitFragment(hashIndex >= 0 ? rest.slice(hashIndex + 1) : void 0, source)
4095
+ };
4096
+ }
4097
+ if (source.startsWith("git:")) {
4098
+ const rest = source.slice("git:".length);
4099
+ const hashIndex = rest.lastIndexOf("#");
4100
+ if (hashIndex < 0) return { url: rest };
4101
+ return { url: rest.slice(0, hashIndex), ...splitFragment(rest.slice(hashIndex + 1), source) };
4102
+ }
4103
+ throw new Error(`Invalid git source: ${source}`);
4104
+ }
4105
+ function formatGitSource(url, ref, subpath) {
4106
+ return subpath ? `git:${url}#${ref}${SUBPATH_SEPARATOR}${subpath}` : `git:${url}#${ref}`;
4107
+ }
4108
+ function normalizeGitSubpath(subpath, source) {
4109
+ if (subpath === void 0) return void 0;
4110
+ if (subpath.includes("\\")) throw new Error(`Git source subpath must use forward slashes: ${source}`);
4111
+ if (subpath.startsWith("/")) throw new Error(`Git source subpath must be relative to the repository: ${source}`);
4112
+ const normalized = posix.normalize(subpath).replace(/\/+$/g, "");
4113
+ if (normalized === "." || normalized === "") return void 0;
4114
+ if (normalized === ".." || normalized.startsWith("../")) {
4115
+ throw new Error(`Git source subpath escapes the repository: ${source}`);
4116
+ }
4117
+ return normalized;
4118
+ }
4119
+ function resolveGitRelativeSubpath(declaringSubpath, relative12, source) {
4120
+ const joined = posix.normalize(posix.join(declaringSubpath ?? ".", relative12));
4121
+ if (joined === ".." || joined.startsWith("../")) {
4122
+ throw new Error(`Relative dependency '${relative12}' escapes the git repository it is declared in: ${source}`);
4123
+ }
4124
+ return normalizeGitSubpath(joined, source);
4125
+ }
4126
+ function splitFragment(fragment, source) {
4127
+ if (fragment === void 0) return {};
4128
+ const separator = fragment.indexOf(SUBPATH_SEPARATOR);
4129
+ if (separator < 0) return fragment ? { ref: fragment } : {};
4130
+ const ref = fragment.slice(0, separator);
4131
+ const subpath = normalizeGitSubpath(fragment.slice(separator + SUBPATH_SEPARATOR.length), source);
4132
+ return { ...ref ? { ref } : {}, ...subpath ? { subpath } : {} };
4133
+ }
4134
+
4082
4135
  // src/source/git.ts
4083
4136
  var execFileAsync = promisify(execFile);
4084
4137
  var GitSourceDriver = class {
@@ -4138,15 +4191,18 @@ var GitSourceDriver = class {
4138
4191
  return { path, leasePath };
4139
4192
  }
4140
4193
  );
4141
- const snapshotPath = snapshot.path;
4142
- const manifest = await readPackageManifest(snapshotPath);
4194
+ const packageRoot = parsed.subpath ? join19(snapshot.path, parsed.subpath) : snapshot.path;
4195
+ if (parsed.subpath && !await isDirectory(packageRoot)) {
4196
+ throw new Error(`Git source subpath '${parsed.subpath}' does not exist at ${resolvedCommit} in ${parsed.url}`);
4197
+ }
4198
+ const manifest = await readPackageManifest(packageRoot);
4143
4199
  return {
4144
4200
  ...resolved,
4145
- resolvedPath: snapshotPath,
4201
+ resolvedPath: packageRoot,
4146
4202
  packageName: manifest?.name,
4147
4203
  packageVersion: manifest?.version,
4148
4204
  resolvedCommit,
4149
- sourceHash: await hashPath(snapshotPath),
4205
+ sourceHash: await hashPath(packageRoot),
4150
4206
  cacheLeasePath: snapshot.leasePath
4151
4207
  };
4152
4208
  }, "git cache");
@@ -4164,22 +4220,12 @@ var GitSourceDriver = class {
4164
4220
  return resolved;
4165
4221
  }
4166
4222
  };
4167
- function parseGitSource(source) {
4168
- if (source.startsWith("github:")) {
4169
- const rest = source.slice("github:".length);
4170
- const [repo, ref] = rest.split("#", 2);
4171
- if (!repo.includes("/")) throw new Error(`Invalid GitHub source: ${source}`);
4172
- return { url: `https://github.com/${repo}.git`, ref };
4173
- }
4174
- if (source.startsWith("git:")) {
4175
- const rest = source.slice("git:".length);
4176
- const hashIndex = rest.lastIndexOf("#");
4177
- if (hashIndex >= 0) {
4178
- return { url: rest.slice(0, hashIndex), ref: rest.slice(hashIndex + 1) };
4179
- }
4180
- return { url: rest };
4223
+ async function isDirectory(path) {
4224
+ try {
4225
+ return (await stat4(path)).isDirectory();
4226
+ } catch {
4227
+ return false;
4181
4228
  }
4182
- throw new Error(`Invalid git source: ${source}`);
4183
4229
  }
4184
4230
  function cachePathFor2(url, cacheRoot) {
4185
4231
  const root = cacheRoot ? resolve5(cacheRoot) : join19(homedir2(), ".agentwheel", "cache");
@@ -5337,7 +5383,8 @@ function claudeAgentName(artifact) {
5337
5383
 
5338
5384
  // src/staging/staging.ts
5339
5385
  async function stageSource(driver, source, options = {}) {
5340
- return renderStagedBundle(await stageSourceRaw(driver, source, options), options);
5386
+ const raw = await stageSourceRaw(driver, source, options);
5387
+ return removeOnFailure(raw.root, () => renderStagedBundle(raw, options));
5341
5388
  }
5342
5389
  async function stageSourceRaw(driver, source, options = {}) {
5343
5390
  const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(source, options))));
@@ -5352,25 +5399,27 @@ async function stageResolvedSourceRaw(driver, resolved) {
5352
5399
  const artifacts = await driver.list(resolved);
5353
5400
  return stageResolvedArtifactsRaw(resolved, artifacts);
5354
5401
  }
5355
- async function stageResolvedArtifactsRaw(resolved, artifacts) {
5356
- const root = await mkdtemp(join27(tmpdir2(), "agentwheel-stage-"));
5402
+ async function stageResolvedArtifactsRaw(resolved, artifacts, parentDir = tmpdir2()) {
5403
+ const root = await mkdtemp(join27(parentDir, "agentwheel-stage-"));
5357
5404
  const stagedArtifacts = [];
5358
- for (const artifact of artifacts) {
5359
- const stagedPath = join27(root, artifact.relativePath);
5360
- await mkdir11(dirname13(stagedPath), { recursive: true });
5361
- await cp3(artifact.sourcePath, stagedPath, {
5362
- recursive: artifact.kind === "dir",
5363
- dereference: true,
5364
- filter: (path) => !isIgnoredGeneratedEntry(basename16(path))
5365
- });
5366
- await composeAssets(artifact, resolved.resolvedPath, stagedPath);
5367
- stagedArtifacts.push({
5368
- ...artifact,
5369
- stagedPath,
5370
- hash: await hashPath(stagedPath),
5371
- channel: artifact.channel ?? "managed"
5372
- });
5373
- }
5405
+ await removeOnFailure(root, async () => {
5406
+ for (const artifact of artifacts) {
5407
+ const stagedPath = join27(root, artifact.relativePath);
5408
+ await mkdir11(dirname13(stagedPath), { recursive: true });
5409
+ await cp3(artifact.sourcePath, stagedPath, {
5410
+ recursive: artifact.kind === "dir",
5411
+ dereference: true,
5412
+ filter: (path) => !isIgnoredGeneratedEntry(basename16(path))
5413
+ });
5414
+ await composeAssets(artifact, resolved.resolvedPath, stagedPath);
5415
+ stagedArtifacts.push({
5416
+ ...artifact,
5417
+ stagedPath,
5418
+ hash: await hashPath(stagedPath),
5419
+ channel: artifact.channel ?? "managed"
5420
+ });
5421
+ }
5422
+ });
5374
5423
  return {
5375
5424
  root,
5376
5425
  source: resolved,
@@ -5527,8 +5576,7 @@ import { dirname as dirname15, join as join30, relative as relative6 } from "pat
5527
5576
 
5528
5577
  // src/resolve/graph.ts
5529
5578
  import { createHash as createHash6 } from "crypto";
5530
- import { mkdtemp as mkdtemp2, readdir as readdir7, readFile as readFile17, stat as stat11 } from "fs/promises";
5531
- import { tmpdir as tmpdir3 } from "os";
5579
+ import { readdir as readdir7, readFile as readFile17, stat as stat11 } from "fs/promises";
5532
5580
  import { basename as basename17, extname as extname4, join as join29 } from "path";
5533
5581
 
5534
5582
  // src/model/workspace-composition.ts
@@ -5789,7 +5837,7 @@ var RegistryClient = class {
5789
5837
  }
5790
5838
  };
5791
5839
  async function resolvePackageSource(source, workspaceRoot, options = {}) {
5792
- const { isExplicitSource } = await import("./identify-O54QXPQR.js");
5840
+ const { isExplicitSource } = await import("./identify-M6IA4RS7.js");
5793
5841
  if (await isExplicitSource(source)) return { source };
5794
5842
  const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
5795
5843
  if (!entry) {
@@ -5831,6 +5879,17 @@ async function normalizeDependencySource(source, options) {
5831
5879
  if (isBareRegistryName(trimmed)) {
5832
5880
  return normalizeRegistrySource(trimmed, options);
5833
5881
  }
5882
+ if (options.declaringGitSource && (trimmed === "." || trimmed === ".." || trimmed.startsWith("./") || trimmed.startsWith("../"))) {
5883
+ const declaring = options.declaringGitSource;
5884
+ const subpath = resolveGitRelativeSubpath(declaring.subpath, trimmed, source);
5885
+ const normalizedSource = formatGitSource(declaring.url, declaring.commit, subpath);
5886
+ return {
5887
+ source: normalizedSource,
5888
+ normalizedSource,
5889
+ driver: "git",
5890
+ requestedRef: declaring.commit
5891
+ };
5892
+ }
5834
5893
  if (isLocalSource(trimmed)) {
5835
5894
  const path = localSourcePath(trimmed);
5836
5895
  const resolvedPath = resolveLocalPath(path, options.declaringPackageRoot);
@@ -5841,10 +5900,10 @@ async function normalizeDependencySource(source, options) {
5841
5900
  };
5842
5901
  }
5843
5902
  if (trimmed.startsWith("github:") || trimmed.startsWith("git:")) {
5844
- const parsed = parseGitSource2(trimmed);
5903
+ const parsed = parseGitSource(trimmed);
5845
5904
  const requestedRef = options.ref ?? parsed.ref ?? "HEAD";
5846
5905
  const url = normalizeGitRemoteUrl(parsed.url, options.declaringPackageRoot);
5847
- const normalizedSource = `git:${url}#${requestedRef}`;
5906
+ const normalizedSource = formatGitSource(url, requestedRef, parsed.subpath);
5848
5907
  return {
5849
5908
  source: normalizedSource,
5850
5909
  normalizedSource,
@@ -5921,20 +5980,6 @@ async function normalizeRegistrySource(name, options) {
5921
5980
  normalizedSource: `registry:${name}:${resolved.normalizedSource}`
5922
5981
  };
5923
5982
  }
5924
- function parseGitSource2(source) {
5925
- if (source.startsWith("github:")) {
5926
- const rest2 = source.slice("github:".length);
5927
- const [repo, ref] = rest2.split("#", 2);
5928
- if (!repo.includes("/")) throw new Error(`Invalid GitHub dependency source: ${source}`);
5929
- return { url: `https://github.com/${repo.replace(/\.git$/i, "")}.git`, ref };
5930
- }
5931
- const rest = source.slice("git:".length);
5932
- const hashIndex = rest.lastIndexOf("#");
5933
- if (hashIndex >= 0) {
5934
- return { url: rest.slice(0, hashIndex), ref: rest.slice(hashIndex + 1) };
5935
- }
5936
- return { url: rest };
5937
- }
5938
5983
  function normalizeGitRemoteUrl(url, declaringPackageRoot) {
5939
5984
  if (url.startsWith("./") || url.startsWith("../") || url.startsWith("/") || url === "~" || url.startsWith("~/")) {
5940
5985
  return resolveLocalPath(url, declaringPackageRoot);
@@ -5986,7 +6031,6 @@ var SnapshotConflictError = class extends Error {
5986
6031
  var cacheLocks = /* @__PURE__ */ new Map();
5987
6032
  async function resolveDependencyGraph(roots, options) {
5988
6033
  if (roots.length === 0) throw new Error("At least one graph root is required.");
5989
- const graphRoot = await mkdtemp2(join29(tmpdir3(), "agentwheel-graph-"));
5990
6034
  const fetchCache = /* @__PURE__ */ new Map();
5991
6035
  const nodesByKey = /* @__PURE__ */ new Map();
5992
6036
  const nodesBySource = /* @__PURE__ */ new Map();
@@ -6052,7 +6096,6 @@ async function resolveDependencyGraph(roots, options) {
6052
6096
  }
6053
6097
  detectDirectCollisions(rawNodes);
6054
6098
  return {
6055
- root: graphRoot,
6056
6099
  roots: rootResults.sort((a, b) => a.rootId.localeCompare(b.rootId)),
6057
6100
  nodes: rawNodes.map((state) => state.node),
6058
6101
  rawNodes,
@@ -6094,6 +6137,7 @@ async function processRequirement(requirement, options, fetchCache, nodesByKey,
6094
6137
  let lockedByReference = lockedNodeForRequirementReference(requirement, options, lockLabel);
6095
6138
  let normalized = lockedByReference ? normalizedSourceFromLockedNode(lockedByReference.node) : await normalizeDependencySource(requirement.source, {
6096
6139
  declaringPackageRoot: requirement.declaringPackageRoot,
6140
+ declaringGitSource: requirement.declaringGitSource,
6097
6141
  workspaceRoot: options.workspaceRoot,
6098
6142
  ref: requirement.ref,
6099
6143
  registryClient: options.registryClient
@@ -6101,6 +6145,7 @@ async function processRequirement(requirement, options, fetchCache, nodesByKey,
6101
6145
  if (lockedByReference && shouldCheckLockedRootSource(requirement)) {
6102
6146
  const declared = await normalizeDependencySource(requirement.source, {
6103
6147
  declaringPackageRoot: requirement.declaringPackageRoot,
6148
+ declaringGitSource: requirement.declaringGitSource,
6104
6149
  workspaceRoot: options.workspaceRoot,
6105
6150
  ref: requirement.ref,
6106
6151
  registryClient: options.registryClient
@@ -6378,6 +6423,7 @@ function dependencyRequirement(state, fetched, alias, dependency, select, chain,
6378
6423
  useLock: dependency.mode !== "tracking" || options.lockedResolution === true && !updateClosure,
6379
6424
  updateClosure,
6380
6425
  declaringPackageRoot: fetched.resolved.resolvedPath,
6426
+ declaringGitSource: declaringGitSource(fetched),
6381
6427
  requiredBy: state.node.id,
6382
6428
  alias,
6383
6429
  parentId: state.node.id,
@@ -6885,6 +6931,15 @@ async function mapLimit(items, limit, fn) {
6885
6931
  if (failure !== void 0) throw failure;
6886
6932
  return out;
6887
6933
  }
6934
+ function declaringGitSource(fetched) {
6935
+ if (fetched.resolved.driver !== "git" || !fetched.resolved.resolvedCommit) return void 0;
6936
+ const parsed = parseGitSource(fetched.resolved.source);
6937
+ return {
6938
+ url: normalizeGitRemoteUrl(parsed.url, fetched.resolved.resolvedPath),
6939
+ commit: fetched.resolved.resolvedCommit,
6940
+ subpath: parsed.subpath
6941
+ };
6942
+ }
6888
6943
 
6889
6944
  // src/lifecycle/customization.ts
6890
6945
  async function remember(workspaceRoot, runtime, text) {
@@ -7039,7 +7094,7 @@ import { rm as rm7 } from "fs/promises";
7039
7094
  // src/lifecycle/source-plan.ts
7040
7095
  import { createHash as createHash8 } from "crypto";
7041
7096
  import { mkdir as mkdir14, readdir as readdir9 } from "fs/promises";
7042
- import { basename as basename18, dirname as dirname17, join as join33, posix as posix2, resolve as resolve16 } from "path";
7097
+ import { basename as basename18, dirname as dirname17, join as join33, posix as posix3, resolve as resolve16 } from "path";
7043
7098
 
7044
7099
  // src/resolve/graph-diff.ts
7045
7100
  function diffGraphLocks(previous, next) {
@@ -7202,11 +7257,14 @@ function formatSelectionImport(root) {
7202
7257
 
7203
7258
  // src/resolve/render.ts
7204
7259
  import { createHash as createHash7 } from "crypto";
7205
- import { readFile as readFile18, mkdtemp as mkdtemp3 } from "fs/promises";
7206
- import { tmpdir as tmpdir4 } from "os";
7260
+ import { readFile as readFile18, mkdtemp as mkdtemp2 } from "fs/promises";
7261
+ import { tmpdir as tmpdir3 } from "os";
7207
7262
  import { join as join31 } from "path";
7208
7263
  async function renderGraphForTarget(graph, targetContext = {}) {
7209
- const root = await mkdtemp3(join31(tmpdir4(), "agentwheel-render-"));
7264
+ const root = await mkdtemp2(join31(tmpdir3(), "agentwheel-render-"));
7265
+ return removeOnFailure(root, () => renderGraphIntoRoot(root, graph, targetContext));
7266
+ }
7267
+ async function renderGraphIntoRoot(root, graph, targetContext) {
7210
7268
  const artifacts = [];
7211
7269
  const stagedNodes = /* @__PURE__ */ new Map();
7212
7270
  const includeEdges = /* @__PURE__ */ new Map();
@@ -7214,7 +7272,7 @@ async function renderGraphForTarget(graph, targetContext = {}) {
7214
7272
  for (const rawNode of graph.rawNodes) {
7215
7273
  const ownsCompositionRules = rawNode.depth === 0 && rawNode.manifest?.schemaVersion === 3 && Boolean(rawNode.manifest.compositionRules?.length);
7216
7274
  if (rawNode.node.selected.length === 0 && !ownsCompositionRules) continue;
7217
- const rawBundle = await stageResolvedArtifactsRaw(rawNode.resolved, rawNode.artifacts);
7275
+ const rawBundle = await stageResolvedArtifactsRaw(rawNode.resolved, rawNode.artifacts, root);
7218
7276
  const fragmentCustomized = targetContext.workspaceRoot && targetContext.adapter ? await applyFragmentCustomizations(rawBundle.artifacts, {
7219
7277
  workspaceRoot: targetContext.workspaceRoot,
7220
7278
  adapter: targetContext.adapter,
@@ -8081,7 +8139,7 @@ function containsPath(parent, candidate) {
8081
8139
  }
8082
8140
 
8083
8141
  // src/model/target-state.ts
8084
- import { posix, resolve as resolve15 } from "path";
8142
+ import { posix as posix2, resolve as resolve15 } from "path";
8085
8143
  var targetEvolutionFields = /* @__PURE__ */ new Set([
8086
8144
  "adapterCodeHash",
8087
8145
  "adapterConfig",
@@ -8114,7 +8172,7 @@ function persistentTargetParts(value) {
8114
8172
  );
8115
8173
  }
8116
8174
  function normalizeInstallRoot(path, transportKind) {
8117
- return transportKind === "ssh" ? posix.normalize(path) : resolve15(path);
8175
+ return transportKind === "ssh" ? posix2.normalize(path) : resolve15(path);
8118
8176
  }
8119
8177
  function targetEndpoint(value, transportKind) {
8120
8178
  if (transportKind !== "ssh") return { kind: "local" };
@@ -8271,85 +8329,87 @@ async function createGraphSourcePlan(options) {
8271
8329
  noDeps: options.noDeps,
8272
8330
  warn
8273
8331
  });
8274
- const desiredArtifacts = filterArtifactsByAdapterTargets(
8275
- desiredArtifactsFromGraphBundle(bundle),
8276
- options.adapter,
8277
- installationType,
8278
- { warn }
8279
- );
8280
- const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
8281
- const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
8282
- const graphLockDigest = digestGraphLock(bundle.graphLock);
8283
- const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
8284
- if (normalizeInstallRoot2(resolvedInstallRoot, transport.kind) !== normalizeInstallRoot2(installRoot, transport.kind)) {
8285
- throw new Error(
8286
- `Adapter ${options.adapter.name} resolved a different install root after rendering; target state identity cannot be proven.`
8332
+ return removeOnFailure(bundle.root, async () => {
8333
+ const desiredArtifacts = filterArtifactsByAdapterTargets(
8334
+ desiredArtifactsFromGraphBundle(bundle),
8335
+ options.adapter,
8336
+ installationType,
8337
+ { warn }
8287
8338
  );
8288
- }
8289
- const manifest = priorState.manifest;
8290
- const basePlan = options.retireExactMcp ? await createExactMcpRetirementPlan(
8291
- desiredArtifacts,
8292
- options.adapter,
8293
- options.targetRoot,
8294
- manifest,
8295
- transport,
8296
- {
8297
- installationType: resolvedInstallationType,
8298
- stateKey,
8299
- workspaceOwner,
8300
- expectedFromWorkspaceOwner: options.expectedFromWorkspaceOwner,
8301
- graphLockDigest
8339
+ const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
8340
+ const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
8341
+ const graphLockDigest = digestGraphLock(bundle.graphLock);
8342
+ const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
8343
+ if (normalizeInstallRoot2(resolvedInstallRoot, transport.kind) !== normalizeInstallRoot2(installRoot, transport.kind)) {
8344
+ throw new Error(
8345
+ `Adapter ${options.adapter.name} resolved a different install root after rendering; target state identity cannot be proven.`
8346
+ );
8302
8347
  }
8303
- ) : await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
8304
- baseRevision: manifest?.revision ?? null,
8305
- graphLockDigest,
8306
- workspaceOwner,
8307
- installationType: resolvedInstallationType,
8308
- stateKey,
8309
- forceDrift: options.forceDrift,
8310
- forceConflict: options.forceConflict,
8311
- replaceConflict: options.replaceConflict,
8312
- stateMigration: priorState.migration,
8313
- warn
8314
- });
8315
- const plan = priorState.migration && !basePlan.stateMigration ? { ...basePlan, stateMigration: priorState.migration } : basePlan;
8316
- plan.targetStateFilePreconditions = {
8317
- graphLockPath,
8318
- graphLockRevision: stableLock ? digestGraphLock(stableLock) : null,
8319
- sourceLockRevision: computeSourceLockRevision(stableSourceLock)
8320
- };
8321
- let foreignStateObservations = [];
8322
- if (options.forceForeignState !== true && options.deferForeignStateCheck !== true) {
8323
- foreignStateObservations = await assertNoForeignWorkspaceState({
8324
- installRoot: resolvedInstallRoot,
8325
- adapter: options.adapter.name,
8348
+ const manifest = priorState.manifest;
8349
+ const basePlan = options.retireExactMcp ? await createExactMcpRetirementPlan(
8350
+ desiredArtifacts,
8351
+ options.adapter,
8352
+ options.targetRoot,
8353
+ manifest,
8326
8354
  transport,
8327
- workspaceRoot,
8355
+ {
8356
+ installationType: resolvedInstallationType,
8357
+ stateKey,
8358
+ workspaceOwner,
8359
+ expectedFromWorkspaceOwner: options.expectedFromWorkspaceOwner,
8360
+ graphLockDigest
8361
+ }
8362
+ ) : await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
8363
+ baseRevision: manifest?.revision ?? null,
8364
+ graphLockDigest,
8328
8365
  workspaceOwner,
8329
- globalRoot: options.globalRoot,
8366
+ installationType: resolvedInstallationType,
8330
8367
  stateKey,
8331
- migratingStateKey: priorState.migration?.fromStateKey,
8332
- plannedPaths: plan.operations.map((operation) => operation.relativeDestPath),
8333
- plannedOperations: plan.operations
8368
+ forceDrift: options.forceDrift,
8369
+ forceConflict: options.forceConflict,
8370
+ replaceConflict: options.replaceConflict,
8371
+ stateMigration: priorState.migration,
8372
+ warn
8334
8373
  });
8335
- plan.hasBlockingChanges = plan.operations.some((operation) => operation.action === "drift" || operation.action === "conflict");
8336
- }
8337
- return {
8338
- plan,
8339
- graph,
8340
- bundle,
8341
- desiredArtifacts,
8342
- graphLockPath,
8343
- graphLockDigest,
8344
- targetFingerprint,
8345
- warnings,
8346
- newTransitiveSources: trustEvaluation.promptSources,
8347
- graphDiff,
8348
- recoveredPendingApply,
8349
- previousManifest: priorState.manifest,
8350
- previousGraphLock: priorState.graphLock,
8351
- foreignStateObservations
8352
- };
8374
+ const plan = priorState.migration && !basePlan.stateMigration ? { ...basePlan, stateMigration: priorState.migration } : basePlan;
8375
+ plan.targetStateFilePreconditions = {
8376
+ graphLockPath,
8377
+ graphLockRevision: stableLock ? digestGraphLock(stableLock) : null,
8378
+ sourceLockRevision: computeSourceLockRevision(stableSourceLock)
8379
+ };
8380
+ let foreignStateObservations = [];
8381
+ if (options.forceForeignState !== true && options.deferForeignStateCheck !== true) {
8382
+ foreignStateObservations = await assertNoForeignWorkspaceState({
8383
+ installRoot: resolvedInstallRoot,
8384
+ adapter: options.adapter.name,
8385
+ transport,
8386
+ workspaceRoot,
8387
+ workspaceOwner,
8388
+ globalRoot: options.globalRoot,
8389
+ stateKey,
8390
+ migratingStateKey: priorState.migration?.fromStateKey,
8391
+ plannedPaths: plan.operations.map((operation) => operation.relativeDestPath),
8392
+ plannedOperations: plan.operations
8393
+ });
8394
+ plan.hasBlockingChanges = plan.operations.some((operation) => operation.action === "drift" || operation.action === "conflict");
8395
+ }
8396
+ return {
8397
+ plan,
8398
+ graph,
8399
+ bundle,
8400
+ desiredArtifacts,
8401
+ graphLockPath,
8402
+ graphLockDigest,
8403
+ targetFingerprint,
8404
+ warnings,
8405
+ newTransitiveSources: trustEvaluation.promptSources,
8406
+ graphDiff,
8407
+ recoveredPendingApply,
8408
+ previousManifest: priorState.manifest,
8409
+ previousGraphLock: priorState.graphLock,
8410
+ foreignStateObservations
8411
+ };
8412
+ });
8353
8413
  }
8354
8414
  async function assertNoForeignWorkspaceStateForPlan(plan, options) {
8355
8415
  if (!plan.stateKey) throw new Error(`Foreign-state validation requires a state key for ${plan.adapter}.`);
@@ -8708,7 +8768,7 @@ function stableJson2(value) {
8708
8768
  return `{${Object.keys(record).filter((key) => record[key] !== void 0).sort((left, right) => left.localeCompare(right)).map((key) => `${JSON.stringify(key)}:${stableJson2(record[key])}`).join(",")}}`;
8709
8769
  }
8710
8770
  function normalizeInstallRoot2(path, transportKind) {
8711
- return transportKind === "ssh" ? posix2.normalize(path) : resolve16(path);
8771
+ return transportKind === "ssh" ? posix3.normalize(path) : resolve16(path);
8712
8772
  }
8713
8773
  var workspaceOwnerPrefix2 = "workspace-root:";
8714
8774
  async function assertNoForeignWorkspaceState(check) {
@@ -9785,6 +9845,7 @@ function resolveCliVersion() {
9785
9845
 
9786
9846
  // src/lifecycle/ownership.ts
9787
9847
  import { resolve as resolve20 } from "path";
9848
+ var unknownWorkspaceOwner = "workspace:unknown";
9788
9849
  async function planArtifactOwnershipHandoff(request) {
9789
9850
  return validateOwnershipHandoff(request, request.transport ?? localTransport);
9790
9851
  }
@@ -9803,6 +9864,9 @@ async function applyArtifactOwnershipHandoff(request) {
9803
9864
  throw new Error("Cannot hand off ownership while an apply journal is pending. Recover or abort it first.");
9804
9865
  }
9805
9866
  const plan = await validateOwnershipHandoff(request, transport);
9867
+ if (plan.drifted && !request.expectedRuntimeHash) {
9868
+ throw new Error("Applying a drifted ownership handoff requires expectedRuntimeHash from a reviewed dry-run.");
9869
+ }
9806
9870
  const manifest = await readInstallManifest(request.targetRoot, request.adapter, transport, scope);
9807
9871
  if (!manifest || manifest.version !== 2) {
9808
9872
  throw new Error("Ownership handoff requires an Agentwheel v2 install manifest.");
@@ -9833,6 +9897,13 @@ async function applyArtifactOwnershipHandoff(request) {
9833
9897
  async function validateOwnershipHandoff(request, transport) {
9834
9898
  if (request.expectedHash) assertSha256(request.expectedHash, "expected artifact hash");
9835
9899
  if (request.expectedRevision) assertSha256(request.expectedRevision, "expected manifest revision");
9900
+ if (request.expectedRuntimeHash) {
9901
+ assertSha256(request.expectedRuntimeHash, "expected runtime hash");
9902
+ if (request.carryDrift !== true) throw new Error("An expected runtime hash is only valid with carryDrift.");
9903
+ }
9904
+ if (request.fromWorkspaceRoot === void 0 === (request.fromUnknownOwner !== true)) {
9905
+ throw new Error("Ownership handoff requires exactly one of fromWorkspaceRoot or fromUnknownOwner.");
9906
+ }
9836
9907
  const scope = { installationType: request.installationType, stateKey: request.stateKey };
9837
9908
  const manifest = await readInstallManifest(request.targetRoot, request.adapter, transport, scope);
9838
9909
  if (!manifest) throw new Error(`No install manifest for ${request.adapter} at ${request.targetRoot}`);
@@ -9850,7 +9921,7 @@ async function validateOwnershipHandoff(request, transport) {
9850
9921
  );
9851
9922
  }
9852
9923
  const entry = matches[0];
9853
- const fromOwner = workspaceOwnerForRoot(request.fromWorkspaceRoot);
9924
+ const fromOwner = request.fromUnknownOwner === true ? unknownWorkspaceOwner : workspaceOwnerForRoot(request.fromWorkspaceRoot);
9854
9925
  const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot, request.toFleetId);
9855
9926
  if (fromOwner === toOwner) throw new Error("Ownership handoff requires different workspace roots.");
9856
9927
  if (entry.workspaceOwner !== fromOwner) {
@@ -9861,9 +9932,14 @@ async function validateOwnershipHandoff(request, transport) {
9861
9932
  }
9862
9933
  const destPath = containedArtifactPath(request.targetRoot, entry.path);
9863
9934
  if (!await transport.pathExists(destPath)) throw new Error(`Managed artifact is missing: ${entry.path}`);
9864
- const currentHash = await verifiedEntryHash(entry, destPath, transport);
9865
- if (request.expectedHash && currentHash !== request.expectedHash) {
9866
- throw new Error(`Current hash precondition failed for ${entry.path}: expected ${request.expectedHash}, found ${currentHash}`);
9935
+ const runtimeHash = await verifiedEntryHash(entry, destPath, transport, request.carryDrift === true);
9936
+ const drifted = runtimeHash !== entry.hash;
9937
+ if (drifted) {
9938
+ if (request.expectedRuntimeHash && runtimeHash !== request.expectedRuntimeHash) {
9939
+ throw new Error(`Runtime hash precondition failed for ${entry.path}: expected ${request.expectedRuntimeHash}, found ${runtimeHash}`);
9940
+ }
9941
+ } else if (request.expectedRuntimeHash) {
9942
+ throw new Error(`Runtime hash precondition failed for ${entry.path}: the artifact is no longer drifted`);
9867
9943
  }
9868
9944
  return {
9869
9945
  adapter: request.adapter,
@@ -9872,13 +9948,15 @@ async function validateOwnershipHandoff(request, transport) {
9872
9948
  targetRoot: request.targetRoot,
9873
9949
  selector: `${request.artifactType}/${request.artifactName}`,
9874
9950
  path: entry.path,
9875
- artifactHash: currentHash,
9951
+ artifactHash: entry.hash,
9952
+ runtimeHash,
9953
+ drifted,
9876
9954
  manifestRevision: manifest.revision,
9877
9955
  fromOwner,
9878
9956
  toOwner
9879
9957
  };
9880
9958
  }
9881
- async function verifiedEntryHash(entry, destPath, transport) {
9959
+ async function verifiedEntryHash(entry, destPath, transport, carryFileDrift = false) {
9882
9960
  if (entry.semanticPlugin) throw new Error(`Ownership handoff cannot verify semantic plugin state at ${destPath}`);
9883
9961
  if (entry.mode === "managed-block") {
9884
9962
  const selector = managedInstructionSelector(entry.logicalSelector, entry.artifactType, entry.artifactName);
@@ -9896,7 +9974,7 @@ async function verifiedEntryHash(entry, destPath, transport) {
9896
9974
  return entry.hash;
9897
9975
  }
9898
9976
  const currentHash = await transport.hashPath(destPath);
9899
- if (currentHash !== entry.hash) {
9977
+ if (currentHash !== entry.hash && !carryFileDrift) {
9900
9978
  throw new Error(`Managed artifact is drifted at ${destPath}: manifest ${entry.hash}, current ${currentHash}`);
9901
9979
  }
9902
9980
  return currentHash;
@@ -10226,7 +10304,7 @@ import { isAbsolute as isAbsolute3, relative as relative9, resolve as resolve23
10226
10304
  // src/lifecycle/legacy-recovery-plan.ts
10227
10305
  import { createHash as createHash10 } from "crypto";
10228
10306
  import { lstat as lstat3, readdir as readdir10 } from "fs/promises";
10229
- import { basename as basename20, dirname as dirname21, join as join39, posix as posix3, relative as relative8, resolve as resolve22 } from "path";
10307
+ import { basename as basename20, dirname as dirname21, join as join39, posix as posix4, relative as relative8, resolve as resolve22 } from "path";
10230
10308
  async function inventoryHistoricalLocks(fleetRoot) {
10231
10309
  const root = join39(fleetRoot, ".agentwheel", "locks");
10232
10310
  const found = [];
@@ -10384,7 +10462,7 @@ function claimFor(item, entry, locks, binding) {
10384
10462
  const matches = locks.filter((candidate) => candidate.digest === digest2);
10385
10463
  const evidence = [];
10386
10464
  const owner = "workspaceOwner" in entry ? entry.workspaceOwner : legacyUnownedWorkspaceOwner;
10387
- const sameRoot = binding.transportKind === "ssh" ? posix3.normalize(item.manifest.targetRoot) === posix3.normalize(binding.installRoot) : resolve22(item.manifest.targetRoot) === resolve22(binding.installRoot);
10465
+ const sameRoot = binding.transportKind === "ssh" ? posix4.normalize(item.manifest.targetRoot) === posix4.normalize(binding.installRoot) : resolve22(item.manifest.targetRoot) === resolve22(binding.installRoot);
10388
10466
  const inTargetDir = matches.filter((candidate) => resolve22(dirname21(candidate.path)) === resolve22(binding.targetLockDir));
10389
10467
  const boundLocks = inTargetDir.filter((candidate) => lockBindsState(candidate, item, binding));
10390
10468
  if (owner !== binding.expectedOwner) evidence.push("workspace owner differs from selected Fleet target");
@@ -14355,10 +14433,13 @@ program.command("remember").description("append text to the local instructions o
14355
14433
  console.log(nextInstallNudge());
14356
14434
  });
14357
14435
  var ownershipCommand = program.command("ownership").description("inspect and transfer manifest ownership without rewriting runtime artifacts");
14358
- 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) => {
14436
+ ownershipCommand.command("handoff").description("transfer one managed artifact between Agentwheel workspace roots").argument("<selector>", "exact artifact selector in type/name form").option("--from-workspace-root <path>", "current owning workspace root").option("--from-unknown-owner", "hand off an artifact recorded as workspace:unknown", false).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 recorded artifact hash; required when applying").option("--expected-revision <sha256>", "expected install manifest revision; required when applying").option("--carry-drift", "accept a file or directory whose runtime differs from the recorded hash; the recorded hash is kept", false).option("--expected-runtime-hash <sha256>", "expected drifted runtime hash; required when applying with --carry-drift").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) => {
14359
14437
  if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
14360
14438
  throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
14361
14439
  }
14440
+ if (Boolean(options.fromWorkspaceRoot) === (options.fromUnknownOwner === true)) {
14441
+ throw new Error("Ownership handoff requires exactly one of --from-workspace-root or --from-unknown-owner.");
14442
+ }
14362
14443
  const normalizedOptions = normalizeRuntimeScopeOptions(options);
14363
14444
  const targets = await resolveCliTargets(normalizedOptions);
14364
14445
  if (targets.length !== 1) {
@@ -14390,11 +14471,13 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
14390
14471
  adapter: adapter.name,
14391
14472
  artifactType,
14392
14473
  artifactName,
14393
- fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot),
14474
+ ...options.fromUnknownOwner === true ? { fromUnknownOwner: true } : { fromWorkspaceRoot: normalizeCliPath(options.fromWorkspaceRoot) },
14394
14475
  toWorkspaceRoot,
14395
14476
  toFleetId: options.toFleet,
14396
14477
  expectedHash: options.expectedHash,
14397
14478
  expectedRevision: options.expectedRevision,
14479
+ carryDrift: options.carryDrift === true,
14480
+ expectedRuntimeHash: options.expectedRuntimeHash,
14398
14481
  transport: transportForTarget(target)
14399
14482
  };
14400
14483
  const result = options.dryRun ? await planArtifactOwnershipHandoff(request) : await applyArtifactOwnershipHandoff(request);
@@ -14402,6 +14485,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
14402
14485
  console.log(`Target: ${result.adapter}/${result.installationType ?? "default"} at ${result.targetRoot}`);
14403
14486
  console.log(`Path: ${result.path}`);
14404
14487
  console.log(`Hash: ${result.artifactHash}`);
14488
+ if (result.drifted) console.log(`Runtime hash: ${result.runtimeHash} (drift carried; the recorded hash is kept)`);
14405
14489
  console.log(`Manifest revision: ${result.manifestRevision}`);
14406
14490
  console.log(`Owner: ${result.fromOwner} -> ${result.toOwner}`);
14407
14491
  });
@@ -14911,30 +14995,34 @@ async function runInstallCommand(nameOrSource, options, behavior) {
14911
14995
  throw teachingInstallError(nameOrSource, error);
14912
14996
  }
14913
14997
  }
14914
- for (const result of await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage, reportFormat: outputFormat }, { mode: "install" })) {
14915
- if (!behavior.quiet) console.log(formatGraphPlan(result));
14916
- if (behavior.apply) {
14917
- declareMutationPath(result.graphLockPath);
14918
- if (extraPackage && !targetOptions.onlySource) declareMutationPath(workspaceConfigPath(target.workspaceRoot));
14919
- const transport = transportForTarget(target);
14920
- const executePlugins = target.executePlugins ?? targetOptions.executePlugins;
14921
- await applyCombinedInstallPlan(result.plan, {
14922
- executePlugins,
14923
- transport,
14924
- graphLockDigest: result.graphLockDigest,
14925
- graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
14926
- });
14927
- const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
14928
- enabled: target.reloadRuntimes ?? shouldReloadRuntimes(targetOptions),
14929
- executePlugins
14930
- });
14931
- if (!behavior.quiet) {
14932
- console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
14933
- if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
14998
+ const results = await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage, reportFormat: outputFormat }, { mode: "install" });
14999
+ try {
15000
+ for (const result of results) {
15001
+ if (!behavior.quiet) console.log(formatGraphPlan(result));
15002
+ if (behavior.apply) {
15003
+ declareMutationPath(result.graphLockPath);
15004
+ if (extraPackage && !targetOptions.onlySource) declareMutationPath(workspaceConfigPath(target.workspaceRoot));
15005
+ const transport = transportForTarget(target);
15006
+ const executePlugins = target.executePlugins ?? targetOptions.executePlugins;
15007
+ await applyCombinedInstallPlan(result.plan, {
15008
+ executePlugins,
15009
+ transport,
15010
+ graphLockDigest: result.graphLockDigest,
15011
+ graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
15012
+ });
15013
+ const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
15014
+ enabled: target.reloadRuntimes ?? shouldReloadRuntimes(targetOptions),
15015
+ executePlugins
15016
+ });
15017
+ if (!behavior.quiet) {
15018
+ console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
15019
+ if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
15020
+ }
14934
15021
  }
15022
+ if (result.plan.hasBlockingChanges) process.exitCode = 1;
14935
15023
  }
14936
- await rm10(result.bundle.root, { recursive: true, force: true });
14937
- if (result.plan.hasBlockingChanges) process.exitCode = 1;
15024
+ } finally {
15025
+ await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
14938
15026
  }
14939
15027
  if (behavior.apply && extraPackage && !targetOptions.onlySource) {
14940
15028
  await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), extraPackage));
@@ -15385,31 +15473,43 @@ async function runSkillUpdateCommand(skillName, options) {
15385
15473
  }
15386
15474
  async function runConfiguredGraphPackages(target, options, behavior) {
15387
15475
  const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
15388
- for (const result of results) {
15389
- console.log(`${behavior.mode === "update" ? "Update" : "Install"} ${result.plan.adapter} at ${result.plan.targetRoot}:`);
15390
- console.log(formatGraphPlan(result));
15391
- if (!options.dryRun) {
15392
- declareMutationPath(result.graphLockPath);
15393
- const transport = transportForTarget(target);
15394
- const executePlugins = target.executePlugins ?? options.executePlugins;
15395
- await applyCombinedInstallPlan(result.plan, {
15396
- executePlugins,
15397
- transport,
15398
- graphLockDigest: result.graphLockDigest,
15399
- graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
15400
- });
15401
- const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
15402
- enabled: target.reloadRuntimes ?? shouldReloadRuntimes(options),
15403
- executePlugins
15404
- });
15405
- console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
15406
- if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
15476
+ try {
15477
+ for (const result of results) {
15478
+ console.log(`${behavior.mode === "update" ? "Update" : "Install"} ${result.plan.adapter} at ${result.plan.targetRoot}:`);
15479
+ console.log(formatGraphPlan(result));
15480
+ if (!options.dryRun) {
15481
+ declareMutationPath(result.graphLockPath);
15482
+ const transport = transportForTarget(target);
15483
+ const executePlugins = target.executePlugins ?? options.executePlugins;
15484
+ await applyCombinedInstallPlan(result.plan, {
15485
+ executePlugins,
15486
+ transport,
15487
+ graphLockDigest: result.graphLockDigest,
15488
+ graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
15489
+ });
15490
+ const reloaded = await reloadRuntimeAfterPluginChanges(result.plan, target, transport, {
15491
+ enabled: target.reloadRuntimes ?? shouldReloadRuntimes(options),
15492
+ executePlugins
15493
+ });
15494
+ console.log(`Applied ${result.plan.adapter} at ${result.plan.targetRoot}.`);
15495
+ if (reloaded) console.log(`Reloaded runtime via ${formatReloadCommands(target.reloadCommands)}.`);
15496
+ }
15497
+ if (result.plan.hasBlockingChanges) process.exitCode = 1;
15407
15498
  }
15408
- await rm10(result.bundle.root, { recursive: true, force: true });
15409
- if (result.plan.hasBlockingChanges) process.exitCode = 1;
15499
+ } finally {
15500
+ await Promise.all(results.map((result) => rm10(result.bundle.root, { recursive: true, force: true })));
15410
15501
  }
15411
15502
  }
15412
15503
  async function buildGraphPlansForTarget(target, source, options, behavior) {
15504
+ const bundleRoots = [];
15505
+ try {
15506
+ return await collectGraphPlansForTarget(target, source, options, behavior, bundleRoots);
15507
+ } catch (error) {
15508
+ await Promise.all(bundleRoots.map((root) => rm10(root, { recursive: true, force: true })));
15509
+ throw error;
15510
+ }
15511
+ }
15512
+ async function collectGraphPlansForTarget(target, source, options, behavior, bundleRoots) {
15413
15513
  const targetOptions = optionsForResolvedTarget(options, target);
15414
15514
  const config = await readMergedWorkspaceConfig(target.workspaceRoot);
15415
15515
  const groups = /* @__PURE__ */ new Map();
@@ -15603,6 +15703,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
15603
15703
  expectedFromWorkspaceOwner: targetOptions.expectedFromWorkspaceOwner,
15604
15704
  recoverLegacyState: targetOptions.recoverLegacyState
15605
15705
  });
15706
+ bundleRoots.push(result.bundle.root);
15606
15707
  if ((behavior.mode === "install" || behavior.mode === "update") && scopedRootId) {
15607
15708
  const manifest = result.previousManifest;
15608
15709
  const previousLock = result.previousGraphLock;
@@ -16239,7 +16340,7 @@ async function uninstallConfiguredPackage(target, packageName, options) {
16239
16340
  let renderedRoot;
16240
16341
  if (remainingGroup && remainingGroup.packages.length > 0) {
16241
16342
  const remainingAdapter = await resolveAdapterForTarget(remainingGroup.target, remainingGroup.adapterOptions);
16242
- const result2 = await createGraphSourcePlan({
16343
+ const result = await createGraphSourcePlan({
16243
16344
  roots: remainingGroup.packages.map((pkg2) => ({
16244
16345
  rootId: pkg2.name,
16245
16346
  source: pkg2.source,
@@ -16271,46 +16372,49 @@ async function uninstallConfiguredPackage(target, packageName, options) {
16271
16372
  readOnly: options.dryRun === true,
16272
16373
  isTTY: process.stdin.isTTY === true
16273
16374
  });
16274
- remainingGraphPlan = result2;
16275
- remainingDesired = desiredArtifactsFromGraphBundle(result2.bundle);
16276
- renderedRoot = result2.bundle.root;
16375
+ remainingGraphPlan = result;
16376
+ remainingDesired = desiredArtifactsFromGraphBundle(result.bundle);
16377
+ renderedRoot = result.bundle.root;
16277
16378
  }
16278
- const uninstallPlan = await createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport, {
16279
- graphLockDigest: remainingGraphPlan?.graphLockDigest
16280
- });
16281
- const transitionPlan = remainingGraphPlan?.plan;
16282
- const plan = {
16283
- ...uninstallPlan,
16284
- stateKey: transitionPlan?.stateKey ?? removedState.stableStateKey,
16285
- stateMigration: transitionPlan?.stateMigration ?? removedState.migration,
16286
- targetStateFilePreconditions: transitionPlan?.targetStateFilePreconditions ?? removedState.targetStateFilePreconditions
16287
- };
16288
- console.log(`Uninstall ${pkg.name} (${adapter.name} at ${removedTarget.targetRoot}):`);
16289
- console.log(formatPlan(plan));
16290
- const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
16291
- removeGraphLockPath: removedState.graphLockPath ?? removedState.stableGraphLockPath
16292
- };
16293
- if (!options.dryRun) {
16294
- declareMutationPath(workspaceConfigPath(target.workspaceRoot));
16295
- const declarativeGraphPath = remainingGraphPlan ? remainingGraphPlan.graphLockPath : removedState.graphLockPath ?? removedState.stableGraphLockPath;
16296
- declareMutationPath(declarativeGraphPath);
16297
- }
16298
- const result = await uninstall(plan, {
16299
- dryRun: options.dryRun,
16300
- force: options.force,
16301
- keepFiles: options.keepFiles,
16302
- transport,
16303
- ...graphLockFinalState,
16304
- workspaceConfig: {
16305
- path: workspaceConfigPath(target.workspaceRoot),
16306
- data: { ...config, packages: remaining }
16379
+ try {
16380
+ const uninstallPlan = await createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport, {
16381
+ graphLockDigest: remainingGraphPlan?.graphLockDigest
16382
+ });
16383
+ const transitionPlan = remainingGraphPlan?.plan;
16384
+ const plan = {
16385
+ ...uninstallPlan,
16386
+ stateKey: transitionPlan?.stateKey ?? removedState.stableStateKey,
16387
+ stateMigration: transitionPlan?.stateMigration ?? removedState.migration,
16388
+ targetStateFilePreconditions: transitionPlan?.targetStateFilePreconditions ?? removedState.targetStateFilePreconditions
16389
+ };
16390
+ console.log(`Uninstall ${pkg.name} (${adapter.name} at ${removedTarget.targetRoot}):`);
16391
+ console.log(formatPlan(plan));
16392
+ const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
16393
+ removeGraphLockPath: removedState.graphLockPath ?? removedState.stableGraphLockPath
16394
+ };
16395
+ if (!options.dryRun) {
16396
+ declareMutationPath(workspaceConfigPath(target.workspaceRoot));
16397
+ const declarativeGraphPath = remainingGraphPlan ? remainingGraphPlan.graphLockPath : removedState.graphLockPath ?? removedState.stableGraphLockPath;
16398
+ declareMutationPath(declarativeGraphPath);
16307
16399
  }
16308
- });
16309
- if (!options.dryRun) {
16310
- console.log(formatUninstallResult(result));
16400
+ const result = await uninstall(plan, {
16401
+ dryRun: options.dryRun,
16402
+ force: options.force,
16403
+ keepFiles: options.keepFiles,
16404
+ transport,
16405
+ ...graphLockFinalState,
16406
+ workspaceConfig: {
16407
+ path: workspaceConfigPath(target.workspaceRoot),
16408
+ data: { ...config, packages: remaining }
16409
+ }
16410
+ });
16411
+ if (!options.dryRun) {
16412
+ console.log(formatUninstallResult(result));
16413
+ }
16414
+ if (plan.hasBlockingChanges) process.exitCode = 1;
16415
+ } finally {
16416
+ if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
16311
16417
  }
16312
- if (renderedRoot) await rm10(renderedRoot, { recursive: true, force: true });
16313
- if (plan.hasBlockingChanges) process.exitCode = 1;
16314
16418
  }
16315
16419
  }
16316
16420
  function graphGroupForPackage(groups, target, pkg, options) {
@@ -16367,7 +16471,7 @@ function targetFingerprintParts(target, adapter, options, installationType) {
16367
16471
  };
16368
16472
  }
16369
16473
  async function withFreshReadOnlyGraphPlan(target, adapter, adapterOptions, installationType, packages, use) {
16370
- const stage = await mkdtemp4(join46(tmpdir5(), "agentwheel-recovery-"));
16474
+ const stage = await mkdtemp3(join46(tmpdir4(), "agentwheel-recovery-"));
16371
16475
  const priorTmpdir = process.env.TMPDIR;
16372
16476
  try {
16373
16477
  process.env.TMPDIR = stage;
@@ -18,8 +18,8 @@ import {
18
18
  removeApplyJournal,
19
19
  rollbackCompletedOperations,
20
20
  writeApplyJournal
21
- } from "./chunk-FP26KSBM.js";
22
- import "./chunk-GE7F76AC.js";
21
+ } from "./chunk-BHT57MK5.js";
22
+ import "./chunk-BNIF3FUR.js";
23
23
  export {
24
24
  abortApplyJournal,
25
25
  acquireApplyLock,
package/openpack.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "name": "NestDevLab/agentwheel",
4
- "version": "0.20.18",
4
+ "version": "0.20.19",
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.18",
3
+ "version": "0.20.19",
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.18"
8
+ version: "0.20.19"
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.18"
8
+ version: "0.20.19"
9
9
  ---
10
10
 
11
11
  # Agentwheel Discovery