agentwheel 0.22.0 → 0.22.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/README.md +36 -0
- package/dist/index.js +198 -59
- package/openpack.json +1 -1
- package/package.json +2 -1
- package/skills/agentwheel/SKILL.md +1 -1
- package/skills/agentwheel-discovery/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -139,6 +139,25 @@ refused unless `--carry-drift` is passed: the dry-run prints the runtime hash, t
|
|
|
139
139
|
`--expected-runtime-hash <sha256>`, and the manifest keeps the recorded hash so the next install
|
|
140
140
|
still reports the drift. Merge and managed-block entries never carry drift.
|
|
141
141
|
|
|
142
|
+
Qualify a Fleet-owned source with `--from-fleet <id>` together with its exact
|
|
143
|
+
`--from-workspace-root`; use `--to-fleet <id>` for a Fleet-owned destination. Both IDs must
|
|
144
|
+
be registered at the specified roots. These options qualify ownership, whereas `--fleet`
|
|
145
|
+
selects the governing workspace. `--from-fleet` cannot be combined with
|
|
146
|
+
`--from-unknown-owner`. For example, preview returning one entry to its original plain owner:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
agentwheel ownership handoff skills/example-skill --local --agent local-codex --installation-type user \
|
|
150
|
+
--source-state-key codex.user.existing-state \
|
|
151
|
+
--from-workspace-root /fleet/control --from-fleet example-fleet \
|
|
152
|
+
--to-workspace-root /original/workspace --dry-run
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Reverse handoffs require a fresh reviewed plan against the post-handoff state; they do not
|
|
156
|
+
reuse old manifest revisions or erase audit history. For run-as targets, also pass the reviewed
|
|
157
|
+
`--plan-digest` and `--expected-inventory-revision` when applying. The qualified old/new owner
|
|
158
|
+
identities are included in the native plan, its digest and the audit. Each handoff changes the
|
|
159
|
+
inventory revision, so review subsequent operations against their exact intermediate states.
|
|
160
|
+
|
|
142
161
|
Use `--source-state-key <exact-key>` when an existing source manifest must be selected explicitly.
|
|
143
162
|
This is a command-local equivalent of a named agent's `stateKey`; it does not edit agent configuration,
|
|
144
163
|
move state files, or reconstruct graph locks. The key must be canonical and match the selected adapter
|
|
@@ -527,6 +546,23 @@ agentwheel ownership retire-stale \
|
|
|
527
546
|
--fleet <fleet-id> --agent <agent> --json
|
|
528
547
|
```
|
|
529
548
|
|
|
549
|
+
To use a target configured in a nested workspace while retiring ownership into its containing
|
|
550
|
+
registered Fleet, run from that workspace and select the destination owner separately:
|
|
551
|
+
|
|
552
|
+
```bash
|
|
553
|
+
agentwheel ownership retire-stale --local --agent service-agent \
|
|
554
|
+
--to-fleet <fleet-id> --to-workspace-root <fleet-root> \
|
|
555
|
+
--from-workspace-root <stale-owner-root> \
|
|
556
|
+
--source-state-key <stale-state-key> \
|
|
557
|
+
--destination-state-key <fleet-owned-state-key> --json
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
`--to-fleet` qualifies the destination owner; `--local`, `--user`, or `--fleet` selects the target
|
|
561
|
+
workspace and its configured transport. The selected workspace must be within the registered
|
|
562
|
+
Fleet root, including after symlink resolution, and cannot identify a different Fleet.
|
|
563
|
+
The original `--fleet <id>` command remains supported for targets configured directly at that
|
|
564
|
+
Fleet root. Neither form changes agent configuration or relaxes contribution and revision guards.
|
|
565
|
+
|
|
530
566
|
The dry-run still proves exact destination coverage and runtime bytes. Apply only its emitted command,
|
|
531
567
|
which includes the plan, source, destination, and manifest-inventory revisions.
|
|
532
568
|
For duplicate state owned by the same registered Fleet, set both workspace roots to the Fleet root
|
package/dist/index.js
CHANGED
|
@@ -797,11 +797,11 @@ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
|
|
|
797
797
|
onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
|
|
798
798
|
ensurePropertyComplete(offset + length);
|
|
799
799
|
},
|
|
800
|
-
onSeparator: (
|
|
800
|
+
onSeparator: (sep13, offset, length) => {
|
|
801
801
|
if (currentParent.type === "property") {
|
|
802
|
-
if (
|
|
802
|
+
if (sep13 === ":") {
|
|
803
803
|
currentParent.colonOffset = offset;
|
|
804
|
-
} else if (
|
|
804
|
+
} else if (sep13 === ",") {
|
|
805
805
|
ensurePropertyComplete(offset);
|
|
806
806
|
}
|
|
807
807
|
}
|
|
@@ -14730,14 +14730,14 @@ var init_artifact = __esm({
|
|
|
14730
14730
|
include: external_exports.string().min(1),
|
|
14731
14731
|
markers: external_exports.boolean().optional(),
|
|
14732
14732
|
optional: external_exports.boolean().optional()
|
|
14733
|
-
});
|
|
14733
|
+
}).strict();
|
|
14734
14734
|
packageCompositionRuleSchema = external_exports.object({
|
|
14735
14735
|
target: external_exports.string().min(1),
|
|
14736
14736
|
include: external_exports.string().min(1),
|
|
14737
14737
|
exclude: external_exports.array(external_exports.string().min(1)).optional(),
|
|
14738
14738
|
runtimes: external_exports.array(external_exports.string().min(1)).optional(),
|
|
14739
14739
|
markers: external_exports.boolean().optional()
|
|
14740
|
-
});
|
|
14740
|
+
}).strict();
|
|
14741
14741
|
packageSupersedesEntrySchema = external_exports.object({
|
|
14742
14742
|
package: external_exports.string().min(1),
|
|
14743
14743
|
selector: external_exports.string().min(1),
|
|
@@ -20415,6 +20415,7 @@ var init_transaction = __esm({
|
|
|
20415
20415
|
|
|
20416
20416
|
// src/install/toml-merge.ts
|
|
20417
20417
|
import { mkdir as mkdir8, readFile as readFile16, writeFile as writeFile8 } from "fs/promises";
|
|
20418
|
+
import { parse as parseToml } from "smol-toml";
|
|
20418
20419
|
import { dirname as dirname13 } from "path";
|
|
20419
20420
|
async function mergeCodexTomlMcp(sourcePath, destPath) {
|
|
20420
20421
|
const source = JSON.parse(await readFile16(sourcePath, "utf8"));
|
|
@@ -20429,6 +20430,39 @@ function mismatchedCodexTomlMcpServers(source, currentContent) {
|
|
|
20429
20430
|
const servers = extractMcpServers(source);
|
|
20430
20431
|
return Object.entries(servers).filter(([name, server]) => extractMcpServerBlock(currentContent, name) !== formatMcpServer(name, server)).map(([name]) => name);
|
|
20431
20432
|
}
|
|
20433
|
+
function mismatchedManagedCodexTomlMcpServers(source, currentContent) {
|
|
20434
|
+
const servers = extractMcpServers(source);
|
|
20435
|
+
let current;
|
|
20436
|
+
try {
|
|
20437
|
+
const parsed = parseToml(currentContent, { integersAsBigInt: "asNeeded" });
|
|
20438
|
+
current = isPlainRecord(parsed.mcp_servers) ? parsed.mcp_servers : void 0;
|
|
20439
|
+
} catch {
|
|
20440
|
+
return Object.keys(servers);
|
|
20441
|
+
}
|
|
20442
|
+
return Object.entries(servers).filter(([name, server]) => {
|
|
20443
|
+
try {
|
|
20444
|
+
const rendered = parseToml(formatMcpServer(name, server), { integersAsBigInt: "asNeeded" });
|
|
20445
|
+
const expected = isPlainRecord(rendered.mcp_servers) ? rendered.mcp_servers[name] : void 0;
|
|
20446
|
+
return !current || !Object.hasOwn(current, name) || !managedValueMatches(current[name], expected);
|
|
20447
|
+
} catch {
|
|
20448
|
+
return true;
|
|
20449
|
+
}
|
|
20450
|
+
}).map(([name]) => name);
|
|
20451
|
+
}
|
|
20452
|
+
function managedValueMatches(actual, expected) {
|
|
20453
|
+
if (Array.isArray(expected)) {
|
|
20454
|
+
return Array.isArray(actual) && actual.length === expected.length && expected.every((value, index) => managedValueMatches(actual[index], value));
|
|
20455
|
+
}
|
|
20456
|
+
if (isPlainRecord(expected)) {
|
|
20457
|
+
return isPlainRecord(actual) && Object.entries(expected).every(([key, value]) => Object.hasOwn(actual, key) && managedValueMatches(actual[key], value));
|
|
20458
|
+
}
|
|
20459
|
+
return actual === expected;
|
|
20460
|
+
}
|
|
20461
|
+
function isPlainRecord(value) {
|
|
20462
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
20463
|
+
const prototype = Object.getPrototypeOf(value);
|
|
20464
|
+
return prototype === null || prototype === Object.prototype;
|
|
20465
|
+
}
|
|
20432
20466
|
function extractMcpServers(source) {
|
|
20433
20467
|
const raw = isRecord2(source.mcpServers) ? source.mcpServers : source;
|
|
20434
20468
|
const servers = {};
|
|
@@ -20617,8 +20651,18 @@ function assertExactMergeContribution(contribution, strategy, currentContent) {
|
|
|
20617
20651
|
throw new MergeAdoptionMismatchError(`exact merge contribution differs or is missing at ${mismatch}`);
|
|
20618
20652
|
}
|
|
20619
20653
|
}
|
|
20654
|
+
function assertManagedMergeContribution(contribution, strategy, currentContent) {
|
|
20655
|
+
if (strategy !== "codex-toml-mcp") {
|
|
20656
|
+
assertExactMergeContribution(contribution, strategy, currentContent);
|
|
20657
|
+
return;
|
|
20658
|
+
}
|
|
20659
|
+
const mismatched = mismatchedManagedCodexTomlMcpServers(contribution, currentContent);
|
|
20660
|
+
if (mismatched.length > 0) {
|
|
20661
|
+
throw new MergeAdoptionMismatchError(`managed MCP fields differ or are missing for ${mismatched.join(", ")}`);
|
|
20662
|
+
}
|
|
20663
|
+
}
|
|
20620
20664
|
async function assertMergedSourceContribution(sourcePath, strategy, currentContent) {
|
|
20621
|
-
|
|
20665
|
+
assertManagedMergeContribution(await readMergeSource(sourcePath, strategy), strategy, currentContent);
|
|
20622
20666
|
}
|
|
20623
20667
|
function assertExactMcpMergeContribution(removal, strategy, currentContent) {
|
|
20624
20668
|
if (strategy === "codex-toml-mcp") {
|
|
@@ -21813,7 +21857,9 @@ async function applyOperation(operation, context) {
|
|
|
21813
21857
|
if (!await transport.pathExists(operation.destPath)) {
|
|
21814
21858
|
throw new Error(`Merge skip destination disappeared after planning: ${operation.relativeDestPath}`);
|
|
21815
21859
|
}
|
|
21816
|
-
|
|
21860
|
+
const exactAdoption = operation.exactMergeAdoption === true || operation.reason === "force adopting exact unmanaged merge contribution" || operation.reason === "repairing exact incomplete merge ownership";
|
|
21861
|
+
const verify = exactAdoption ? assertExactMergeContribution : assertManagedMergeContribution;
|
|
21862
|
+
verify(operation.mergeRemoval, operation.mergeStrategy, await transport.readFile(operation.destPath));
|
|
21817
21863
|
verifiedCurrentHash = await transport.hashPath(operation.destPath);
|
|
21818
21864
|
} else if (!operation.semanticPlugin && !operation.programmaticOperation) {
|
|
21819
21865
|
if (!await transport.pathExists(operation.destPath)) {
|
|
@@ -22448,7 +22494,7 @@ import { createHash as createHash32 } from "crypto";
|
|
|
22448
22494
|
import { existsSync } from "fs";
|
|
22449
22495
|
import { mkdir as mkdir28, mkdtemp as mkdtemp6, readFile as readFile45, realpath as realpath4, rm as rm16, writeFile as writeFile24 } from "fs/promises";
|
|
22450
22496
|
import { homedir as homedir18, tmpdir as tmpdir7 } from "os";
|
|
22451
|
-
import { basename as basename24, dirname as dirname41, isAbsolute as isAbsolute17, join as join66, relative as relative22, resolve as resolve48 } from "path";
|
|
22497
|
+
import { basename as basename24, dirname as dirname41, isAbsolute as isAbsolute17, join as join66, relative as relative22, resolve as resolve48, sep as sep12 } from "path";
|
|
22452
22498
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
22453
22499
|
import { Command } from "commander";
|
|
22454
22500
|
|
|
@@ -23690,6 +23736,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
23690
23736
|
mergeCreatedDestination: existing2?.mergeCreatedDestination,
|
|
23691
23737
|
currentHash: currentHash2,
|
|
23692
23738
|
manifestHash: existing2?.hash,
|
|
23739
|
+
exactMergeAdoption: true,
|
|
23693
23740
|
reason: existing2 ? "repairing exact incomplete merge ownership" : "force adopting exact unmanaged merge contribution"
|
|
23694
23741
|
});
|
|
23695
23742
|
continue;
|
|
@@ -27082,7 +27129,8 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
27082
27129
|
entry,
|
|
27083
27130
|
packageRoot,
|
|
27084
27131
|
artifactPaths,
|
|
27085
|
-
nodeId: options.nodeId
|
|
27132
|
+
nodeId: options.nodeId,
|
|
27133
|
+
packageName: options.packageName
|
|
27086
27134
|
}));
|
|
27087
27135
|
const externalEntries = isPrimaryMarkdownFile(artifact, file2) ? options.additionalComposeEntries?.(artifact) ?? [] : [];
|
|
27088
27136
|
const result = await expandFile(file2, packageRoot, [...localEntries, ...externalEntries], artifactPaths, options);
|
|
@@ -27114,12 +27162,13 @@ async function expandFile(file2, packageRoot, appendEntries, artifactPaths, opti
|
|
|
27114
27162
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
27115
27163
|
let content = expanded.content;
|
|
27116
27164
|
const composedFrom = [...expanded.composedFrom];
|
|
27117
|
-
const appliedComposeIdentities = /* @__PURE__ */ new
|
|
27165
|
+
const appliedComposeIdentities = /* @__PURE__ */ new Map();
|
|
27118
27166
|
for (const external of appendEntries) {
|
|
27119
27167
|
const { entry } = external;
|
|
27120
27168
|
const included = await expandInclude(entry.include, external.packageRoot, external.artifactPaths, {
|
|
27121
27169
|
...options,
|
|
27122
27170
|
nodeId: external.nodeId,
|
|
27171
|
+
packageName: external.packageName,
|
|
27123
27172
|
optional: entry.optional === true,
|
|
27124
27173
|
markers: entry.markers !== false,
|
|
27125
27174
|
chain: [owner]
|
|
@@ -27127,10 +27176,15 @@ async function expandFile(file2, packageRoot, appendEntries, artifactPaths, opti
|
|
|
27127
27176
|
if (!included) continue;
|
|
27128
27177
|
const markerMode = entry.markers === false ? "plain" : "markers";
|
|
27129
27178
|
const identity = `${markerMode}\0${included.identity}`;
|
|
27130
|
-
|
|
27131
|
-
|
|
27179
|
+
const previous = appliedComposeIdentities.get(identity);
|
|
27180
|
+
if (external.deduplicate && previous) {
|
|
27181
|
+
if (previous.hash !== included.hash) {
|
|
27182
|
+
throw new Error(`Conflicting OpenPack composition revisions for ${included.selector}: ${previous.selector} (${previous.hash}) and ${included.selector} (${included.hash}). Select one revision explicitly.`);
|
|
27183
|
+
}
|
|
27184
|
+
composedFrom.push(...included.composedFrom);
|
|
27185
|
+
continue;
|
|
27132
27186
|
}
|
|
27133
|
-
appliedComposeIdentities.
|
|
27187
|
+
appliedComposeIdentities.set(identity, { hash: included.hash, selector: included.selector });
|
|
27134
27188
|
content = `${content.trimEnd()}
|
|
27135
27189
|
|
|
27136
27190
|
${included.rendered}
|
|
@@ -27150,6 +27204,7 @@ async function expandContent(content, packageRoot, chain, artifactPaths, options
|
|
|
27150
27204
|
}
|
|
27151
27205
|
const composedFrom = [];
|
|
27152
27206
|
const replacements = [];
|
|
27207
|
+
const semanticReplacements = [];
|
|
27153
27208
|
for (const match of content.matchAll(includePattern)) {
|
|
27154
27209
|
const selector = cleanSelector(match[2] ?? "");
|
|
27155
27210
|
const included = await expandInclude(selector, packageRoot, artifactPaths, {
|
|
@@ -27163,13 +27218,14 @@ async function expandContent(content, packageRoot, chain, artifactPaths, options
|
|
|
27163
27218
|
end: (match.index ?? 0) + match[0].length,
|
|
27164
27219
|
value: included?.rendered ?? ""
|
|
27165
27220
|
});
|
|
27221
|
+
semanticReplacements.push({ start: match.index ?? 0, end: (match.index ?? 0) + match[0].length, value: included?.semanticContent ?? "" });
|
|
27166
27222
|
if (included) composedFrom.push(...included.composedFrom);
|
|
27167
27223
|
}
|
|
27168
27224
|
let expanded = applyReplacements(content, replacements);
|
|
27169
27225
|
expanded = expanded.replace(escapedIncludePattern, (_match, optional2, selector) => {
|
|
27170
27226
|
return `<!-- openpack:include${optional2 ?? ""} ${cleanSelector(selector)} -->`;
|
|
27171
27227
|
});
|
|
27172
|
-
return { content: expanded, composedFrom: uniqueComposedFrom(composedFrom) };
|
|
27228
|
+
return { content: expanded, semanticContent: applyReplacements(content, semanticReplacements), composedFrom: uniqueComposedFrom(composedFrom) };
|
|
27173
27229
|
}
|
|
27174
27230
|
async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
27175
27231
|
const parsed = parseOpenPackIncludeSelector(selector);
|
|
@@ -27178,6 +27234,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
27178
27234
|
let includePackageRoot = packageRoot;
|
|
27179
27235
|
let includeArtifactPaths = artifactPaths;
|
|
27180
27236
|
let includeNodeId = options.nodeId;
|
|
27237
|
+
let includePackageName = options.packageName;
|
|
27181
27238
|
let displaySelector = displaySelectorForInclude(parsed.selector, options.nodeId, options.originNodeId);
|
|
27182
27239
|
if (parsed.alias) {
|
|
27183
27240
|
if (!options.nodeId || !options.resolveCrossPackageInclude) {
|
|
@@ -27196,6 +27253,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
27196
27253
|
includePackageRoot = resolved.packageRoot;
|
|
27197
27254
|
includeArtifactPaths = resolved.artifactPaths;
|
|
27198
27255
|
includeNodeId = resolved.toNodeId;
|
|
27256
|
+
includePackageName = resolved.packageName;
|
|
27199
27257
|
displaySelector = `${resolved.toNodeId}:${parsed.selector}`;
|
|
27200
27258
|
} else {
|
|
27201
27259
|
sourcePath = artifactPaths.get(parsed.selector) ?? resolvePackageSelector(packageRoot, parsed.selector);
|
|
@@ -27215,13 +27273,14 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
27215
27273
|
const { optional: _optional2, markers: _markers, chain: _chain, ...childOptions } = options;
|
|
27216
27274
|
const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
|
|
27217
27275
|
...childOptions,
|
|
27218
|
-
nodeId: includeNodeId
|
|
27276
|
+
nodeId: includeNodeId,
|
|
27277
|
+
packageName: includePackageName
|
|
27219
27278
|
});
|
|
27220
27279
|
const contentHash = sha2563(expanded.content);
|
|
27221
|
-
const identity = `${includeNodeId ?? resolve28(includePackageRoot)}\0${parsed.selector}
|
|
27280
|
+
const identity = `${includePackageName ?? includeNodeId ?? resolve28(includePackageRoot)}\0${parsed.selector}`;
|
|
27222
27281
|
const ownEntry = { selector: displaySelector, hash: contentHash };
|
|
27223
27282
|
const composedFrom = uniqueComposedFrom([ownEntry, ...expanded.composedFrom]);
|
|
27224
|
-
if (!options.markers) return { rendered: expanded.content, composedFrom, identity };
|
|
27283
|
+
if (!options.markers) return { rendered: expanded.content, composedFrom, identity, hash: sha2563(expanded.semanticContent), selector: displaySelector, semanticContent: expanded.semanticContent };
|
|
27225
27284
|
return {
|
|
27226
27285
|
rendered: [
|
|
27227
27286
|
`<!-- BEGIN openpack:include ${displaySelector} sha256:${contentHash.slice(0, markerHashLength)} -->`,
|
|
@@ -27229,7 +27288,10 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
27229
27288
|
`<!-- END openpack:include ${displaySelector} -->`
|
|
27230
27289
|
].join("\n"),
|
|
27231
27290
|
composedFrom,
|
|
27232
|
-
identity
|
|
27291
|
+
identity,
|
|
27292
|
+
hash: sha2563(expanded.semanticContent),
|
|
27293
|
+
selector: displaySelector,
|
|
27294
|
+
semanticContent: expanded.semanticContent
|
|
27233
27295
|
};
|
|
27234
27296
|
}
|
|
27235
27297
|
function parseOpenPackIncludeSelector(selector) {
|
|
@@ -29497,6 +29559,7 @@ async function renderGraphIntoRoot(root, graph, targetContext) {
|
|
|
29497
29559
|
const compositionArtifacts = runtimeArtifacts;
|
|
29498
29560
|
const expandedArtifacts = await expandMarkdownIncludes(compositionArtifacts, staged.root, {
|
|
29499
29561
|
nodeId: rawNode.node.id,
|
|
29562
|
+
packageName: rawNode.node.name,
|
|
29500
29563
|
originNodeId: rawNode.node.id,
|
|
29501
29564
|
additionalComposeEntries: (artifact) => matchingCompositionEntries(
|
|
29502
29565
|
artifact,
|
|
@@ -29534,6 +29597,7 @@ async function renderGraphIntoRoot(root, graph, targetContext) {
|
|
|
29534
29597
|
includeEdges.set(`${includeEdge.fromNodeId}\0${includeEdge.alias}\0${includeEdge.toNodeId}\0${includeEdge.selector}\0${includeEdge.sourceHash}`, includeEdge);
|
|
29535
29598
|
return {
|
|
29536
29599
|
toNodeId: edge.to,
|
|
29600
|
+
packageName: target.rawNode.node.name,
|
|
29537
29601
|
packageRoot: target.root,
|
|
29538
29602
|
artifactPaths: target.artifactPaths,
|
|
29539
29603
|
sourcePath,
|
|
@@ -29621,6 +29685,7 @@ function matchingCompositionEntries(artifact, targetNodeId, runtime, rules) {
|
|
|
29621
29685
|
packageRoot: candidate.packageRoot,
|
|
29622
29686
|
artifactPaths: candidate.artifactPaths,
|
|
29623
29687
|
nodeId: candidate.ownerNodeId,
|
|
29688
|
+
packageName: candidate.ownerPackageName,
|
|
29624
29689
|
deduplicate: true
|
|
29625
29690
|
});
|
|
29626
29691
|
}
|
|
@@ -32400,6 +32465,7 @@ init_adapter();
|
|
|
32400
32465
|
init_transaction();
|
|
32401
32466
|
init_transport();
|
|
32402
32467
|
import { resolve as resolve40 } from "path";
|
|
32468
|
+
init_workspace();
|
|
32403
32469
|
var unknownWorkspaceOwner = "workspace:unknown";
|
|
32404
32470
|
async function planArtifactOwnershipHandoff(request) {
|
|
32405
32471
|
return validateOwnershipHandoff(request, request.transport ?? localTransport);
|
|
@@ -32461,6 +32527,11 @@ async function applyArtifactOwnershipHandoff(request) {
|
|
|
32461
32527
|
}
|
|
32462
32528
|
}
|
|
32463
32529
|
async function validateOwnershipHandoff(request, transport) {
|
|
32530
|
+
if (request.fromFleetId !== void 0 && (request.fromWorkspaceRoot === void 0 || request.fromUnknownOwner === true)) {
|
|
32531
|
+
throw new Error("A source fleet requires fromWorkspaceRoot and cannot be combined with fromUnknownOwner.");
|
|
32532
|
+
}
|
|
32533
|
+
if (request.fromFleetId !== void 0) fleetIdSchema.parse(request.fromFleetId);
|
|
32534
|
+
if (request.toFleetId !== void 0) fleetIdSchema.parse(request.toFleetId);
|
|
32464
32535
|
const endpoint = ownershipRunAsEndpoint(transport, request.targetRoot);
|
|
32465
32536
|
if (request.expectedHash) assertSha256(request.expectedHash, "expected artifact hash");
|
|
32466
32537
|
if (request.expectedRevision) assertSha256(request.expectedRevision, "expected manifest revision");
|
|
@@ -32488,7 +32559,7 @@ async function validateOwnershipHandoff(request, transport) {
|
|
|
32488
32559
|
);
|
|
32489
32560
|
}
|
|
32490
32561
|
const entry = matches[0];
|
|
32491
|
-
const fromOwner = request.fromUnknownOwner === true ? unknownWorkspaceOwner : workspaceOwnerForRoot(request.fromWorkspaceRoot);
|
|
32562
|
+
const fromOwner = request.fromUnknownOwner === true ? unknownWorkspaceOwner : workspaceOwnerForRoot(request.fromWorkspaceRoot, request.fromFleetId);
|
|
32492
32563
|
const toOwner = workspaceOwnerForRoot(request.toWorkspaceRoot, request.toFleetId);
|
|
32493
32564
|
if (fromOwner === toOwner) throw new Error("Ownership handoff requires different workspace roots.");
|
|
32494
32565
|
if (entry.workspaceOwner !== fromOwner) {
|
|
@@ -37371,13 +37442,28 @@ ownershipCommand.command("release-claim").description("release one exact plain o
|
|
|
37371
37442
|
console.log(`Plan digest: ${result.planDigest}`);
|
|
37372
37443
|
if (!options.apply) console.log("Apply with this same scope and reason plus --apply --plan-digest, --expected-source-revision and --expected-inventory-revision from this plan.");
|
|
37373
37444
|
});
|
|
37374
|
-
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("--source-state-key <key>", "explicit source install-manifest state key for this handoff only").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("--plan-digest <sha256>", "reviewed plan digest; required for run-as handoff").option("--expected-inventory-revision <sha256>", "reviewed full inventory revision; required for run-as handoff").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).option("--json", "print the complete deterministic ownership handoff plan", false).action(async (selector, options) => {
|
|
37445
|
+
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-fleet <id>", "qualify the current owner with a registered fleet id").option("--from-unknown-owner", "hand off an artifact recorded as workspace:unknown", false).requiredOption("--to-workspace-root <path>", "new owning workspace root").option("--source-state-key <key>", "explicit source install-manifest state key for this handoff only").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("--plan-digest <sha256>", "reviewed plan digest; required for run-as handoff").option("--expected-inventory-revision <sha256>", "reviewed full inventory revision; required for run-as handoff").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).option("--json", "print the complete deterministic ownership handoff plan", false).action(async (selector, options) => {
|
|
37375
37446
|
if (!options.dryRun && (!options.expectedHash || !options.expectedRevision)) {
|
|
37376
37447
|
throw new Error("Applying an ownership handoff requires --expected-hash and --expected-revision from a reviewed --dry-run.");
|
|
37377
37448
|
}
|
|
37378
37449
|
if (Boolean(options.fromWorkspaceRoot) === (options.fromUnknownOwner === true)) {
|
|
37379
37450
|
throw new Error("Ownership handoff requires exactly one of --from-workspace-root or --from-unknown-owner.");
|
|
37380
37451
|
}
|
|
37452
|
+
if (options.fromFleet !== void 0 && (!options.fromWorkspaceRoot || options.fromUnknownOwner)) {
|
|
37453
|
+
throw new Error("--from-fleet requires --from-workspace-root and cannot be combined with --from-unknown-owner.");
|
|
37454
|
+
}
|
|
37455
|
+
const fromWorkspaceRoot = options.fromWorkspaceRoot === void 0 ? void 0 : normalizeCliPath(options.fromWorkspaceRoot);
|
|
37456
|
+
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
37457
|
+
for (const [id, root, label] of [
|
|
37458
|
+
[options.fromFleet, fromWorkspaceRoot, "Source"],
|
|
37459
|
+
[options.toFleet, toWorkspaceRoot, "Destination"]
|
|
37460
|
+
]) {
|
|
37461
|
+
if (id === void 0) continue;
|
|
37462
|
+
const fleet = await showRegisteredFleet(id);
|
|
37463
|
+
if (resolve48(fleet.root) !== resolve48(root)) {
|
|
37464
|
+
throw new Error(`${label} fleet '${id}' is registered at ${fleet.root}, not ${root}.`);
|
|
37465
|
+
}
|
|
37466
|
+
}
|
|
37381
37467
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
37382
37468
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
37383
37469
|
if (targets.length !== 1) {
|
|
@@ -37401,13 +37487,6 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
37401
37487
|
"Migrate the target state before ownership handoff by running a full agentwheel install for this target."
|
|
37402
37488
|
);
|
|
37403
37489
|
}
|
|
37404
|
-
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
37405
|
-
if (options.toFleet) {
|
|
37406
|
-
const fleet = await showRegisteredFleet(options.toFleet);
|
|
37407
|
-
if (resolve48(fleet.root) !== resolve48(toWorkspaceRoot)) {
|
|
37408
|
-
throw new Error(`Destination fleet '${options.toFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
37409
|
-
}
|
|
37410
|
-
}
|
|
37411
37490
|
const request = {
|
|
37412
37491
|
workspaceRoot: await workspaceRootFromOptions(normalizedOptions),
|
|
37413
37492
|
reason: options.reason,
|
|
@@ -37419,7 +37498,7 @@ ownershipCommand.command("handoff").description("transfer one managed artifact b
|
|
|
37419
37498
|
adapter: adapter.name,
|
|
37420
37499
|
artifactType,
|
|
37421
37500
|
artifactName,
|
|
37422
|
-
...options.fromUnknownOwner === true ? { fromUnknownOwner: true } : { fromWorkspaceRoot:
|
|
37501
|
+
...options.fromUnknownOwner === true ? { fromUnknownOwner: true } : { fromWorkspaceRoot, fromFleetId: options.fromFleet },
|
|
37423
37502
|
toWorkspaceRoot,
|
|
37424
37503
|
toFleetId: options.toFleet,
|
|
37425
37504
|
expectedHash: options.expectedHash,
|
|
@@ -37485,20 +37564,43 @@ ownershipCommand.command("recovery-plan").description("write a read-only per-pat
|
|
|
37485
37564
|
console.log(JSON.stringify(report.counts));
|
|
37486
37565
|
});
|
|
37487
37566
|
});
|
|
37488
|
-
ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").option("--from-fleet-id <id>", "exact stale Fleet owner id for same-Fleet duplicate state").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").
|
|
37567
|
+
ownershipCommand.command("retire-stale").description("retire stale source-manifest ownership already covered by one Fleet manifest").requiredOption("--from-workspace-root <path>", "exact stale workspace owner root").option("--from-fleet-id <id>", "exact stale Fleet owner id for same-Fleet duplicate state").requiredOption("--to-workspace-root <path>", "registered destination Fleet root").requiredOption("--source-state-key <key>", "exact source install-manifest state key").option("--destination-state-key <key>", "exact Fleet-owned destination install-manifest state key (defaults to the stable target state)").option("--to-fleet <id>", "registered destination Fleet owner, independent of target workspace scope").option("--user", "use the user workspace", false).option("--local", "use the nearest local workspace", false).option("--fleet <id>", "use one registered named fleet (also the destination owner unless --to-fleet is set)").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named destination Fleet agent").option("--profile <name>", "destination Fleet profile resolving to one target").option("--plan-digest <sha256>", "reviewed plan digest; required with --apply").option("--expected-source-revision <sha256>", "reviewed source manifest revision; required with --apply").option("--expected-destination-revision <sha256>", "reviewed destination manifest revision; required with --apply").option("--expected-inventory-revision <sha256>", "reviewed runtime manifest inventory revision; required with --apply").option("--abandon-incomplete-merge-owner", "explicitly retire incomplete legacy merge metadata without changing runtime bytes", false).option("--apply", "apply the exact reviewed metadata-only retirement", false).option("--json", "print the complete deterministic plan as JSON", false).action(async (options) => {
|
|
37489
37568
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
37490
|
-
const
|
|
37569
|
+
const destinationFleet = options.toFleet ?? options.fleet;
|
|
37570
|
+
if (!destinationFleet) throw new Error("Stale ownership retirement requires --to-fleet <id> or the legacy --fleet <id> scope.");
|
|
37571
|
+
const fleet = await showRegisteredFleet(destinationFleet);
|
|
37491
37572
|
const toWorkspaceRoot = normalizeCliPath(options.toWorkspaceRoot);
|
|
37492
37573
|
if (resolve48(fleet.root) !== resolve48(toWorkspaceRoot)) {
|
|
37493
|
-
throw new Error(`Destination Fleet '${
|
|
37574
|
+
throw new Error(`Destination Fleet '${destinationFleet}' is registered at ${fleet.root}, not ${toWorkspaceRoot}.`);
|
|
37575
|
+
}
|
|
37576
|
+
const assertDestinationWorkspace = async (workspaceRoot, fleetId) => {
|
|
37577
|
+
const fleetRoot = resolve48(fleet.root);
|
|
37578
|
+
const canonicalFleetRoot = await realpath4(fleetRoot);
|
|
37579
|
+
const within = (root, path) => {
|
|
37580
|
+
const suffix = relative22(root, path);
|
|
37581
|
+
return suffix === "" || suffix !== ".." && !suffix.startsWith(`..${sep12}`) && !isAbsolute17(suffix);
|
|
37582
|
+
};
|
|
37583
|
+
if (canonicalFleetRoot !== fleetRoot || fleetId !== void 0 && fleetId !== fleet.id || !within(fleetRoot, resolve48(workspaceRoot)) || !within(canonicalFleetRoot, await realpath4(workspaceRoot))) {
|
|
37584
|
+
throw new Error(`Resolved target workspace does not belong to destination Fleet '${fleet.id}'.`);
|
|
37585
|
+
}
|
|
37586
|
+
};
|
|
37587
|
+
if (options.toFleet) {
|
|
37588
|
+
await resolveWorkspaceScope({ fleet: fleet.id });
|
|
37589
|
+
if (normalizedOptions.targetRoot && !normalizedOptions.workspaceTargetDerived) {
|
|
37590
|
+
await assertDestinationWorkspace(normalizedOptions.targetRoot);
|
|
37591
|
+
}
|
|
37592
|
+
const scope = await resolveCliWorkspaceScope(normalizedOptions);
|
|
37593
|
+
await assertDestinationWorkspace(scope.root, scope.fleetId);
|
|
37494
37594
|
}
|
|
37495
37595
|
const targets = await resolveCliTargets(normalizedOptions);
|
|
37496
37596
|
if (targets.length !== 1) {
|
|
37497
37597
|
throw new Error(`Stale ownership retirement requires exactly one destination Fleet target, found ${targets.length}.`);
|
|
37498
37598
|
}
|
|
37499
37599
|
const target = targets[0];
|
|
37500
|
-
if (
|
|
37501
|
-
|
|
37600
|
+
if (options.toFleet) {
|
|
37601
|
+
await assertDestinationWorkspace(target.workspaceRoot, target.fleetId);
|
|
37602
|
+
} else if (target.fleetId !== fleet.id || resolve48(target.workspaceRoot) !== resolve48(fleet.root)) {
|
|
37603
|
+
throw new Error(`Resolved target does not belong to destination Fleet '${fleet.id}'.`);
|
|
37502
37604
|
}
|
|
37503
37605
|
const adapterOptions = adapterOptionsForTarget(target, normalizedOptions);
|
|
37504
37606
|
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
@@ -37522,7 +37624,8 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
37522
37624
|
transport: transportForTarget(target)
|
|
37523
37625
|
};
|
|
37524
37626
|
const result = options.apply ? await applyRetireStaleOwnership(request) : await planRetireStaleOwnership(request);
|
|
37525
|
-
const
|
|
37627
|
+
const workspaceArgs = options.toFleet ? options.fleet ? ["--fleet", options.fleet] : options.user ? ["--user"] : options.targetRoot ? [] : ["--local"] : ["--fleet", fleet.id];
|
|
37628
|
+
const targetArgs = options.profile ? ["--profile", options.profile] : target.agentName ? ["--agent", target.agentName] : ["--adapter", adapter.name, ...workspaceArgs.length ? [] : ["--target-root", target.targetRoot]];
|
|
37526
37629
|
const applyArgs = [
|
|
37527
37630
|
"agentwheel",
|
|
37528
37631
|
"ownership",
|
|
@@ -37536,8 +37639,9 @@ ownershipCommand.command("retire-stale").description("retire stale source-manife
|
|
|
37536
37639
|
request.sourceStateKey,
|
|
37537
37640
|
"--destination-state-key",
|
|
37538
37641
|
request.destinationStateKey,
|
|
37539
|
-
"--fleet",
|
|
37540
|
-
|
|
37642
|
+
...options.toFleet ? ["--to-fleet", request.toFleetId] : [],
|
|
37643
|
+
...workspaceArgs,
|
|
37644
|
+
...(options.profile || target.agentName) && options.targetRoot ? ["--target-root", normalizedOptions.targetRoot] : [],
|
|
37541
37645
|
...targetArgs,
|
|
37542
37646
|
"--installation-type",
|
|
37543
37647
|
installationType,
|
|
@@ -38777,13 +38881,23 @@ function preserveUnrelatedGraphPackages(current, previous, selectedCurrentNodeId
|
|
|
38777
38881
|
for (const root of selectedCurrentRoots) {
|
|
38778
38882
|
if (!previous.canonical.roots.some((candidate) => candidate.rootId === root.rootId)) roots.push(root);
|
|
38779
38883
|
}
|
|
38884
|
+
const preservedNodeIds = /* @__PURE__ */ new Set();
|
|
38885
|
+
if (selectedRootIds.size > 0) {
|
|
38886
|
+
for (const root of previous.canonical.roots) {
|
|
38887
|
+
if (!selectedRootIds.has(root.rootId)) {
|
|
38888
|
+
for (const id of graphRootClosure(previous, root.rootId)) preservedNodeIds.add(id);
|
|
38889
|
+
}
|
|
38890
|
+
}
|
|
38891
|
+
}
|
|
38892
|
+
const replacedOwners = new Set([...selectedPreviousNodeIds].filter((id) => !preservedNodeIds.has(id)));
|
|
38893
|
+
for (const id of selectedRootIds) replacedOwners.add(`workspace:${id}`);
|
|
38780
38894
|
const currentNodes = current.canonical.nodes.filter((node) => selectedCurrentNodeIds.has(node.id));
|
|
38781
|
-
const previousNodes = previous.canonical.nodes.filter((node) => !selectedPreviousNodeIds.has(node.id));
|
|
38895
|
+
const previousNodes = previous.canonical.nodes.filter((node) => !selectedPreviousNodeIds.has(node.id) || preservedNodeIds.has(node.id)).map((node) => ({ ...node, requiredBy: node.requiredBy.filter((owner) => !replacedOwners.has(owner)) }));
|
|
38782
38896
|
const selectedCurrentArtifacts = current.canonical.artifacts.filter((artifact) => selectedCurrentNodeIds.has(artifact.graphNodeId));
|
|
38783
|
-
const previousArtifacts = previous.canonical.artifacts.filter((artifact) => !selectedPreviousNodeIds.has(artifact.graphNodeId));
|
|
38897
|
+
const previousArtifacts = previous.canonical.artifacts.filter((artifact) => !selectedPreviousNodeIds.has(artifact.graphNodeId) || preservedNodeIds.has(artifact.graphNodeId)).map((artifact) => ({ ...artifact, owners: artifact.owners.filter((owner) => !replacedOwners.has(owner)) }));
|
|
38784
38898
|
const edgeTouchesSelected = (from, to) => selectedCurrentNodeIds.has(from) || selectedCurrentNodeIds.has(to);
|
|
38785
|
-
const previousEdge = (edge) => !selectedPreviousNodeIds.has(edge.from) && !selectedPreviousNodeIds.has(edge.to);
|
|
38786
|
-
const previousIncludeEdge = (edge) => !selectedPreviousNodeIds.has(edge.fromNodeId) && !selectedPreviousNodeIds.has(edge.toNodeId);
|
|
38899
|
+
const previousEdge = (edge) => preservedNodeIds.has(edge.from) || !selectedPreviousNodeIds.has(edge.from) && !selectedPreviousNodeIds.has(edge.to);
|
|
38900
|
+
const previousIncludeEdge = (edge) => preservedNodeIds.has(edge.fromNodeId) || !selectedPreviousNodeIds.has(edge.fromNodeId) && !selectedPreviousNodeIds.has(edge.toNodeId);
|
|
38787
38901
|
const selectedCurrentRootIds = new Set(selectedCurrentRoots.map((root) => root.graphNodeId));
|
|
38788
38902
|
const currentRootOrNode = (id) => selectedCurrentNodeIds.has(id) || selectedCurrentRootIds.has(id);
|
|
38789
38903
|
const previousRootOrNode = (id) => selectedPreviousNodeIds.has(id) || previous.canonical.roots.some((root) => selectedRootIds.has(root.rootId) && root.graphNodeId === id);
|
|
@@ -38792,28 +38906,38 @@ function preserveUnrelatedGraphPackages(current, previous, selectedCurrentNodeId
|
|
|
38792
38906
|
canonical: {
|
|
38793
38907
|
...current.canonical,
|
|
38794
38908
|
roots,
|
|
38795
|
-
nodes: [...currentNodes
|
|
38796
|
-
|
|
38909
|
+
nodes: uniqueBy([...currentNodes.map((node) => {
|
|
38910
|
+
const old = previousNodes.find((candidate) => candidate.id === node.id);
|
|
38911
|
+
return old ? {
|
|
38912
|
+
...node,
|
|
38913
|
+
requiredBy: sortedUniqueValues([...node.requiredBy, ...old.requiredBy]),
|
|
38914
|
+
selected: sortedUniqueValues([...node.selected, ...old.selected])
|
|
38915
|
+
} : node;
|
|
38916
|
+
}), ...previousNodes], (node) => node.id),
|
|
38917
|
+
edges: uniqueBy([
|
|
38797
38918
|
...current.canonical.edges.filter((edge) => edgeTouchesSelected(edge.from, edge.to)),
|
|
38798
38919
|
...previous.canonical.edges.filter(previousEdge)
|
|
38799
|
-
],
|
|
38800
|
-
includeEdges: [
|
|
38920
|
+
], (edge) => `${edge.from}\0${edge.alias}\0${edge.to}`),
|
|
38921
|
+
includeEdges: uniqueBy([
|
|
38801
38922
|
...current.canonical.includeEdges.filter((edge) => edgeTouchesSelected(edge.fromNodeId, edge.toNodeId)),
|
|
38802
38923
|
...previous.canonical.includeEdges.filter(previousIncludeEdge)
|
|
38803
|
-
],
|
|
38804
|
-
artifacts: [...selectedCurrentArtifacts
|
|
38805
|
-
|
|
38924
|
+
], graphIncludeEdgeIdentity),
|
|
38925
|
+
artifacts: uniqueBy([...selectedCurrentArtifacts.map((artifact) => {
|
|
38926
|
+
const old = previousArtifacts.find((candidate) => graphArtifactIdentity2(candidate) === graphArtifactIdentity2(artifact));
|
|
38927
|
+
return old ? { ...artifact, owners: sortedUniqueValues([...artifact.owners, ...old.owners]) } : artifact;
|
|
38928
|
+
}), ...previousArtifacts], graphArtifactIdentity2),
|
|
38929
|
+
namespacing: uniqueBy([
|
|
38806
38930
|
...current.canonical.namespacing.filter((entry) => currentRootOrNode(entry.graphNodeId)),
|
|
38807
|
-
...previous.canonical.namespacing.filter((entry) => !previousRootOrNode(entry.graphNodeId))
|
|
38808
|
-
],
|
|
38809
|
-
overrides: [
|
|
38931
|
+
...previous.canonical.namespacing.filter((entry) => !previousRootOrNode(entry.graphNodeId) || preservedNodeIds.has(entry.graphNodeId))
|
|
38932
|
+
], (entry) => `${entry.graphNodeId}\0${entry.type}\0${entry.name}`),
|
|
38933
|
+
overrides: uniqueBy([
|
|
38810
38934
|
...current.canonical.overrides.filter((entry) => currentRootOrNode(entry.graphNodeId) || currentRootOrNode(entry.overriddenGraphNodeId)),
|
|
38811
|
-
...previous.canonical.overrides.filter((entry) => !previousRootOrNode(entry.graphNodeId) && !previousRootOrNode(entry.overriddenGraphNodeId))
|
|
38812
|
-
],
|
|
38813
|
-
plainNameIncumbents: [
|
|
38935
|
+
...previous.canonical.overrides.filter((entry) => !selectedRootIds.has(entry.rootId) && (!previousRootOrNode(entry.graphNodeId) && !previousRootOrNode(entry.overriddenGraphNodeId) || preservedNodeIds.has(entry.graphNodeId)))
|
|
38936
|
+
], (entry) => `${entry.rootId}\0${entry.selector}`),
|
|
38937
|
+
plainNameIncumbents: uniqueBy([
|
|
38814
38938
|
...current.canonical.plainNameIncumbents.filter((entry) => currentRootOrNode(entry.graphNodeId)),
|
|
38815
|
-
...previous.canonical.plainNameIncumbents.filter((entry) => !previousRootOrNode(entry.graphNodeId))
|
|
38816
|
-
]
|
|
38939
|
+
...previous.canonical.plainNameIncumbents.filter((entry) => !previousRootOrNode(entry.graphNodeId) || preservedNodeIds.has(entry.graphNodeId))
|
|
38940
|
+
], (entry) => `${entry.adapter}\0${entry.targetFingerprint}\0${entry.type}\0${entry.name}`)
|
|
38817
38941
|
}
|
|
38818
38942
|
});
|
|
38819
38943
|
}
|
|
@@ -39143,16 +39267,31 @@ function uniqueBy(values, key) {
|
|
|
39143
39267
|
return true;
|
|
39144
39268
|
});
|
|
39145
39269
|
}
|
|
39146
|
-
function scopeInstallPlanToRoot(result, rootId, manifest) {
|
|
39270
|
+
function scopeInstallPlanToRoot(result, rootId, manifest, previousLock) {
|
|
39147
39271
|
const scopedOwners = scopedGraphOwnerKeys(result, rootId);
|
|
39272
|
+
const exclusiveOwners = new Set(scopedOwners);
|
|
39273
|
+
if (previousLock) {
|
|
39274
|
+
for (const id of graphRootClosure(previousLock, rootId)) exclusiveOwners.add(id);
|
|
39275
|
+
for (const root of previousLock.canonical.roots) {
|
|
39276
|
+
if (root.rootId === rootId) continue;
|
|
39277
|
+
for (const id of graphRootClosure(previousLock, root.rootId)) exclusiveOwners.delete(id);
|
|
39278
|
+
}
|
|
39279
|
+
}
|
|
39148
39280
|
const manifestByPath = new Map((manifest?.entries ?? []).map((entry) => [entry.path, entry]));
|
|
39149
39281
|
const preservedPaths = /* @__PURE__ */ new Set();
|
|
39150
39282
|
const plannedPaths = /* @__PURE__ */ new Set();
|
|
39151
39283
|
const operations = [];
|
|
39152
39284
|
for (const operation of result.plan.operations) {
|
|
39153
39285
|
plannedPaths.add(operation.relativeDestPath);
|
|
39154
|
-
|
|
39155
|
-
|
|
39286
|
+
const stale = operation.action === "remove" || operation.action === "drift" && !operation.desiredHash;
|
|
39287
|
+
const exclusivelyOwned = operation.owners?.length && operation.owners.every((owner) => exclusiveOwners.has(owner));
|
|
39288
|
+
if (stale ? exclusivelyOwned : operationBelongsToScopedRoot(operation, scopedOwners)) {
|
|
39289
|
+
const entry2 = manifestByPath.get(operation.relativeDestPath);
|
|
39290
|
+
const foreignOwners = entry2 && "owners" in entry2 && Array.isArray(entry2.owners) ? entry2.owners.filter((owner) => typeof owner === "string" && !exclusiveOwners.has(owner)) : [];
|
|
39291
|
+
operations.push(stale || !operation.owners ? operation : {
|
|
39292
|
+
...operation,
|
|
39293
|
+
owners: sortedUniqueValues([...operation.owners, ...foreignOwners])
|
|
39294
|
+
});
|
|
39156
39295
|
continue;
|
|
39157
39296
|
}
|
|
39158
39297
|
const entry = manifestByPath.get(operation.relativeDestPath);
|
|
@@ -39178,7 +39317,7 @@ function scopeInstallPlanToRoot(result, rootId, manifest) {
|
|
|
39178
39317
|
};
|
|
39179
39318
|
}
|
|
39180
39319
|
function scopeUpdatePlanToRoot(result, rootId, previousLock, manifest) {
|
|
39181
|
-
const scoped = scopeInstallPlanToRoot(result, rootId, manifest);
|
|
39320
|
+
const scoped = scopeInstallPlanToRoot(result, rootId, manifest, previousLock);
|
|
39182
39321
|
const selectedCurrentNodeIds = graphRootClosure(scoped.bundle.graphLock, rootId);
|
|
39183
39322
|
const selectedPreviousNodeIds = graphRootClosure(previousLock, rootId);
|
|
39184
39323
|
const graphLock = preserveUnrelatedGraphPackages(
|
package/openpack.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentwheel",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.2",
|
|
4
4
|
"description": "Weave skills, rules, and instructions across every AI agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@skillkit/core": "1.24.0",
|
|
54
54
|
"commander": "14.0.2",
|
|
55
55
|
"jsonc-parser": "3.3.1",
|
|
56
|
+
"smol-toml": "1.9.1",
|
|
56
57
|
"typescript": "5.9.3",
|
|
57
58
|
"yaml": "2.8.2",
|
|
58
59
|
"zod": "4.1.13"
|