@agentxm/workspace-sync 0.28.4-bootstrap.0 → 0.28.5

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.
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export { WorkspaceSyncFailed, type WorkspaceSyncCleanupFailure } from "./errors.js";
10
10
  export { type SyncFailureAdapter, type SyncPolicyFailure } from "./failure-adapter.js";
11
- export { cleanupManagedArtifactsForRemovedAgents, cleanupStaleManagedSkillDirectories, cleanupStaleManagedSubagentFiles, inspectWorkspaceOwnership, type RemovedAgentArtifactCleanupResult, type RenderedFileCleanupResult, } from "./rendered-file-cleanup.js";
11
+ export { inspectWorkspaceOwnership, reconcileAgentOutputs, type ReconcileAgentOutputsArgs, type ReconcileAgentOutputsResult, } from "./rendered-file-cleanup.js";
12
12
  export { buildInlineMcpServerSyncOperation, buildMcpServerPruneOperation, collectCleanupStep, collectHooksStep, collectInstructionStep, collectKnowledgeStep, isInlineMcpServerEntry, makeSyncPlan, projectionDivergenceLabel, projectionFactsNeedReconciliation, SYNC_PLAN_DESCRIPTION, SYNC_PLAN_NAME, SYNC_PRESENTATION, SYNC_RECOVERY_IDS, syncRecoveryIdentifiers, type SyncStepRequirements, } from "./plan.js";
13
13
  export { collectMaterializeSteps, normalizedIdentity, recoverableExternalPackName, scopedProblems, type ConfiguredPackRecovery, type ResolvedDesiredRef, type RunMcpServerInstall, type SyncSelection, } from "./materialize.js";
14
14
  //# sourceMappingURL=index.d.ts.map
package/dist/src/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  export { WorkspaceSyncFailed } from "./errors.js";
10
10
  export {} from "./failure-adapter.js";
11
- export { cleanupManagedArtifactsForRemovedAgents, cleanupStaleManagedSkillDirectories, cleanupStaleManagedSubagentFiles, inspectWorkspaceOwnership, } from "./rendered-file-cleanup.js";
11
+ export { inspectWorkspaceOwnership, reconcileAgentOutputs, } from "./rendered-file-cleanup.js";
12
12
  export { buildInlineMcpServerSyncOperation, buildMcpServerPruneOperation, collectCleanupStep, collectHooksStep, collectInstructionStep, collectKnowledgeStep, isInlineMcpServerEntry, makeSyncPlan, projectionDivergenceLabel, projectionFactsNeedReconciliation, SYNC_PLAN_DESCRIPTION, SYNC_PLAN_NAME, SYNC_PRESENTATION, SYNC_RECOVERY_IDS, syncRecoveryIdentifiers, } from "./plan.js";
13
13
  export { collectMaterializeSteps, normalizedIdentity, recoverableExternalPackName, scopedProblems, } from "./materialize.js";
14
14
  //# sourceMappingURL=index.js.map
@@ -64,6 +64,8 @@ export declare const collectMaterializeSteps: <E = never, R = SyncStepRequiremen
64
64
  serialMaterialization: boolean;
65
65
  expectedSkillNames: Set<string & import("effect/Brand").Brand<"ExtensionName">>;
66
66
  expectedSubagentNames: Set<string & import("effect/Brand").Brand<"ExtensionName">>;
67
+ expectedMcpServerNames: Set<string>;
68
+ expectedHookNames: Set<string & import("effect/Brand").Brand<"ExtensionName">>;
67
69
  releaseAge: {
68
70
  evaluatedAt: string;
69
71
  holdbacks: readonly import("@agentxm/registry-protocol/unstable/registry/release-age-policy").ReleaseAgeRecordBase[];
@@ -121,5 +123,5 @@ export declare const collectMaterializeSteps: <E = never, R = SyncStepRequiremen
121
123
  registryLifecycle?: import("@agentxm/workspace-operations").RegistryLifecycleEvidence;
122
124
  run: Effect.Effect<JobStepResult<never>, StepFailure, never>;
123
125
  })[];
124
- }, WorkspaceSyncFailed | import("@agentxm/extension-workspace").CoupledDependencyFailure | import("@agentxm/extension-workspace").PackageMaterializationFailed | import("@agentxm/extension-workspace").StagedPackageInvalid | import("@agentxm/extension-workspace").CanonicalPackageProbeFailed | import("@agentxm/extension-workspace").PackageCopyFailed | import("@agentxm/extension-workspace").ArchiveIntegrityMismatch | import("@agentxm/extension-workspace").CreateDestinationExists | import("@agentxm/extension-workspace").CreateNameConfigured | import("@agentxm/extension-workspace").CreateDestinationInspectionFailed | import("@agentxm/workspace-state").PathTraversalDetected | import("@agentxm/extension-workspace").ForkPackageInvalid | import("@agentxm/extension-workspace").ForkPackageConflict | import("@agentxm/extension-workspace").ForkPackageFailed | import("@agentxm/extension-workspace").NativeImportUnsupported | import("@agentxm/extension-workspace").NativeImportInvalid | import("@agentxm/extension-workspace").NativeImportConflict | import("@agentxm/extension-workspace").NativeImportFailed | import("@agentxm/extension-workspace").SourceAuthorityBlocked | import("@agentxm/extension-workspace").LifecyclePostconditionViolated | import("@agentxm/extension-workspace").ScaffoldedExtensionUnresolved | import("@agentxm/extension-workspace").DesiredStateIncomplete | import("@agentxm/extension-workspace").AuthoredContributorUnsupported | import("@agentxm/extension-workspace").ContributorIdentityInvalid | import("@agentxm/extension-workspace").ContributorUnresolved | import("@agentxm/extension-workspace").ContributorTreeMismatch | import("@agentxm/extension-workspace").ProjectionTargetUnsupported | import("@agentxm/extension-workspace").ManagedRegionViolation | import("@agentxm/extension-workspace").ProjectionIoFailed | import("@agentxm/extension-workspace").InstructionMaintenanceFailed | import("@agentxm/extension-workspace").RuleDefinitionInvalid | import("@agentxm/extension-workspace").RuleInstallStateMissing | import("@agentxm/extension-workspace").HookDefinitionInvalid | import("@agentxm/extension-workspace").HookConfigInvalid | import("@agentxm/extension-workspace").HookIoFailed | import("@agentxm/extension-workspace").HookInstallStateMissing | import("@agentxm/extension-workspace").SubagentDefinitionInvalid | import("@agentxm/extension-workspace").SubagentContentUnreadable | import("@agentxm/extension-workspace").SubagentIoFailed | import("@agentxm/extension-workspace").SubagentInstallStateMissing | import("@agentxm/extension-workspace").McpConfigInvalid | import("@agentxm/extension-workspace").McpConfigIoFailed | import("@agentxm/extension-workspace").McpEntryUnmanaged | import("@agentxm/extension-workspace").McpOwnershipMarkerInvalid | import("@agentxm/extension-workspace").McpDefinitionInvalid | import("@agentxm/extension-workspace").McpRegistryOnlyInstall | import("@agentxm/extension-workspace").McpInstallStateMissing | import("@agentxm/extension-workspace").McpSharedTargetConflict | import("@agentxm/extension-workspace").SkillDefinitionInvalid | import("@agentxm/extension-workspace").SkillMaterializationFailed | import("@agentxm/extension-workspace").SkillInstallStateMissing | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible | import("@agentxm/extension-workspace").PackDefinitionInvalid | import("@agentxm/extension-workspace").PackInstallStateMissing | import("@agentxm/extension-workspace").PackArchiveFetchFailed | import("@agentxm/extension-workspace").PackStagingFailed | import("@agentxm/extension-workspace").PackDependencyInvalid | import("@agentxm/extension-workspace").PackDependencyConflict | import("@agentxm/extension-workspace").PackConstraintShadowed | import("@agentxm/extension-workspace").PackDependencyMissing | import("@agentxm/extension-workspace").PackDependencyUnsatisfied | import("@agentxm/extension-workspace").KnowledgeDefinitionInvalid | import("@agentxm/extension-workspace").KnowledgeIoFailed | import("@agentxm/extension-workspace").KnowledgeInstallStateMissing | import("@agentxm/extension-workspace").KnowledgeResolutionMissing | import("@agentxm/extension-workspace").KnowledgeDesiredStateUnreconcilable | import("@agentxm/extension-workspace").KnowledgeUnavailable | import("@agentxm/extension-workspace").KnowledgeObservableContractViolated | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/extension-workspace").TransientBackupFailed | import("@agentxm/extension-workspace").WriteBackupRetained | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").LockfileIoError | import("@agentxm/workspace-state").LockfileParseError | import("@agentxm/workspace-state").LockfileDecodeError | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").SettingsWriteError | import("@agentxm/workspace-state").WorkspaceSnapshotError | import("@agentxm/workspace-state").LockfileValidationError | import("@agentxm/workspace-state").LockfileWriteError | import("@agentxm/workspace-state").WorkspaceDirectoryError | import("@agentxm/workspace-state").TransitionLockError | import("@agentxm/workspace-state").TransitionLockUnavailable | import("@agentxm/workspace-state").WorkspaceTransitionCompromised | import("@agentxm/workspace-state").WorkspaceRestorationIncomplete | import("@agentxm/workspace-state").WorkspaceLayoutError | import("@agentxm/workspace-state").WorkspaceNotInitialized | import("@agentxm/workspace-state").LockedSkillMissing | import("@agentxm/workspace-state").SettingsEntryMissing | import("@agentxm/workspace-state").InvalidAgentId | import("@agentxm/workspace-state").DesiredPackGraphIncomplete | import("@agentxm/workspace-state").CanonicalPathRemovalError | import("@agentxm/workspace-state").SymlinkCreationError | import("@agentxm/workspace-state").LockEntrySourceMissing | import("@agentxm/workspace-state").LockEntryUrlInvalid | import("@agentxm/workspace-state").LockEntryNameInvalid | import("@agentxm/workspace-state").LockEntryEndpointConflict | import("@agentxm/workspace-state").LockEntrySourceTypeConflict | import("@agentxm/workspace-state").AcceptedResolutionMissing | import("@agentxm/workspace-state").InlineExtensionSourceMissing | import("@agentxm/workspace-state").SupersededCanonicalRemovalFailed | import("@agentxm/workspace-state").PackageContentHashFailed | import("@agentxm/workspace-state").WorkspaceSourceInvalid | import("@agentxm/workspace-state").SkillDiscoveryRootInvalid | import("@agentxm/workspace-state").SubagentScanFailed | import("@agentxm/workspace-state").LockfileResolvedVersionInvalid | import("@agentxm/extension-model/unstable/extensions").FqnInvalidError | import("@agentxm/registry-protocol/unstable/content/frontmatter").FrontmatterParseFailure | import("@agentxm/registry-protocol/unstable/content/subagent-content").SubagentContentError | E, Path.Path | FileSystem.FileSystem | CodingAgentRepository | WorkspaceMutations | KnowledgeManager | HookManager | RuleManager | SubagentManager | RResolve | SkillManager>;
126
+ }, WorkspaceSyncFailed | import("@agentxm/extension-workspace").CoupledDependencyFailure | import("@agentxm/extension-workspace").PackageMaterializationFailed | import("@agentxm/extension-workspace").StagedPackageInvalid | import("@agentxm/extension-workspace").CanonicalPackageProbeFailed | import("@agentxm/extension-workspace").PackageCopyFailed | import("@agentxm/extension-workspace").ArchiveIntegrityMismatch | import("@agentxm/extension-workspace").CreateDestinationExists | import("@agentxm/extension-workspace").CreateNameConfigured | import("@agentxm/extension-workspace").CreateDestinationInspectionFailed | import("@agentxm/workspace-state").PathTraversalDetected | import("@agentxm/extension-workspace").ForkPackageInvalid | import("@agentxm/extension-workspace").ForkPackageConflict | import("@agentxm/extension-workspace").ForkPackageFailed | import("@agentxm/extension-workspace").NativeImportUnsupported | import("@agentxm/extension-workspace").NativeImportInvalid | import("@agentxm/extension-workspace").NativeImportConflict | import("@agentxm/extension-workspace").NativeImportFailed | import("@agentxm/extension-workspace").SourceAuthorityBlocked | import("@agentxm/extension-workspace").LifecyclePostconditionViolated | import("@agentxm/extension-workspace").ScaffoldedExtensionUnresolved | import("@agentxm/extension-workspace").DesiredStateIncomplete | import("@agentxm/extension-workspace").AuthoredContributorUnsupported | import("@agentxm/extension-workspace").ContributorIdentityInvalid | import("@agentxm/extension-workspace").ContributorUnresolved | import("@agentxm/extension-workspace").ContributorTreeMismatch | import("@agentxm/extension-workspace").ProjectionTargetUnsupported | import("@agentxm/extension-workspace").ManagedRegionViolation | import("@agentxm/extension-workspace").ProjectionIoFailed | import("@agentxm/extension-workspace").InstructionMaintenanceFailed | import("@agentxm/extension-workspace").RuleDefinitionInvalid | import("@agentxm/extension-workspace").RuleInstallStateMissing | import("@agentxm/extension-workspace").HookDefinitionInvalid | import("@agentxm/extension-workspace").HookConfigInvalid | import("@agentxm/extension-workspace").HookIoFailed | import("@agentxm/extension-workspace").HookInstallStateMissing | import("@agentxm/extension-workspace").SubagentDefinitionInvalid | import("@agentxm/extension-workspace").SubagentContentUnreadable | import("@agentxm/extension-workspace").SubagentIoFailed | import("@agentxm/extension-workspace").SubagentInstallStateMissing | import("@agentxm/extension-workspace").McpConfigInvalid | import("@agentxm/extension-workspace").McpConfigIoFailed | import("@agentxm/extension-workspace").McpEntryUnmanaged | import("@agentxm/extension-workspace").McpOwnershipMarkerInvalid | import("@agentxm/extension-workspace").McpDefinitionInvalid | import("@agentxm/extension-workspace").McpRegistryOnlyInstall | import("@agentxm/extension-workspace").McpInstallStateMissing | import("@agentxm/extension-workspace").McpSharedTargetConflict | import("@agentxm/extension-workspace").SkillDefinitionInvalid | import("@agentxm/extension-workspace").SkillMaterializationFailed | import("@agentxm/extension-workspace").SkillInstallStateMissing | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible | import("@agentxm/extension-workspace").PackDefinitionInvalid | import("@agentxm/extension-workspace").PackInstallStateMissing | import("@agentxm/extension-workspace").PackArchiveFetchFailed | import("@agentxm/extension-workspace").PackStagingFailed | import("@agentxm/extension-workspace").PackDependencyInvalid | import("@agentxm/extension-workspace").PackDependencyConflict | import("@agentxm/extension-workspace").PackConstraintShadowed | import("@agentxm/extension-workspace").PackDependencyMissing | import("@agentxm/extension-workspace").PackDependencyUnsatisfied | import("@agentxm/extension-workspace").KnowledgeDefinitionInvalid | import("@agentxm/extension-workspace").KnowledgeIoFailed | import("@agentxm/extension-workspace").KnowledgeInstallStateMissing | import("@agentxm/extension-workspace").KnowledgeResolutionMissing | import("@agentxm/extension-workspace").KnowledgeDesiredStateUnreconcilable | import("@agentxm/extension-workspace").KnowledgeUnavailable | import("@agentxm/extension-workspace").KnowledgeObservableContractViolated | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/extension-workspace").TransientBackupFailed | import("@agentxm/extension-workspace").WriteBackupRetained | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").LockfileIoError | import("@agentxm/workspace-state").LockfileParseError | import("@agentxm/workspace-state").LockfileDecodeError | import("@agentxm/workspace-state").LockfileVersionUnsupported | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").SettingsWriteError | import("@agentxm/workspace-state").WorkspaceSnapshotError | import("@agentxm/workspace-state").LockfileValidationError | import("@agentxm/workspace-state").LockfileWriteError | import("@agentxm/workspace-state").WorkspaceDirectoryError | import("@agentxm/workspace-state").TransitionLockError | import("@agentxm/workspace-state").TransitionLockUnavailable | import("@agentxm/workspace-state").WorkspaceTransitionCompromised | import("@agentxm/workspace-state").WorkspaceRestorationIncomplete | import("@agentxm/workspace-state").WorkspaceLayoutError | import("@agentxm/workspace-state").WorkspaceNotInitialized | import("@agentxm/workspace-state").LockedSkillMissing | import("@agentxm/workspace-state").SettingsEntryMissing | import("@agentxm/workspace-state").InvalidAgentId | import("@agentxm/workspace-state").DesiredPackGraphIncomplete | import("@agentxm/workspace-state").CanonicalPathRemovalError | import("@agentxm/workspace-state").SymlinkCreationError | import("@agentxm/workspace-state").LockEntrySourceMissing | import("@agentxm/workspace-state").LockEntryUrlInvalid | import("@agentxm/workspace-state").LockEntryNameInvalid | import("@agentxm/workspace-state").LockEntryEndpointConflict | import("@agentxm/workspace-state").LockEntrySourceTypeConflict | import("@agentxm/workspace-state").AcceptedResolutionMissing | import("@agentxm/workspace-state").InlineExtensionSourceMissing | import("@agentxm/workspace-state").SupersededCanonicalRemovalFailed | import("@agentxm/workspace-state").PackageContentHashFailed | import("@agentxm/workspace-state").WorkspaceSourceInvalid | import("@agentxm/workspace-state").SkillDiscoveryRootInvalid | import("@agentxm/workspace-state").SubagentScanFailed | import("@agentxm/workspace-state").LockfileResolvedVersionInvalid | import("@agentxm/extension-model/unstable/extensions").FqnInvalidError | import("@agentxm/registry-protocol/unstable/content/frontmatter").FrontmatterParseFailure | import("@agentxm/registry-protocol/unstable/content/subagent-content").SubagentContentError | E, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations | KnowledgeManager | HookManager | RuleManager | SubagentManager | RResolve | SkillManager>;
125
127
  //# sourceMappingURL=materialize.d.ts.map
@@ -15,7 +15,7 @@ import * as Option from "effect/Option";
15
15
  import * as Path from "effect/Path";
16
16
  import * as ServiceMap from "effect/Context";
17
17
  import * as semver from "semver";
18
- import { CodingAgentRepository, HookManager, KnowledgeManager, RuleManager, SkillManager, SubagentManager, buildMaterializeOperation, collectManagedAgentMcpServers, enabledConfiguredEntries, extensionConstraintFactText, inspectMcpServerAcrossAgents, isConfiguredEntryEnabled, makeExtensionConstraintInvariantFact, planExtensionConstraintFact, pruneManagedMcpServersForAgent, skillArtifactFromTargets, targetFromRef, toStepKey, } from "@agentxm/extension-workspace";
18
+ import { CodingAgentRepository, HookManager, KnowledgeManager, RuleManager, SkillManager, SubagentManager, buildMaterializeOperation, collectManagedAgentMcpServers, enabledConfiguredEntries, extensionConstraintFactText, inspectMcpServerAcrossAgents, isConfiguredEntryEnabled, makeExtensionConstraintInvariantFact, planExtensionConstraintFact, skillArtifactFromTargets, targetFromRef, toStepKey, } from "@agentxm/extension-workspace";
19
19
  import { normalizeReleaseAgeRecords, } from "@agentxm/registry-protocol/unstable/registry/release-age-policy";
20
20
  import {} from "@agentxm/extension-model/unstable/extensions/release-age";
21
21
  import { sanitizeName, acceptedResolutionRef, acceptedCanonicalObservation, isSourcedDesiredExtension, desiredStateProblemsText, WorkspaceMutations, usableAcceptedCanonical, } from "@agentxm/workspace-state";
@@ -29,7 +29,7 @@ import {} from "@agentxm/extension-model/unstable/extensions/refs/subagent";
29
29
  import { parseExtensionFqnParts, } from "@agentxm/extension-model/unstable/extensions";
30
30
  import { StepFailure, } from "@agentxm/workspace-operations";
31
31
  import { WorkspaceSyncFailed } from "./errors.js";
32
- import { isInlineMcpServerEntry, SYNC_RECOVERY_IDS, buildInlineMcpServerSyncOperation, buildMcpServerPruneOperation, } from "./plan.js";
32
+ import { isInlineMcpServerEntry, SYNC_RECOVERY_IDS, buildInlineMcpServerSyncOperation, } from "./plan.js";
33
33
  export const normalizedIdentity = (identity) => identity.startsWith("workspace:") ? identity.slice("workspace:".length) : identity;
34
34
  const sourceTransitionIdentity = (authority, identity) => authority === "workspace"
35
35
  ? "workspace"
@@ -205,7 +205,6 @@ export const collectMaterializeSteps = (args) => Effect.gen(function* () {
205
205
  const configuredAgents = args.configuredAgents ?? (yield* ws.getConfiguredAgents());
206
206
  const desiredState = yield* ws.getDesiredStateGraph();
207
207
  const selection = args.selection ?? { target: Option.none(), type: Option.none() };
208
- const isScoped = Option.isSome(selection.target) || Option.isSome(selection.type);
209
208
  const problems = scopedProblems(desiredState, selection);
210
209
  const blockers = problems.filter((problem) => {
211
210
  const name = recoverableExternalPackName(desiredState, problem);
@@ -435,15 +434,6 @@ export const collectMaterializeSteps = (args) => Effect.gen(function* () {
435
434
  adapter: args.adapter,
436
435
  }));
437
436
  }), { concurrency: "unbounded" }).pipe(Effect.map((steps) => steps.flatMap((step) => (Option.isSome(step) ? [step.value] : []))));
438
- const needsMcpServerPrune = problems.length === 0 &&
439
- !isScoped &&
440
- configuredAgents.length > 0 &&
441
- (yield* Effect.forEach(configuredAgents, (agentId) => pruneManagedMcpServersForAgent(agentId, {
442
- workspaceRoot: ws.baseDir,
443
- declaredServerNames: declaredMcpServerNames,
444
- scope: ws.scope,
445
- dryRun: true,
446
- }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, path)), { concurrency: "unbounded" }).pipe(Effect.map((outcomes) => outcomes.some((outcome) => outcome._tag === "success" && outcome.targets !== undefined))));
447
437
  const skillMaterializeStep = ({ ref, force, transitionLabel }) => Effect.gen(function* () {
448
438
  const buildArtifact = () => skillSyncArtifact({
449
439
  ref,
@@ -490,6 +480,8 @@ export const collectMaterializeSteps = (args) => Effect.gen(function* () {
490
480
  serialMaterialization: packRecoverySteps.length > 0,
491
481
  expectedSkillNames: new Set(skillRefs.map(({ ref }) => ref.skill.name)),
492
482
  expectedSubagentNames: new Set(subagentRefs.map(({ ref }) => ref.subagent.name)),
483
+ expectedMcpServerNames: declaredMcpServerNames,
484
+ expectedHookNames: new Set(hookRefs.map(({ ref }) => ref.hook.name)),
493
485
  releaseAge: {
494
486
  evaluatedAt: DateTime.formatIso(releaseAgeEvaluation.evaluatedAt),
495
487
  holdbacks: normalizeReleaseAgeRecords([
@@ -513,16 +505,6 @@ export const collectMaterializeSteps = (args) => Effect.gen(function* () {
513
505
  runMcpServerInstall: args.runMcpServerInstall,
514
506
  })),
515
507
  ...inlineMcpServerSteps,
516
- ...(needsMcpServerPrune
517
- ? [
518
- buildMcpServerPruneOperation({
519
- declaredServerNames: declaredMcpServerNames,
520
- agentIds: configuredAgents,
521
- ws,
522
- adapter: args.adapter,
523
- }),
524
- ]
525
- : []),
526
508
  ...subagentRefs.filter(({ materialize }) => materialize).map(subagentMaterializeStep),
527
509
  ...ruleRefs
528
510
  .filter(({ materialize }) => materialize)
@@ -76,8 +76,10 @@ export declare const collectKnowledgeStep: (args: {
76
76
  export declare const collectCleanupStep: (args: {
77
77
  readonly expectedSkillNames: ReadonlySet<string>;
78
78
  readonly expectedSubagentNames: ReadonlySet<string>;
79
+ readonly expectedMcpServerNames: ReadonlySet<string>;
80
+ readonly expectedHookNames: ReadonlySet<string>;
79
81
  readonly adapter: SyncFailureAdapter;
80
- }) => Effect.Effect<Option.Option<PlannedJobStep<SyncStepRequirements>>, import("./errors.ts").WorkspaceSyncCleanupFailure, Path.Path | FileSystem.FileSystem | CodingAgentRepository | WorkspaceMutations>;
82
+ }) => Effect.Effect<Option.Option<PlannedJobStep<SyncStepRequirements>>, import("./errors.ts").WorkspaceSyncFailed | import("@agentxm/extension-workspace").CoupledDependencyFailure | import("@agentxm/extension-workspace").PackageMaterializationFailed | import("@agentxm/extension-workspace").StagedPackageInvalid | import("@agentxm/extension-workspace").CanonicalPackageProbeFailed | import("@agentxm/extension-workspace").PackageCopyFailed | import("@agentxm/extension-workspace").ArchiveIntegrityMismatch | import("@agentxm/extension-workspace").CreateDestinationExists | import("@agentxm/extension-workspace").CreateNameConfigured | import("@agentxm/extension-workspace").CreateDestinationInspectionFailed | import("@agentxm/workspace-state").PathTraversalDetected | import("@agentxm/extension-workspace").ForkPackageInvalid | import("@agentxm/extension-workspace").ForkPackageConflict | import("@agentxm/extension-workspace").ForkPackageFailed | import("@agentxm/extension-workspace").NativeImportUnsupported | import("@agentxm/extension-workspace").NativeImportInvalid | import("@agentxm/extension-workspace").NativeImportConflict | import("@agentxm/extension-workspace").NativeImportFailed | import("@agentxm/extension-workspace").SourceAuthorityBlocked | import("@agentxm/extension-workspace").LifecyclePostconditionViolated | import("@agentxm/extension-workspace").ScaffoldedExtensionUnresolved | import("@agentxm/extension-workspace").DesiredStateIncomplete | import("@agentxm/extension-workspace").AuthoredContributorUnsupported | import("@agentxm/extension-workspace").ContributorIdentityInvalid | import("@agentxm/extension-workspace").ContributorUnresolved | import("@agentxm/extension-workspace").ContributorTreeMismatch | import("@agentxm/extension-workspace").ProjectionTargetUnsupported | import("@agentxm/extension-workspace").ManagedRegionViolation | import("@agentxm/extension-workspace").ProjectionIoFailed | import("@agentxm/extension-workspace").InstructionMaintenanceFailed | import("@agentxm/extension-workspace").RuleDefinitionInvalid | import("@agentxm/extension-workspace").RuleInstallStateMissing | import("@agentxm/extension-workspace").HookDefinitionInvalid | import("@agentxm/extension-workspace").HookConfigInvalid | import("@agentxm/extension-workspace").HookIoFailed | import("@agentxm/extension-workspace").HookInstallStateMissing | import("@agentxm/extension-workspace").SubagentDefinitionInvalid | import("@agentxm/extension-workspace").SubagentContentUnreadable | import("@agentxm/extension-workspace").SubagentIoFailed | import("@agentxm/extension-workspace").SubagentInstallStateMissing | import("@agentxm/extension-workspace").McpConfigInvalid | import("@agentxm/extension-workspace").McpConfigIoFailed | import("@agentxm/extension-workspace").McpEntryUnmanaged | import("@agentxm/extension-workspace").McpOwnershipMarkerInvalid | import("@agentxm/extension-workspace").McpDefinitionInvalid | import("@agentxm/extension-workspace").McpRegistryOnlyInstall | import("@agentxm/extension-workspace").McpInstallStateMissing | import("@agentxm/extension-workspace").McpSharedTargetConflict | import("@agentxm/extension-workspace").SkillDefinitionInvalid | import("@agentxm/extension-workspace").SkillMaterializationFailed | import("@agentxm/extension-workspace").SkillInstallStateMissing | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible | import("@agentxm/extension-workspace").PackDefinitionInvalid | import("@agentxm/extension-workspace").PackInstallStateMissing | import("@agentxm/extension-workspace").PackArchiveFetchFailed | import("@agentxm/extension-workspace").PackStagingFailed | import("@agentxm/extension-workspace").PackDependencyInvalid | import("@agentxm/extension-workspace").PackDependencyConflict | import("@agentxm/extension-workspace").PackConstraintShadowed | import("@agentxm/extension-workspace").PackDependencyMissing | import("@agentxm/extension-workspace").PackDependencyUnsatisfied | import("@agentxm/extension-workspace").KnowledgeDefinitionInvalid | import("@agentxm/extension-workspace").KnowledgeIoFailed | import("@agentxm/extension-workspace").KnowledgeInstallStateMissing | import("@agentxm/extension-workspace").KnowledgeResolutionMissing | import("@agentxm/extension-workspace").KnowledgeDesiredStateUnreconcilable | import("@agentxm/extension-workspace").KnowledgeUnavailable | import("@agentxm/extension-workspace").KnowledgeObservableContractViolated | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/extension-workspace").TransientBackupFailed | import("@agentxm/extension-workspace").WriteBackupRetained | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").LockfileIoError | import("@agentxm/workspace-state").LockfileParseError | import("@agentxm/workspace-state").LockfileDecodeError | import("@agentxm/workspace-state").LockfileVersionUnsupported | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").SettingsWriteError | import("@agentxm/workspace-state").WorkspaceSnapshotError | import("@agentxm/workspace-state").LockfileValidationError | import("@agentxm/workspace-state").LockfileWriteError | import("@agentxm/workspace-state").WorkspaceDirectoryError | import("@agentxm/workspace-state").TransitionLockError | import("@agentxm/workspace-state").TransitionLockUnavailable | import("@agentxm/workspace-state").WorkspaceTransitionCompromised | import("@agentxm/workspace-state").WorkspaceRestorationIncomplete | import("@agentxm/workspace-state").WorkspaceLayoutError | import("@agentxm/workspace-state").WorkspaceNotInitialized | import("@agentxm/workspace-state").LockedSkillMissing | import("@agentxm/workspace-state").SettingsEntryMissing | import("@agentxm/workspace-state").InvalidAgentId | import("@agentxm/workspace-state").DesiredPackGraphIncomplete | import("@agentxm/workspace-state").CanonicalPathRemovalError | import("@agentxm/workspace-state").SymlinkCreationError | import("@agentxm/workspace-state").LockEntrySourceMissing | import("@agentxm/workspace-state").LockEntryUrlInvalid | import("@agentxm/workspace-state").LockEntryNameInvalid | import("@agentxm/workspace-state").LockEntryEndpointConflict | import("@agentxm/workspace-state").LockEntrySourceTypeConflict | import("@agentxm/workspace-state").AcceptedResolutionMissing | import("@agentxm/workspace-state").InlineExtensionSourceMissing | import("@agentxm/workspace-state").SupersededCanonicalRemovalFailed | import("@agentxm/workspace-state").PackageContentHashFailed | import("@agentxm/workspace-state").WorkspaceSourceInvalid | import("@agentxm/workspace-state").SkillDiscoveryRootInvalid | import("@agentxm/workspace-state").SubagentScanFailed | import("@agentxm/workspace-state").LockfileResolvedVersionInvalid | import("@agentxm/extension-model/unstable/extensions").FqnInvalidError | import("@agentxm/registry-protocol/unstable/content/frontmatter").FrontmatterParseFailure | import("@agentxm/registry-protocol/unstable/content/subagent-content").SubagentContentError, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
81
83
  export declare const collectHooksStep: (args: {
82
84
  readonly facts: ReadonlyArray<ProjectionInvariantFact>;
83
85
  readonly adapter: SyncFailureAdapter;
@@ -85,7 +87,7 @@ export declare const collectHooksStep: (args: {
85
87
  export declare const collectInstructionStep: (args: {
86
88
  readonly projectionFacts: ReadonlyArray<ProjectionInvariantFact>;
87
89
  readonly adapter: SyncFailureAdapter;
88
- }) => Effect.Effect<Option.Option<PlannedJobStep<SyncStepRequirements>>, import("@agentxm/workspace-state").WorkspaceSettingsReadFailure, Path.Path | FileSystem.FileSystem | WorkspaceMutations | RuleManager>;
90
+ }) => Effect.Effect<Option.Option<PlannedJobStep<SyncStepRequirements>>, import("@agentxm/workspace-state").WorkspaceSettingsReadFailure, FileSystem.FileSystem | Path.Path | WorkspaceMutations | RuleManager>;
89
91
  export declare const makeSyncPlan: <R>({ materializeSteps, knowledgeStep, hooksStep, cleanupStep, instructionStep, releaseAge, serialMaterialization, name, description, }: {
90
92
  readonly materializeSteps: ReadonlyArray<PlannedJobStep<R>>;
91
93
  readonly knowledgeStep: Option.Option<PlannedJobStep<R>>;
package/dist/src/plan.js CHANGED
@@ -19,7 +19,7 @@ import * as Result from "effect/Result";
19
19
  import { CodingAgentRepository, HookManager, KnowledgeManager, RuleManager, applyPlannedProjections, inspectMcpServerAcrossAgents, observeInstructionProjection, projectionFactRequiresReconciliation, pruneManagedMcpServersForAgent, resolveInstructionsConfig, syncInlineMcpServerToAgents, assertInstructionTargetsSafe, assertInstructionsGitignoreSafe, instructionProjectionEffects, instructionProjectionIsCurrent, } from "@agentxm/extension-workspace";
20
20
  import { StepFailure, } from "@agentxm/workspace-operations";
21
21
  import { WorkspaceMutations } from "@agentxm/workspace-state";
22
- import { cleanupStaleManagedSkillDirectories, cleanupStaleManagedSubagentFiles, } from "./rendered-file-cleanup.js";
22
+ import { reconcileAgentOutputs } from "./rendered-file-cleanup.js";
23
23
  export const SYNC_RECOVERY_IDS = {
24
24
  packManifestDivergence: "pack:manifest-divergence",
25
25
  extensionConstraintMismatch: "extension:constraint-mismatch",
@@ -53,9 +53,7 @@ export const projectionFactsNeedReconciliation = (facts) => facts.some(projectio
53
53
  export const projectionDivergenceLabel = (label, facts) => {
54
54
  const violations = facts.filter(projectionFactRequiresReconciliation);
55
55
  const statuses = Array.from(new Set(violations.map(({ observation }) => observation.status))).join(", ");
56
- const contributors = Array.from(new Set(violations.flatMap(({ affectedContributors }) => affectedContributors)));
57
- const details = contributors.length === 0 ? statuses : `${statuses}: ${contributors.join(", ")}`;
58
- return details.length === 0 ? label : `${label} (${details})`;
56
+ return statuses.length === 0 ? label : `${label} (${statuses})`;
59
57
  };
60
58
  const managedRegionsForFacts = (facts) => facts.flatMap(({ subject }) => subject.owner === undefined
61
59
  ? []
@@ -188,7 +186,7 @@ export const collectKnowledgeStep = Effect.fn("Sync.collectKnowledgeStep")(funct
188
186
  };
189
187
  return Option.some({
190
188
  key: "knowledge:discovery",
191
- label: "Knowledge discovery",
189
+ label: projectionDivergenceLabel("Knowledge discovery", args.facts ?? []),
192
190
  readiness: "ready",
193
191
  artifact,
194
192
  ...(message.length === 0 ? {} : { message }),
@@ -215,21 +213,24 @@ export const collectKnowledgeStep = Effect.fn("Sync.collectKnowledgeStep")(funct
215
213
  });
216
214
  export const collectCleanupStep = Effect.fn("Sync.collectCleanupStep")(function* (args) {
217
215
  const ws = yield* WorkspaceMutations;
216
+ const agentRepo = yield* CodingAgentRepository;
217
+ const desiredAgentIds = new Set((yield* agentRepo.getMaterializationAgents()).map(({ id }) => id));
218
218
  const expectedSkillProjectionNames = new Set([
219
219
  ...args.expectedSkillNames,
220
220
  ...args.expectedSubagentNames,
221
221
  ]);
222
- const preview = yield* Effect.all([
223
- cleanupStaleManagedSkillDirectories({
224
- expectedSkillNames: expectedSkillProjectionNames,
225
- dryRun: true,
226
- }),
227
- cleanupStaleManagedSubagentFiles({
228
- expectedSubagentNames: args.expectedSubagentNames,
229
- dryRun: true,
230
- }),
231
- ]);
232
- const previewPaths = preview.flatMap(({ removedPaths }) => removedPaths);
222
+ const expectedNames = {
223
+ skill: expectedSkillProjectionNames,
224
+ subagent: args.expectedSubagentNames,
225
+ "mcp-server": args.expectedMcpServerNames,
226
+ hook: args.expectedHookNames,
227
+ };
228
+ const preview = yield* reconcileAgentOutputs({
229
+ desiredAgentIds,
230
+ expectedNames,
231
+ dryRun: true,
232
+ });
233
+ const previewPaths = preview.removedPaths;
233
234
  if (previewPaths.length === 0)
234
235
  return Option.none();
235
236
  return Option.some({
@@ -243,13 +244,8 @@ export const collectCleanupStep = Effect.fn("Sync.collectCleanupStep")(function*
243
244
  fileCount: previewPaths.length,
244
245
  targets: previewPaths.map((filePath) => ({ path: filePath, change: "removed" })),
245
246
  },
246
- run: Effect.all([
247
- cleanupStaleManagedSkillDirectories({
248
- expectedSkillNames: expectedSkillProjectionNames,
249
- }),
250
- cleanupStaleManagedSubagentFiles({ expectedSubagentNames: args.expectedSubagentNames }),
251
- ]).pipe(Effect.mapError(args.adapter.toStepFailure), Effect.map((results) => {
252
- const removedPaths = results.flatMap((result) => result.removedPaths);
247
+ run: reconcileAgentOutputs({ desiredAgentIds, expectedNames }).pipe(Effect.mapError(args.adapter.toStepFailure), Effect.map((result) => {
248
+ const removedPaths = result.removedPaths;
253
249
  return {
254
250
  result: "success",
255
251
  message: `Removed ${count(removedPaths.length, "stale managed agent projection")}`,
@@ -1,45 +1,27 @@
1
1
  /**
2
- * Destructive sweep of AXM-managed rendered files across agent surfaces:
3
- * stale skill and subagent projections, managed MCP server entries, and
4
- * managed hook groups. Read-only discovery primitives live in
5
- * `extension-workspace/managed-file-discovery.ts`.
2
+ * Destructive reconciliation of AXM-owned agent-native outputs. Read-only
3
+ * ownership and claimant discovery lives in `@agentxm/extension-workspace`.
6
4
  *
7
5
  * @experimental This API is unstable and may change without notice.
8
- * @packageDocumentation
9
6
  */
10
7
  import * as Effect from "effect/Effect";
11
8
  import * as FileSystem from "effect/FileSystem";
12
9
  import * as Path from "effect/Path";
13
- import { type WorkspaceSyncCleanupFailure } from "./errors.js";
14
10
  import { CodingAgentRepository, type WorkspaceOwnershipIssue } from "@agentxm/extension-workspace";
11
+ import type { PerAgentType } from "@agentxm/extension-model/unstable/extensions/common";
15
12
  import { WorkspaceMutations } from "@agentxm/workspace-state";
16
- export interface RenderedFileCleanupResult {
17
- readonly removedPaths: ReadonlyArray<string>;
18
- }
19
- export interface RemovedAgentArtifactCleanupResult {
13
+ import { type WorkspaceSyncCleanupFailure } from "./errors.js";
14
+ export interface ReconcileAgentOutputsResult {
20
15
  readonly removedPaths: ReadonlyArray<string>;
21
16
  readonly preservedPaths: ReadonlyArray<string>;
22
17
  }
23
- /** Remove AXM-owned skill projections whose package is no longer desired. */
24
- export declare const cleanupStaleManagedSkillDirectories: (args: {
25
- readonly expectedSkillNames: ReadonlySet<string>;
18
+ export interface ReconcileAgentOutputsArgs {
19
+ readonly desiredAgentIds: ReadonlySet<string>;
20
+ readonly expectedNames: Readonly<Record<PerAgentType, ReadonlySet<string>>>;
26
21
  readonly dryRun?: boolean;
27
- }) => Effect.Effect<RenderedFileCleanupResult, WorkspaceSyncCleanupFailure, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
28
- /**
29
- * Remove AXM-managed artifacts for agents that are no longer configured for a
30
- * workspace: rendered skill and subagent files, plus the agent's
31
- * managed MCP server entries and hook groups. Only content carrying an
32
- * AXM-managed signal is removed; user-authored files and entries are left
33
- * untouched.
34
- */
35
- export declare const cleanupManagedArtifactsForRemovedAgents: (args: {
36
- readonly removedAgentIds: ReadonlySet<string>;
37
- readonly dryRun?: boolean;
38
- }) => Effect.Effect<RemovedAgentArtifactCleanupResult, WorkspaceSyncCleanupFailure, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
22
+ }
23
+ /** Converge all AXM-owned per-agent outputs on desired workspace state. */
24
+ export declare const reconcileAgentOutputs: (args: ReconcileAgentOutputsArgs) => Effect.Effect<ReconcileAgentOutputsResult, WorkspaceSyncCleanupFailure, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
39
25
  /** Inspect ownership proofs without mutating any agent-native artifact. */
40
26
  export declare const inspectWorkspaceOwnership: () => Effect.Effect<ReadonlyArray<WorkspaceOwnershipIssue>, WorkspaceSyncCleanupFailure, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
41
- export declare const cleanupStaleManagedSubagentFiles: (args: {
42
- readonly expectedSubagentNames: ReadonlySet<string>;
43
- readonly dryRun?: boolean;
44
- }) => Effect.Effect<RenderedFileCleanupResult, WorkspaceSyncCleanupFailure, CodingAgentRepository | FileSystem.FileSystem | Path.Path | WorkspaceMutations>;
45
27
  //# sourceMappingURL=rendered-file-cleanup.d.ts.map
@@ -1,381 +1,119 @@
1
1
  /**
2
- * Destructive sweep of AXM-managed rendered files across agent surfaces:
3
- * stale skill and subagent projections, managed MCP server entries, and
4
- * managed hook groups. Read-only discovery primitives live in
5
- * `extension-workspace/managed-file-discovery.ts`.
2
+ * Destructive reconciliation of AXM-owned agent-native outputs. Read-only
3
+ * ownership and claimant discovery lives in `@agentxm/extension-workspace`.
6
4
  *
7
5
  * @experimental This API is unstable and may change without notice.
8
- * @packageDocumentation
9
6
  */
10
7
  import * as Effect from "effect/Effect";
11
8
  import * as FileSystem from "effect/FileSystem";
12
9
  import * as Path from "effect/Path";
13
- import { WorkspaceSyncFailed } from "./errors.js";
14
- import { CodingAgentRepository, pruneManagedMcpServersForAgent, extensionNameFromFilename, hasAxmManagedMarker, safeReadDirectory, safeReadFileString, readAmbiguousHookCommands, stripManagedHooksFromJson, } from "@agentxm/extension-workspace";
10
+ import { CodingAgentRepository, observeAgentOutputs, pruneManagedHooksFromJson, pruneManagedMcpServersForAgent, safeReadFileString, } from "@agentxm/extension-workspace";
15
11
  import { AGENTS as CAPABILITY_AGENTS } from "@agentxm/extension-model/unstable/agent-capabilities";
16
- import { PER_AGENT_EXTENSION_TYPES, } from "@agentxm/extension-model/unstable/extensions/common";
17
- import { ACQUIRED_EXTENSIONS_DIR } from "@agentxm/workspace-state";
18
- import { WorkspaceMutations } from "@agentxm/workspace-state";
19
- import { protectWorkspacePath } from "@agentxm/workspace-state";
20
- import { recordFootprint } from "@agentxm/workspace-state";
21
- const isWithin = (path, parent, child) => {
22
- const relative = path.relative(path.resolve(parent), path.resolve(child));
23
- return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
24
- };
25
- const removePath = (fs, filePath, dryRun) => dryRun
26
- ? Effect.void
27
- : protectWorkspacePath(filePath).pipe(Effect.andThen(fs.remove(filePath, { recursive: true })), Effect.andThen(recordFootprint({ path: filePath, change: "removed" })), Effect.mapError((error) => new WorkspaceSyncFailed({
28
- category: "internal",
29
- detail: `Failed to remove managed agent artifact: ${filePath}`,
30
- cause: error,
31
- })));
32
- const hasManagedSkillCopyMarker = (fs, path, artifactPath) => safeReadFileString(fs, path.join(artifactPath, "SKILL.md")).pipe(Effect.map(hasAxmManagedMarker));
33
- const cleanupSkillArtifactsInDir = (args) => Effect.gen(function* () {
34
- const removedPaths = [];
35
- const preservedPaths = [];
36
- const entries = yield* safeReadDirectory(args.fs, args.skillsDir);
37
- const configuredRoots = args.ownershipRoots ?? [
38
- args.path.join(args.baseDir, ACQUIRED_EXTENSIONS_DIR),
39
- ];
40
- const ownershipRoots = yield* Effect.forEach(configuredRoots, (root) => args.fs.realPath(root).pipe(Effect.orElseSucceed(() => root)));
41
- for (const entry of entries) {
42
- if (args.expectedNames?.has(entry) === true)
43
- continue;
44
- const artifactPath = args.path.join(args.skillsDir, entry);
45
- const linkTarget = yield* args.fs.readLink(artifactPath).pipe(Effect.option);
46
- if (linkTarget._tag === "Some") {
47
- const resolvedTarget = args.path.resolve(args.skillsDir, linkTarget.value);
48
- const canonicalTarget = yield* args.fs.realPath(resolvedTarget).pipe(Effect.option);
49
- const ownershipTarget = canonicalTarget._tag === "Some" ? canonicalTarget.value : resolvedTarget;
50
- if (!ownershipRoots.some((root) => isWithin(args.path, root, ownershipTarget))) {
51
- preservedPaths.push(artifactPath);
52
- continue;
53
- }
54
- yield* removePath(args.fs, artifactPath, args.dryRun);
55
- removedPaths.push(artifactPath);
56
- continue;
57
- }
58
- const stat = yield* args.fs.stat(artifactPath).pipe(Effect.option);
59
- if (stat._tag === "None")
60
- continue;
61
- if (stat.value.type !== "Directory") {
62
- preservedPaths.push(artifactPath);
63
- continue;
64
- }
65
- const managedCopy = yield* hasManagedSkillCopyMarker(args.fs, args.path, artifactPath);
66
- if (!managedCopy) {
67
- preservedPaths.push(artifactPath);
68
- continue;
69
- }
70
- yield* removePath(args.fs, artifactPath, args.dryRun);
71
- removedPaths.push(artifactPath);
72
- }
73
- return { removedPaths, preservedPaths };
74
- });
75
- /** Remove AXM-owned skill projections whose package is no longer desired. */
76
- export const cleanupStaleManagedSkillDirectories = (args) => Effect.gen(function* () {
77
- const fs = yield* FileSystem.FileSystem;
78
- const path = yield* Path.Path;
12
+ import { WorkspaceMutations, protectWorkspacePath, recordFootprint, } from "@agentxm/workspace-state";
13
+ import { WorkspaceSyncFailed } from "./errors.js";
14
+ const inventory = (args) => Effect.gen(function* () {
79
15
  const ws = yield* WorkspaceMutations;
80
- const agentRepo = yield* CodingAgentRepository;
81
- const configuredAgentIds = new Set(yield* ws.getConfiguredAgents());
82
- const agents = yield* agentRepo.all;
83
- const removedPaths = [];
84
- const ownershipRoots = ws.layout.scope === "project"
85
- ? [ws.layout.acquiredRoot, ws.layout.authoredRoot("skill")]
86
- : [ws.layout.acquiredRoot];
87
- for (const agent of agents) {
88
- if (!configuredAgentIds.has(agent.id))
89
- continue;
90
- const resolved = yield* agent.resolveEffectiveSkillsDir({ workspaceRoot: ws.baseDir });
91
- if (resolved._tag !== "supported")
92
- continue;
93
- const result = yield* cleanupSkillArtifactsInDir({
94
- fs,
95
- path,
96
- baseDir: ws.baseDir,
97
- skillsDir: resolved.dir,
98
- dryRun: args.dryRun === true,
99
- ownershipRoots,
100
- expectedNames: args.expectedSkillNames,
101
- });
102
- removedPaths.push(...result.removedPaths);
103
- }
104
- return { removedPaths: [...new Set(removedPaths)] };
105
- });
106
- const cleanupSubagentArtifactsInDir = (args) => Effect.gen(function* () {
107
- const removedPaths = [];
108
- const preservedPaths = [];
109
- const entries = yield* safeReadDirectory(args.fs, args.subagentsDir);
110
- for (const entry of entries) {
111
- const filePath = args.path.join(args.subagentsDir, entry);
112
- const stat = yield* args.fs.stat(filePath).pipe(Effect.option);
113
- if (stat._tag === "None" || stat.value.type !== "File")
114
- continue;
115
- const content = yield* safeReadFileString(args.fs, filePath);
116
- if (!hasAxmManagedMarker(content)) {
117
- preservedPaths.push(filePath);
118
- continue;
119
- }
120
- yield* removePath(args.fs, filePath, args.dryRun);
121
- removedPaths.push(filePath);
122
- }
123
- return { removedPaths, preservedPaths };
124
- });
125
- const NO_PATHS = { removedPaths: [], preservedPaths: [] };
126
- const cleanupAgentSkills = (context) => Effect.gen(function* () {
127
- const skillsDir = yield* context.agent.resolveEffectiveSkillsDir({
128
- workspaceRoot: context.workspaceRoot,
129
- });
130
- if (skillsDir._tag !== "supported")
131
- return NO_PATHS;
132
- return yield* cleanupSkillArtifactsInDir({
133
- fs: context.fs,
134
- path: context.path,
135
- baseDir: context.workspaceRoot,
136
- skillsDir: skillsDir.dir,
137
- dryRun: context.dryRun,
138
- ownershipRoots: context.skillOwnershipRoots,
139
- });
140
- });
141
- const cleanupAgentSubagents = (context) => Effect.gen(function* () {
142
- const subagentsDir = yield* context.agent.resolveEffectiveSubagentsDir({
143
- workspaceRoot: context.workspaceRoot,
144
- scope: context.scope,
145
- });
146
- if (subagentsDir._tag !== "supported")
147
- return NO_PATHS;
148
- return yield* cleanupSubagentArtifactsInDir({
149
- fs: context.fs,
150
- path: context.path,
151
- subagentsDir: subagentsDir.dir,
152
- dryRun: context.dryRun,
16
+ return yield* observeAgentOutputs({
17
+ workspaceRoot: ws.baseDir,
18
+ scope: ws.scope,
19
+ desiredAgentIds: args.desiredAgentIds,
20
+ expectedNames: args.expectedNames,
21
+ skillOwnershipRoots: ws.layout.scope === "project"
22
+ ? [ws.layout.acquiredRoot, ws.layout.authoredRoot("skill")]
23
+ : [ws.layout.acquiredRoot],
153
24
  });
154
25
  });
155
- /**
156
- * Drop every `x-axm`-tagged server from the agent's MCP config. An empty
157
- * declared set means "nothing should remain", so only managed entries go and
158
- * user-authored servers stay.
159
- */
160
- const cleanupAgentMcpServers = (context) => Effect.gen(function* () {
161
- const outcome = yield* pruneManagedMcpServersForAgent(context.agent.id, {
162
- workspaceRoot: context.workspaceRoot,
163
- declaredServerNames: new Set(),
164
- scope: context.scope,
165
- dryRun: context.dryRun,
166
- });
167
- if (outcome._tag !== "success")
168
- return NO_PATHS;
169
- return {
170
- removedPaths: (outcome.targets ?? []).map((target) => target.path),
171
- preservedPaths: [],
172
- };
26
+ const cleanupFailure = (detail, cause) => new WorkspaceSyncFailed({ category: "internal", detail, cause });
27
+ const removeOwnedFile = (fs, output) => protectWorkspacePath(output.path).pipe(Effect.andThen(fs.remove(output.path, { recursive: true })), Effect.andThen(recordFootprint({ path: output.path, change: "removed" })), Effect.mapError((error) => cleanupFailure(`Failed to remove managed agent artifact: ${output.path}`, error)));
28
+ const uniqueContainers = (outputs) => outputs.filter((output, index) => outputs.findIndex((candidate) => candidate.extensionType === output.extensionType &&
29
+ candidate.containerPath === output.containerPath) === index);
30
+ const desiredNamesForContainer = (output, args) => output.claimantAgentIds.some((agentId) => args.desiredAgentIds.has(agentId))
31
+ ? args.expectedNames[output.extensionType]
32
+ : new Set();
33
+ const pruneMcpContainer = (output, args, workspaceRoot, scope) => {
34
+ const claimant = output.claimantAgentIds[0];
35
+ if (claimant === undefined)
36
+ return Effect.void;
37
+ return pruneManagedMcpServersForAgent(claimant, {
38
+ workspaceRoot,
39
+ scope,
40
+ declaredServerNames: desiredNamesForContainer(output, args),
41
+ }).pipe(Effect.mapError((error) => cleanupFailure(`Failed to reconcile managed MCP entries in: ${output.containerPath}`, error)), Effect.asVoid);
42
+ };
43
+ const pruneHookContainer = (fs, path, output, args, workspaceRoot, scope) => Effect.gen(function* () {
44
+ const writer = CAPABILITY_AGENTS.flatMap((agent) => {
45
+ if (!output.claimantAgentIds.includes(agent.id))
46
+ return [];
47
+ const candidate = agent.capabilities.hook.axm.writer;
48
+ if (candidate === null)
49
+ return [];
50
+ const ownsPath = candidate.configFiles.some((file) => file.scope === scope &&
51
+ file.format === "json" &&
52
+ path.resolve(workspaceRoot, file.path) === output.containerPath);
53
+ return ownsPath ? [candidate] : [];
54
+ })[0];
55
+ if (writer === undefined)
56
+ return;
57
+ const raw = yield* safeReadFileString(fs, output.containerPath);
58
+ const next = yield* pruneManagedHooksFromJson(output.containerPath, writer.settingsKey, raw, desiredNamesForContainer(output, args)).pipe(Effect.mapError((error) => cleanupFailure(`Failed to reconcile managed hooks in: ${output.containerPath}`, error)));
59
+ if (next === raw)
60
+ return;
61
+ yield* protectWorkspacePath(output.containerPath).pipe(Effect.andThen(fs.writeFileString(output.containerPath, next)), Effect.andThen(recordFootprint({ path: output.containerPath, change: "modified" })), Effect.mapError((error) => cleanupFailure(`Failed to write reconciled hooks to: ${output.containerPath}`, error)));
173
62
  });
174
- /**
175
- * Strip AXM-rendered hook groups from the agent's settings files. Edits go
176
- * through jsonc-parser so user-authored groups, comments, and formatting in
177
- * these user-owned files survive.
178
- */
179
- const cleanupAgentHooks = (context) => Effect.gen(function* () {
180
- const capabilityAgent = CAPABILITY_AGENTS.find((candidate) => candidate.id === context.agent.id);
181
- const writer = capabilityAgent?.capabilities.hook.axm.writer;
182
- if (writer === undefined || writer === null)
183
- return NO_PATHS;
184
- const removedPaths = [];
185
- const configFiles = writer.configFiles.filter((file) => file.scope === "project" && file.format === "json");
186
- for (const file of configFiles) {
187
- const configPath = context.path.resolve(context.workspaceRoot, file.path);
188
- const exists = yield* context.fs
189
- .exists(configPath)
190
- .pipe(Effect.catch(() => Effect.succeed(false)));
191
- if (!exists)
192
- continue;
193
- const raw = yield* safeReadFileString(context.fs, configPath);
194
- const next = yield* stripManagedHooksFromJson(configPath, writer.settingsKey, raw);
195
- if (next === raw)
196
- continue;
197
- if (!context.dryRun) {
198
- yield* protectWorkspacePath(configPath);
199
- yield* context.fs.writeFileString(configPath, next).pipe(Effect.mapError((error) => new WorkspaceSyncFailed({
200
- category: "internal",
201
- detail: `Failed to strip managed hooks from: ${configPath}`,
202
- cause: error,
203
- })));
204
- }
205
- removedPaths.push(configPath);
63
+ /** Converge all AXM-owned per-agent outputs on desired workspace state. */
64
+ export const reconcileAgentOutputs = (args) => Effect.gen(function* () {
65
+ const before = yield* inventory(args);
66
+ const candidates = before.ownedResidue;
67
+ const preservedPaths = [...new Set(before.unownedFootprints.map(({ path }) => path))].sort();
68
+ if (args.dryRun === true) {
69
+ return {
70
+ removedPaths: [...new Set(candidates.map(({ path }) => path))].sort(),
71
+ preservedPaths,
72
+ };
206
73
  }
207
- return { removedPaths, preservedPaths: [] };
208
- });
209
- /**
210
- * Cleanup keyed on the placement axis: every extension type that renders into a
211
- * directory the agent owns needs removal behavior here, and adding a per-agent
212
- * type fails to compile until that behavior is decided.
213
- *
214
- * Workspace-placed types are deliberately absent rather than mapped to a no-op.
215
- * Workspace-placed extensions live in shared workspace content and are not
216
- * keyed to a single agent, so removing one agent must not delete them.
217
- */
218
- const cleanupByExtensionType = {
219
- skill: cleanupAgentSkills,
220
- subagent: cleanupAgentSubagents,
221
- "mcp-server": cleanupAgentMcpServers,
222
- hook: cleanupAgentHooks,
223
- };
224
- /**
225
- * Remove AXM-managed artifacts for agents that are no longer configured for a
226
- * workspace: rendered skill and subagent files, plus the agent's
227
- * managed MCP server entries and hook groups. Only content carrying an
228
- * AXM-managed signal is removed; user-authored files and entries are left
229
- * untouched.
230
- */
231
- export const cleanupManagedArtifactsForRemovedAgents = (args) => Effect.gen(function* () {
232
74
  const fs = yield* FileSystem.FileSystem;
233
75
  const path = yield* Path.Path;
234
76
  const ws = yield* WorkspaceMutations;
235
- const agentRepo = yield* CodingAgentRepository;
236
- const agents = yield* agentRepo.all;
237
- const removedPaths = [];
238
- const preservedPaths = [];
239
- for (const agent of agents) {
240
- if (!args.removedAgentIds.has(agent.id))
241
- continue;
242
- const context = {
243
- fs,
244
- path,
245
- agent,
246
- workspaceRoot: ws.baseDir,
247
- scope: ws.scope,
248
- skillOwnershipRoots: ws.layout.scope === "project"
249
- ? [ws.layout.acquiredRoot, ws.layout.authoredRoot("skill")]
250
- : [ws.layout.acquiredRoot],
251
- dryRun: args.dryRun === true,
252
- };
253
- for (const type of PER_AGENT_EXTENSION_TYPES) {
254
- const result = yield* cleanupByExtensionType[type](context);
255
- removedPaths.push(...result.removedPaths);
256
- preservedPaths.push(...result.preservedPaths);
77
+ for (const output of candidates) {
78
+ if (output.extensionType === "skill" || output.extensionType === "subagent") {
79
+ yield* removeOwnedFile(fs, output);
257
80
  }
258
81
  }
259
- return {
260
- removedPaths: [...new Set(removedPaths)],
261
- preservedPaths: [...new Set(preservedPaths)],
262
- };
82
+ for (const output of uniqueContainers(candidates.filter(({ extensionType }) => extensionType === "mcp-server"))) {
83
+ yield* pruneMcpContainer(output, args, ws.baseDir, ws.scope);
84
+ }
85
+ for (const output of uniqueContainers(candidates.filter(({ extensionType }) => extensionType === "hook"))) {
86
+ yield* pruneHookContainer(fs, path, output, args, ws.baseDir, ws.scope);
87
+ }
88
+ const after = yield* inventory(args);
89
+ const remaining = new Set(after.outputs.map((output) => `${output.extensionType}\u0000${output.path}\u0000${output.entryName}`));
90
+ const removedPaths = candidates
91
+ .filter((output) => !remaining.has(`${output.extensionType}\u0000${output.path}\u0000${output.entryName}`))
92
+ .map(({ path: outputPath }) => outputPath);
93
+ return { removedPaths: [...new Set(removedPaths)].sort(), preservedPaths };
263
94
  });
264
95
  /** Inspect ownership proofs without mutating any agent-native artifact. */
265
96
  export const inspectWorkspaceOwnership = () => Effect.gen(function* () {
266
- const fs = yield* FileSystem.FileSystem;
267
- const path = yield* Path.Path;
268
97
  const ws = yield* WorkspaceMutations;
269
- const agentRepo = yield* CodingAgentRepository;
270
98
  const configured = new Set(yield* ws.getConfiguredAgents());
271
- const agents = yield* agentRepo.all;
272
- const issues = [];
273
- for (const agent of agents) {
274
- if (!configured.has(agent.id))
275
- continue;
276
- const skillsDir = yield* agent.resolveEffectiveSkillsDir({ workspaceRoot: ws.baseDir });
277
- if (skillsDir._tag === "supported") {
278
- const result = yield* cleanupSkillArtifactsInDir({
279
- fs,
280
- path,
281
- baseDir: ws.baseDir,
282
- skillsDir: skillsDir.dir,
283
- dryRun: true,
284
- ownershipRoots: ws.layout.scope === "project"
285
- ? [ws.layout.acquiredRoot, ws.layout.authoredRoot("skill")]
286
- : [ws.layout.acquiredRoot],
287
- });
288
- issues.push(...result.preservedPaths.map((artifactPath) => ({
289
- kind: "managed-file-unowned",
290
- path: artifactPath,
291
- detail: "Agent skill artifact has no AXM symlink or structured file ownership proof.",
292
- })));
293
- }
294
- const subagentsDir = yield* agent.resolveEffectiveSubagentsDir({
295
- workspaceRoot: ws.baseDir,
296
- scope: ws.scope,
297
- });
298
- if (subagentsDir._tag === "supported") {
299
- const result = yield* cleanupSubagentArtifactsInDir({
300
- fs,
301
- path,
302
- subagentsDir: subagentsDir.dir,
303
- dryRun: true,
304
- });
305
- issues.push(...result.preservedPaths.map((artifactPath) => ({
306
- kind: "managed-file-unowned",
307
- path: artifactPath,
308
- detail: "Agent subagent artifact has no structured file ownership proof.",
309
- })));
310
- }
311
- const capabilityAgent = CAPABILITY_AGENTS.find((candidate) => candidate.id === agent.id);
312
- const writer = capabilityAgent?.capabilities.hook.axm.writer;
313
- if (writer === undefined || writer === null)
314
- continue;
315
- for (const file of writer.configFiles.filter((candidate) => candidate.scope === ws.scope && candidate.format === "json")) {
316
- const configPath = path.resolve(ws.baseDir, file.path);
317
- if (!(yield* fs.exists(configPath).pipe(Effect.catch(() => Effect.succeed(false)))))
318
- continue;
319
- const raw = yield* safeReadFileString(fs, configPath);
320
- const commands = yield* readAmbiguousHookCommands(configPath, writer.settingsKey, raw);
321
- issues.push(...commands.map((command) => ({
322
- kind: "hook-ownership-ambiguous",
323
- path: configPath,
324
- detail: `Hook command targets an AXM canonical extension path without x-axm ownership metadata: ${command}`,
325
- })));
326
- }
327
- }
328
- return issues.filter((issue, index) => issues.findIndex((candidate) => candidate.kind === issue.kind &&
329
- candidate.path === issue.path &&
330
- candidate.detail === issue.detail) === index);
331
- });
332
- export const cleanupStaleManagedSubagentFiles = (args) => Effect.gen(function* () {
333
- const fs = yield* FileSystem.FileSystem;
334
- const path = yield* Path.Path;
335
- const ws = yield* WorkspaceMutations;
336
- const agentRepo = yield* CodingAgentRepository;
337
- const configuredAgentIds = new Set(yield* ws.getConfiguredAgents());
338
- const agents = yield* agentRepo.all;
339
- const removedPaths = [];
340
- for (const agent of agents) {
341
- const resolved = yield* agent.resolveEffectiveSubagentsDir({
342
- workspaceRoot: ws.baseDir,
343
- scope: ws.scope,
344
- });
345
- if (resolved._tag !== "supported")
346
- continue;
347
- const exists = yield* fs.exists(resolved.dir).pipe(Effect.catch(() => Effect.succeed(false)));
348
- if (!exists)
349
- continue;
350
- const entries = yield* fs
351
- .readDirectory(resolved.dir)
352
- .pipe(Effect.catch(() => Effect.succeed([])));
353
- for (const entry of entries) {
354
- const filePath = path.join(resolved.dir, entry);
355
- const stat = yield* fs.stat(filePath).pipe(Effect.option);
356
- if (stat._tag === "None" || stat.value.type !== "File")
357
- continue;
358
- const content = yield* fs
359
- .readFileString(filePath)
360
- .pipe(Effect.catch(() => Effect.succeed("")));
361
- if (!hasAxmManagedMarker(content))
362
- continue;
363
- const expected = configuredAgentIds.has(agent.id) &&
364
- args.expectedSubagentNames.has(extensionNameFromFilename(entry));
365
- if (expected)
366
- continue;
367
- if (args.dryRun !== true) {
368
- yield* protectWorkspacePath(filePath);
369
- yield* fs.remove(filePath).pipe(Effect.mapError((error) => new WorkspaceSyncFailed({
370
- category: "internal",
371
- detail: `Failed to remove stale managed subagent file: ${filePath}`,
372
- cause: error,
373
- })));
374
- yield* recordFootprint({ path: filePath, change: "removed" });
375
- }
376
- removedPaths.push(filePath);
377
- }
378
- }
379
- return { removedPaths };
99
+ const empty = new Set();
100
+ const observed = yield* inventory({
101
+ desiredAgentIds: configured,
102
+ expectedNames: {
103
+ skill: empty,
104
+ subagent: empty,
105
+ "mcp-server": empty,
106
+ hook: empty,
107
+ },
108
+ });
109
+ return observed.unownedFootprints.map((output) => ({
110
+ kind: output.extensionType === "hook"
111
+ ? "hook-ownership-ambiguous"
112
+ : "managed-file-unowned",
113
+ path: output.path,
114
+ detail: output.extensionType === "hook"
115
+ ? `Hook command targets an AXM canonical extension path without x-axm ownership metadata: ${output.entryName}`
116
+ : `Agent ${output.extensionType} artifact has no AXM ownership proof.`,
117
+ }));
380
118
  });
381
119
  //# sourceMappingURL=rendered-file-cleanup.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentxm/workspace-sync",
3
- "version": "0.28.4-bootstrap.0",
3
+ "version": "0.28.5",
4
4
  "description": "AXM workspace-sync feature: desired-state reconciliation planning, projection realization, and reconciliation outcomes for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-MIT",
@@ -18,6 +18,7 @@
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/src/index.d.ts",
21
+ "axm-source": "./src/index.ts",
21
22
  "default": "./dist/src/index.js"
22
23
  }
23
24
  },
@@ -37,11 +38,11 @@
37
38
  "dependencies": {
38
39
  "effect": "4.0.0-rc.112",
39
40
  "semver": "^7.8.5",
40
- "@agentxm/extension-model": "^0.28.4-bootstrap.0",
41
- "@agentxm/extension-workspace": "^0.28.4-bootstrap.0",
42
- "@agentxm/registry-protocol": "^0.28.4-bootstrap.0",
43
- "@agentxm/workspace-state": "^0.28.4-bootstrap.0",
44
- "@agentxm/workspace-operations": "^0.28.4-bootstrap.0"
41
+ "@agentxm/workspace-operations": "^0.28.5",
42
+ "@agentxm/extension-model": "^0.28.5",
43
+ "@agentxm/registry-protocol": "^0.28.5",
44
+ "@agentxm/workspace-state": "^0.28.5",
45
+ "@agentxm/extension-workspace": "^0.28.5"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@effect/platform-node": "4.0.0-rc.112",