@agentxm/extension-materialization 0.29.4 → 0.30.1

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.
@@ -1,8 +1,3 @@
1
- /**
2
- * Hook manager service.
3
- *
4
- * @experimental This API is unstable and may change without notice.
5
- */
6
1
  import * as FileSystem from "effect/FileSystem";
7
2
  import * as Layer from "effect/Layer";
8
3
  import * as Path from "effect/Path";
@@ -1,3 +1,5 @@
1
+ import { usableAcceptedCanonical } from "@agentxm/workspace-state";
2
+ import { LifecyclePostconditionViolated } from "../extensions/errors.js";
1
3
  /**
2
4
  * Hook manager service.
3
5
  *
@@ -511,13 +513,20 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
511
513
  .map(({ marker }) => marker),
512
514
  };
513
515
  });
514
- const makeHookProjectionPlans = () => Effect.gen(function* () {
516
+ const makeHookProjectionPlans = (prospective = []) => Effect.gen(function* () {
515
517
  const configuredAgents = yield* ws.getConfiguredAgents();
516
518
  const targets = yield* configuredHookWriterTargets(configuredAgents, (configPath) => path.resolve(baseDir, configPath));
517
519
  const fallbackTarget = yield* hookFallbackTarget();
518
520
  const graph = yield* ws.getDesiredStateGraph();
519
521
  const locked = yield* ws.getLockedHooks();
520
- const contributors = yield* selectHookContributors({ graph, locked });
522
+ const retained = yield* selectHookContributors({
523
+ graph: {
524
+ ...graph,
525
+ nodes: graph.nodes.filter((node) => node.type !== "hook" || !prospective.some(({ name }) => name === node.name)),
526
+ },
527
+ locked,
528
+ });
529
+ const contributors = [...retained, ...prospective].sort((a, b) => a.marker.localeCompare(b.marker));
521
530
  const outcomes = evaluateConfiguredOutcomes({
522
531
  configuredAgents,
523
532
  targets,
@@ -590,11 +599,11 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
590
599
  });
591
600
  return [...nativePlans, fallbackPlan];
592
601
  });
593
- const configuredAgentOutcomes = (state) => Effect.gen(function* () {
602
+ const configuredAgentOutcomes = (state, proposedGraph) => Effect.gen(function* () {
594
603
  const configuredAgents = yield* ws.getConfiguredAgents();
595
604
  const targets = yield* configuredHookWriterTargets(configuredAgents, (configPath) => path.resolve(baseDir, configPath));
596
605
  const fallbackTarget = yield* hookFallbackTarget();
597
- const graph = yield* ws.getDesiredStateGraph();
606
+ const graph = proposedGraph ?? (yield* ws.getDesiredStateGraph());
598
607
  const locked = yield* ws.getLockedHooks();
599
608
  const contributors = yield* selectHookContributors({ graph, locked });
600
609
  return evaluateConfiguredOutcomes({
@@ -625,6 +634,45 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
625
634
  state,
626
635
  });
627
636
  });
637
+ const prepareProjection = (refs) => Effect.gen(function* () {
638
+ const prepared = yield* Effect.forEach(refs, (ref) => Effect.scoped(Effect.gen(function* () {
639
+ const root = ref.refType === "registry"
640
+ ? (yield* sources.fetch(ref)).directory
641
+ : stripFileProtocol(ref.location);
642
+ const manifest = yield* readManifest(root);
643
+ const entrypoint = path.resolve(root, manifest.entrypoint);
644
+ yield* validatePathSafety(path, root, entrypoint);
645
+ if (!(yield* fs.exists(entrypoint)))
646
+ return yield* new HookDefinitionInvalid({
647
+ detail: `Hook entrypoint does not exist: ${manifest.entrypoint}`,
648
+ });
649
+ const canonicalPath = computeExtensionPathsForLayout(path.join, ws.layout, ref, HOOK_EXTENSION_DIR, ref.hook.name).canonicalPath;
650
+ const commandPath = path.relative(baseDir, path.resolve(canonicalPath, manifest.entrypoint));
651
+ return {
652
+ name: ref.hook.name,
653
+ marker: formatFqn({ owner: manifest.owner, type: "hook", name: manifest.name }),
654
+ manifest,
655
+ command: `${interpreterForRuntime(manifest.runtime)} ${commandPath}`,
656
+ treeIntegrity: yield* computeMaterializedTreeIntegrity(root),
657
+ };
658
+ })), { concurrency: 1 });
659
+ const configuredAgents = yield* ws.getConfiguredAgents();
660
+ const targets = yield* configuredHookWriterTargets(configuredAgents, (configPath) => path.resolve(baseDir, configPath));
661
+ const fallbackTarget = yield* hookFallbackTarget();
662
+ return {
663
+ plans: yield* makeHookProjectionPlans(prepared),
664
+ agentOutcomes: evaluateConfiguredOutcomes({
665
+ configuredAgents,
666
+ targets,
667
+ fallbackPath: fallbackTarget.workspaceRelative,
668
+ contributors: prepared,
669
+ state: "projected",
670
+ }),
671
+ acquisitions: prepared.map(({ name, treeIntegrity }) => ({ name, treeIntegrity })),
672
+ };
673
+ }).pipe(Effect.mapError((cause) => cause._tag === "PlatformError"
674
+ ? new HookDefinitionInvalid({ detail: "Cannot inspect prepared hook content", cause })
675
+ : cause));
628
676
  const projectionPlans = () => makeHookProjectionPlans();
629
677
  const applyHookProjections = projectionPlans().pipe(Effect.flatMap(applyProjectionPlans));
630
678
  const materializeInstall = Effect.fn("HookManager.materializeInstall")(function* ({ ref }) {
@@ -705,11 +753,28 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
705
753
  return {
706
754
  type: "hook",
707
755
  projectionPlans,
756
+ prepareProjection,
708
757
  aggregateProjectionObservation: Ref.get(lastProjection),
709
758
  configuredAgentOutcomes,
710
759
  configuredAgentOutcomesForRef,
711
760
  isInstalled: ({ target }) => isObservedInstalled(ws, "hook", target.name).pipe(Effect.withSpan("HookManager.isInstalled")),
712
761
  materializeInstall,
762
+ acquireCanonical: materializeInstall,
763
+ materializeRetained: ({ target }) => Effect.gen(function* () {
764
+ const canonical = yield* usableAcceptedCanonical({
765
+ workspace: ws,
766
+ type: "hook",
767
+ name: target.name,
768
+ });
769
+ if (Option.isNone(canonical) || canonical.value.ref.type !== "hook") {
770
+ return yield* new LifecyclePostconditionViolated({
771
+ postcondition: "materialize-observable",
772
+ targetType: "hook",
773
+ targetName: target.name,
774
+ });
775
+ }
776
+ return yield* materializeInstall({ ref: canonical.value.ref });
777
+ }),
713
778
  prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
714
779
  workspace: ws,
715
780
  type: "hook",
@@ -734,45 +799,17 @@ export const HookManagerLive = Layer.effect(HookManager, Effect.gen(function* ()
734
799
  }),
735
800
  materializeUninstall,
736
801
  materializeDeactivate,
737
- upsertSettingsEntry: Effect.fn("HookManager.upsertSettingsEntry")(function* ({ ref, versionRange, materialization, }) {
738
- const lockEntry = yield* buildLockEntry(ref, materialization);
739
- if (Option.isNone(lockEntry)) {
740
- yield* ws.setHookEntry(ref.hook.name, {
741
- source: "workspace",
742
- enabled: true,
743
- });
744
- return;
745
- }
746
- if (lockEntry.value.type === "registry") {
747
- yield* validateExactResolvedVersion(`hooks.${ref.hook.name}.resolvedVersion`, lockEntry.value.resolvedVersion);
748
- }
749
- yield* ws.setHook({
750
- name: ref.hook.name,
751
- lockEntry: lockEntry.value,
752
- versionRange,
753
- });
754
- }),
755
- removeSettingsEntry: Effect.fn("HookManager.removeSettingsEntry")(function* ({ target }) {
756
- yield* ws.removeHookSettings(target.name);
757
- }),
758
- upsertLockfileEntry: Effect.fn("HookManager.upsertLockfileEntry")(function* ({ ref, materialization, }) {
802
+ acceptedResolution: Effect.fn("HookManager.acceptedResolution")(function* ({ ref, materialization, }) {
759
803
  const entry = yield* buildLockEntry(ref, materialization);
760
804
  if (Option.isNone(entry)) {
761
- yield* ws.removeHookLock(ref.hook.name);
762
- return;
805
+ return Option.none();
763
806
  }
764
807
  if (ref.refType === "registry") {
765
808
  yield* validateExactResolvedVersion(`hooks.${ref.hook.name}.resolvedVersion`, ref.version);
766
809
  }
767
- yield* ws.setHookLock({
768
- name: ref.hook.name,
769
- lockEntry: entry.value,
770
- versionRange: Option.none(),
771
- });
772
- }),
773
- removeLockfileEntry: Effect.fn("HookManager.removeLockfileEntry")(function* ({ target }) {
774
- yield* ws.removeHookLock(target.name);
810
+ return Option.some({ key: ref.hook.name, entry: entry.value });
775
811
  }),
812
+ withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
776
813
  };
777
814
  }));
778
815
  //# sourceMappingURL=manager.js.map
@@ -13,7 +13,7 @@
13
13
  */
14
14
  export { NO_MATERIALIZATION_OBSERVATION, type ExtensionManager, type ManagerRequirements, type MaterializationFacts, type MaterializationObservation, } from "./manager-contract.js";
15
15
  export { ExtensionManagers, type ExtensionManagersService } from "./manager-registry.js";
16
- export { HookManager, KnowledgeManager, McpServerManager, PackManager, RuleManager, SkillManager, SubagentManager, type AcquiredContentFacts, type HookManagerService, type HookMaterializationFacts, type KnowledgeManagerService, type KnowledgeMaterializationFacts, type KnowledgeSyncResult, type McpServerMaterializationFacts, type McpServerManagerService, type PackMaterializationFacts, type RuleManagerService, type RuleMaterializationFacts, type SkillMaterializationFacts, type SubagentManagerService, type SubagentMaterializationFacts, } from "./managers.js";
16
+ export { HookManager, KnowledgeManager, McpServerManager, PackManager, RuleManager, SkillManager, SubagentManager, type AcquiredContentFacts, type HookManagerService, type PreparedHookProjection, type HookMaterializationFacts, type KnowledgeManagerService, type KnowledgeMaterializationFacts, type KnowledgeSyncResult, type McpServerMaterializationFacts, type McpServerManagerService, type PackMaterializationFacts, type RuleManagerService, type RuleMaterializationFacts, type SkillMaterializationFacts, type SubagentManagerService, type SubagentMaterializationFacts, } from "./managers.js";
17
17
  export type { ExtensionManagerFailure, ExtensionMaterializationError } from "./errors.js";
18
18
  export { ArchiveIntegrityMismatch, CanonicalPackageProbeFailed, CreateDestinationExists, LifecyclePostconditionViolated, PackageCopyFailed, PackageMaterializationFailed, ScaffoldedExtensionUnresolved, StagedPackageInvalid, type MaterializationError, } from "./extensions/errors.js";
19
19
  export { HookDefinitionInvalid, HookInstallStateMissing, type HookManagerError, } from "./hooks/errors.js";
@@ -23,8 +23,6 @@ export { SubagentContentUnreadable, SubagentDefinitionInvalid, SubagentInstallSt
23
23
  export { SkillDefinitionInvalid, SkillInstallStateMissing, SkillMaterializationFailed, type SkillManagerError, } from "./skills/errors.js";
24
24
  export { PackArchiveFetchFailed, PackDefinitionInvalid, PackInstallStateMissing, PackStagingFailed, type PackManagerError, } from "./packs/errors.js";
25
25
  export { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, type KnowledgeManagerError, } from "./knowledge/errors.js";
26
- export { collectSecretInputNames, deleteMcpSecrets, installMcpServer, readMcpServerManifest, type InstallMcpServerOperation, type InstallMcpServerOperationArgs, type McpSecretDeletionOutcome, type McpServerInstallRequirements, } from "./mcps/install-operation.js";
27
- export { materializeAuthoredMcpServer } from "./mcps/authored-materialization.js";
28
26
  export { NativeMcpEntryRetirementFailed, retireNativeMcpEntry, type NativeMcpEntryRef, } from "./mcps/native-entry.js";
29
27
  export { MCP_SECRET_SERVICE, McpSecretStore, mcpSecretAccount, type McpSecretEraseOutcome, type McpSecretIdentity, type McpSecretStoreService, type McpSecretWriteOutcome, } from "./mcps/secret-store.js";
30
28
  export { MCP_AGENT_CONFIG_SURFACE, agentConfigTarget, agentConfigTargets, mcpConfigSurface, mcpServerArtifact, mcpServerSourcePath, mcpServerVersion, mcpSettingsTarget, mcpSourceTarget, type AgentMcpConfigOutcome, } from "./mcps/artifact.js";
@@ -37,8 +35,5 @@ export { shouldReuseCanonicalInstall } from "./extensions/canonical-reuse.js";
37
35
  export { configuredMcpServersToDiskRefs, configuredPacksToDiskRefs, configuredSkillsToDiskRefs, configuredSubagentsToDiskRefs, } from "./extensions/materializable-from-disk.js";
38
36
  export { canReuseExternalPackage, canReuseInstalledPackage, canonicalMaterializationPaths, createCanonicalDirectory, materializeExternalPackage, materializeExternalPackageWithTreeIntegrity, recoverCanonicalDirectory, replaceCanonicalDirectory, replaceCanonicalDirectoryWithInspection, type CanReuseExternalPackageArgs, type CanReuseInstalledPackageArgs, type CanonicalDirectoryInspection, type CanonicalDirectoryReplacementError, type CreateCanonicalDirectoryArgs, type MaterializeExternalPackageArgs, type MaterializedPackage, type RecoverCanonicalDirectoryArgs, type ReplaceCanonicalDirectoryArgs, type ReplaceCanonicalDirectoryWithInspectionArgs, } from "./extensions/canonical-directory.js";
39
37
  export { materializeRegistryPackage, materializeRegistryPackageWithTreeIntegrity, type MaterializeRegistryPackageArgs, type RegistryPackageMaterializationMessages, } from "./registry-materialization.js";
40
- export { buildAuthoredExtensionStep, buildInstallOperation, buildMaterializeOperation, buildNewExtensionStep, buildUninstallOperation, extensionRefLifecycleWarnings, extensionRefRegistryLifecycle, formatPackageUrlParts, targetFromRef, toLabel, toLabelWithCompanions, toStepKey, type AuthoredExtensionOperationArgs, type CallerStepFailure, type InstallOperationArgs, type MaterializeOperationArgs, type NewExtensionOperationArgs, type RecipeRequirements, type StepFailureAdapter, type UninstallOperationArgs, type UninstallRetentionPolicy, type UninstallSettlement, type UnreadablePackageRetirement, } from "./extensions/operations.js";
41
- export { RetainedContentUnusable } from "./desired-state/errors.js";
42
- export { collectRetainedMaterializeSteps, type RetainedMaterializeFailure, type RetainedMaterializeRequirements, type RetainedMaterializeSteps, type RunRetainedMcpServerInstall, } from "./desired-state/retained-materialization.js";
43
38
  export { projectionErrorToStepFailure } from "./projection-step-failure.js";
44
39
  //# sourceMappingURL=index.d.ts.map
package/dist/src/index.js CHANGED
@@ -28,8 +28,6 @@ export { PackArchiveFetchFailed, PackDefinitionInvalid, PackInstallStateMissing,
28
28
  export { KnowledgeDefinitionInvalid, KnowledgeDesiredStateUnreconcilable, KnowledgeInstallStateMissing, KnowledgeIoFailed, KnowledgeObservableContractViolated, KnowledgeResolutionMissing, KnowledgeUnavailable, } from "./knowledge/errors.js";
29
29
  // MCP server installation: the operation four surfaces share, its credential
30
30
  // port, and the artifact/target vocabulary the plan step reports.
31
- export { collectSecretInputNames, deleteMcpSecrets, installMcpServer, readMcpServerManifest, } from "./mcps/install-operation.js";
32
- export { materializeAuthoredMcpServer } from "./mcps/authored-materialization.js";
33
31
  export { NativeMcpEntryRetirementFailed, retireNativeMcpEntry, } from "./mcps/native-entry.js";
34
32
  export { MCP_SECRET_SERVICE, McpSecretStore, mcpSecretAccount, } from "./mcps/secret-store.js";
35
33
  export { MCP_AGENT_CONFIG_SURFACE, agentConfigTarget, agentConfigTargets, mcpConfigSurface, mcpServerArtifact, mcpServerSourcePath, mcpServerVersion, mcpSettingsTarget, mcpSourceTarget, } from "./mcps/artifact.js";
@@ -45,12 +43,5 @@ export { configuredMcpServersToDiskRefs, configuredPacksToDiskRefs, configuredSk
45
43
  export { canReuseExternalPackage, canReuseInstalledPackage, canonicalMaterializationPaths, createCanonicalDirectory, materializeExternalPackage, materializeExternalPackageWithTreeIntegrity, recoverCanonicalDirectory, replaceCanonicalDirectory, replaceCanonicalDirectoryWithInspection, } from "./extensions/canonical-directory.js";
46
44
  // Registry-backed acquisition
47
45
  export { materializeRegistryPackage, materializeRegistryPackageWithTreeIntegrity, } from "./registry-materialization.js";
48
- // Closure recipes
49
- export { buildAuthoredExtensionStep, buildInstallOperation, buildMaterializeOperation, buildNewExtensionStep, buildUninstallOperation, extensionRefLifecycleWarnings, extensionRefRegistryLifecycle, formatPackageUrlParts, targetFromRef, toLabel, toLabelWithCompanions, toStepKey, } from "./extensions/operations.js";
50
- // Re-materializing content the workspace already accepted. Both the sync
51
- // sweep and Pack activation restore retained members, and neither feature may
52
- // import the other.
53
- export { RetainedContentUnusable } from "./desired-state/errors.js";
54
- export { collectRetainedMaterializeSteps, } from "./desired-state/retained-materialization.js";
55
46
  export { projectionErrorToStepFailure } from "./projection-step-failure.js";
56
47
  //# sourceMappingURL=index.js.map
@@ -1,4 +1,3 @@
1
- /** Lifecycle manager for isolated Open Knowledge Format bundles. */
2
1
  import * as FileSystem from "effect/FileSystem";
3
2
  import * as Layer from "effect/Layer";
4
3
  import * as Path from "effect/Path";
@@ -1,3 +1,5 @@
1
+ import { usableAcceptedCanonical } from "@agentxm/workspace-state";
2
+ import { LifecyclePostconditionViolated } from "../extensions/errors.js";
1
3
  // @effect-diagnostics anyUnknownInErrorContext:off — schema and filesystem errors are swept into KnowledgeIoFailed inside this manager
2
4
  /** Lifecycle manager for isolated Open Knowledge Format bundles. */
3
5
  import * as Effect from "effect/Effect";
@@ -628,6 +630,19 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
628
630
  // Deactivation retains canonical content; the caller updates settings
629
631
  // first, so re-rendering the whole region drops this bundle's routing.
630
632
  const materializeDeactivate = Effect.fn("KnowledgeManager.materializeDeactivate")(() => applyKnowledgeProjection.pipe(Effect.as(withdrawn)));
633
+ const acquireCanonical = Effect.fn("KnowledgeManager.materializeInstall")(function* ({ ref, force }) {
634
+ const relativeLocalSource = ref.refType === "local"
635
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
636
+ : Option.none();
637
+ if (ref.refType === "local" && Option.isNone(relativeLocalSource)) {
638
+ return yield* new KnowledgeDefinitionInvalid({
639
+ detail: `Local knowledge source must stay within the workspace: ${ref.source.path}`,
640
+ });
641
+ }
642
+ const prepared = yield* preparePackage(ref, force === true);
643
+ yield* prepared.commit;
644
+ return acquiredFacts(prepared, relativeLocalSource);
645
+ }, Effect.scoped);
631
646
  return {
632
647
  type: "knowledge",
633
648
  projectionPlans,
@@ -643,19 +658,8 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
643
658
  })),
644
659
  install: installAtomically,
645
660
  isInstalled: ({ target }) => isObservedInstalled(ws, "knowledge", target.name),
646
- materializeInstall: Effect.fn("KnowledgeManager.materializeInstall")(function* ({ ref, force, }) {
647
- const relativeLocalSource = ref.refType === "local"
648
- ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
649
- : Option.none();
650
- if (ref.refType === "local" && Option.isNone(relativeLocalSource)) {
651
- return yield* new KnowledgeDefinitionInvalid({
652
- detail: `Local knowledge source must stay within the workspace: ${ref.source.path}`,
653
- });
654
- }
655
- const prepared = yield* preparePackage(ref, force === true);
656
- yield* prepared.commit;
657
- return acquiredFacts(prepared, relativeLocalSource);
658
- }, Effect.scoped),
661
+ materializeInstall: acquireCanonical,
662
+ acquireCanonical,
659
663
  prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
660
664
  workspace: ws,
661
665
  type: "knowledge",
@@ -683,27 +687,30 @@ export const KnowledgeManagerLive = Layer.effect(KnowledgeManager, Effect.gen(fu
683
687
  }),
684
688
  materializeUninstall,
685
689
  materializeDeactivate,
686
- upsertSettingsEntry: ({ ref, versionRange, materialization }) => buildLockEntry(ref, materialization).pipe(Effect.flatMap((lockEntry) => Option.isSome(lockEntry)
687
- ? ws.setKnowledge({
688
- name: ref.knowledge.name,
689
- lockEntry: lockEntry.value,
690
- versionRange,
691
- })
692
- : setKnowledgeSourceEntry(ref.knowledge.name, "workspace"))),
693
- removeSettingsEntry: ({ target }) => ws.removeKnowledgeSettings(target.name),
694
- upsertLockfileEntry: ({ ref, materialization }) => buildLockEntry(ref, materialization).pipe(Effect.flatMap((lockEntry) => {
690
+ materializeRetained: ({ target }) => Effect.gen(function* () {
691
+ const canonical = yield* usableAcceptedCanonical({
692
+ workspace: ws,
693
+ type: "knowledge",
694
+ name: target.name,
695
+ });
696
+ if (Option.isNone(canonical) || canonical.value.ref.type !== "knowledge") {
697
+ return yield* new LifecyclePostconditionViolated({
698
+ postcondition: "materialize-observable",
699
+ targetType: "knowledge",
700
+ targetName: target.name,
701
+ });
702
+ }
703
+ return yield* materializeDeactivate({ target });
704
+ }),
705
+ acceptedResolution: ({ ref, materialization }) => buildLockEntry(ref, materialization).pipe(Effect.flatMap((lockEntry) => {
695
706
  if (Option.isNone(lockEntry))
696
- return ws.removeKnowledgeLock(ref.knowledge.name);
707
+ return Effect.succeed(Option.none());
697
708
  const validate = lockEntry.value.type === "registry"
698
709
  ? validateExactResolvedVersion(`knowledge.${ref.knowledge.name}.resolvedVersion`, lockEntry.value.resolvedVersion)
699
710
  : Effect.void;
700
- return validate.pipe(Effect.flatMap(() => ws.setKnowledgeLock({
701
- name: ref.knowledge.name,
702
- lockEntry: lockEntry.value,
703
- versionRange: Option.none(),
704
- })));
711
+ return validate.pipe(Effect.as(Option.some({ key: ref.knowledge.name, entry: lockEntry.value })));
705
712
  })),
706
- removeLockfileEntry: ({ target }) => ws.removeKnowledgeLock(target.name),
713
+ withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
707
714
  };
708
715
  }));
709
716
  //# sourceMappingURL=manager.js.map
@@ -20,7 +20,7 @@ import type { ExtensionManagerFailure } from "./errors.js";
20
20
  import type { NativeWriteAuthority } from "@agentxm/agent-integration";
21
21
  import type { ProjectionPlan } from "@agentxm/workspace-projection";
22
22
  import type { ExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
23
- import type { ExtensionTarget, ExtensionTargetFor } from "@agentxm/workspace-state";
23
+ import type { ExtensionTarget, ExtensionTargetFor, LockEntryByType } from "@agentxm/workspace-state";
24
24
  /**
25
25
  * The services every manager keeps in `R`: the platform it reads and writes
26
26
  * through, the registry transport its acquisition steps use, and the authority
@@ -107,30 +107,28 @@ export interface ExtensionManager<TRef extends ExtensionRef, TMaterialization ex
107
107
  readonly materializeUninstall: (args: {
108
108
  readonly target: ExtensionTargetFor<TRef>;
109
109
  }) => Effect.Effect<TMaterialization, ExtensionManagerFailure, R>;
110
+ /** Acquire canonical content without producing any native agent output. */
111
+ readonly acquireCanonical: ExtensionManager<TRef, TMaterialization, R>["materializeInstall"];
112
+ /** Restore projections from verified retained canonical content, without source resolution. */
113
+ readonly materializeRetained: (args: {
114
+ readonly target: ExtensionTargetFor<TRef>;
115
+ }) => Effect.Effect<TMaterialization, ExtensionManagerFailure, R>;
110
116
  /** Remove active projections while retaining canonical managed content. */
111
117
  readonly materializeDeactivate: (args: {
112
118
  readonly target: ExtensionTargetFor<TRef>;
113
119
  }) => Effect.Effect<TMaterialization, ExtensionManagerFailure, R>;
114
- readonly upsertSettingsEntry: (args: {
115
- readonly ref: TRef;
116
- readonly versionRange: Option.Option<string>;
117
- /** What `materializeInstall` observed in this closure, when it ran. */
118
- readonly materialization: Option.Option<TMaterialization>;
119
- }) => Effect.Effect<void, ExtensionManagerFailure, R>;
120
- readonly removeSettingsEntry: (args: {
121
- readonly target: ExtensionTargetFor<TRef>;
122
- /** What the withdrawal in this closure observed, when one ran. */
123
- readonly materialization: Option.Option<TMaterialization>;
124
- }) => Effect.Effect<void, ExtensionManagerFailure, R>;
125
- readonly upsertLockfileEntry: (args: {
120
+ /** Verified acquisition facts; recording the resolution belongs to reconciliation. */
121
+ readonly acceptedResolution: (args: {
126
122
  readonly ref: TRef;
127
- /** What `materializeInstall` observed in this closure, when it ran. */
128
123
  readonly materialization: Option.Option<TMaterialization>;
129
- }) => Effect.Effect<void, ExtensionManagerFailure, R>;
130
- readonly removeLockfileEntry: (args: {
124
+ }) => Effect.Effect<Option.Option<{
125
+ readonly key: string;
126
+ readonly entry: LockEntryByType[TRef["type"]];
127
+ }>, ExtensionManagerFailure, R>;
128
+ /** Accepted keys associated with the withdrawal observed by this adapter. */
129
+ readonly withdrawnResolutionKeys: (args: {
131
130
  readonly target: ExtensionTargetFor<TRef>;
132
- /** What the withdrawal in this closure observed, when one ran. */
133
131
  readonly materialization: Option.Option<TMaterialization>;
134
- }) => Effect.Effect<void, ExtensionManagerFailure, R>;
132
+ }) => Effect.Effect<ReadonlyArray<string>, ExtensionManagerFailure, R>;
135
133
  }
136
134
  //# sourceMappingURL=manager-contract.d.ts.map
@@ -1,3 +1,4 @@
1
+ import type { DesiredStateGraph } from "@agentxm/workspace-state";
1
2
  /**
2
3
  * Per-extension-type manager service tags and the materialization facts each
3
4
  * manager reports.
@@ -110,10 +111,20 @@ export interface RuleManagerService extends ExtensionManager<RuleExtensionRef, R
110
111
  declare const RuleManager_base: ServiceMap.ServiceClass<RuleManager, "@agentxm/extension-materialization/managers/RuleManager", RuleManagerService>;
111
112
  export declare class RuleManager extends RuleManager_base {
112
113
  }
114
+ /** Hook contributors verified during preparation, before they enter the lockfile. */
115
+ export interface PreparedHookProjection {
116
+ readonly plans: ReadonlyArray<ProjectionPlan<void, ExtensionManagerFailure, ManagerRequirements>>;
117
+ readonly agentOutcomes: ReadonlyArray<ConfiguredAgentOutcome>;
118
+ readonly acquisitions: ReadonlyArray<{
119
+ readonly name: string;
120
+ readonly treeIntegrity: TreeIntegrity;
121
+ }>;
122
+ }
113
123
  export interface HookManagerService extends ExtensionManager<HookExtensionRef, HookMaterializationFacts, ManagerRequirements> {
124
+ readonly prepareProjection: (refs: ReadonlyArray<HookExtensionRef>) => Effect.Effect<PreparedHookProjection, ExtensionManagerFailure, ManagerRequirements>;
114
125
  readonly aggregateProjectionObservation: Effect.Effect<MaterializationObservation, ExtensionManagerFailure, ManagerRequirements>;
115
126
  readonly projectionPlans: () => Effect.Effect<ReadonlyArray<ProjectionPlan<void, ExtensionManagerFailure, ManagerRequirements>>, ExtensionManagerFailure, ManagerRequirements>;
116
- readonly configuredAgentOutcomes?: (state: "projected" | "current") => Effect.Effect<ReadonlyArray<ConfiguredAgentOutcome>, ExtensionManagerFailure, ManagerRequirements>;
127
+ readonly configuredAgentOutcomes?: (state: "projected" | "current", proposedGraph?: DesiredStateGraph) => Effect.Effect<ReadonlyArray<ConfiguredAgentOutcome>, ExtensionManagerFailure, ManagerRequirements>;
117
128
  readonly configuredAgentOutcomesForRef?: (ref: HookExtensionRef, state: "projected" | "current") => Effect.Effect<ReadonlyArray<ConfiguredAgentOutcome>, ExtensionManagerFailure, ManagerRequirements>;
118
129
  }
119
130
  declare const HookManager_base: ServiceMap.ServiceClass<HookManager, "@agentxm/extension-materialization/managers/HookManager", HookManagerService>;
@@ -1,16 +1,3 @@
1
- /**
2
- * Per-extension-type manager service tags and the materialization facts each
3
- * manager reports.
4
- *
5
- * Plan-building features require a manager through its tag without depending
6
- * on the module that implements it. Every tag pins the facts type its manager
7
- * carries from a materialization to the settings and lockfile writes that
8
- * follow it in the same closure, so nothing travels through state the layer
9
- * keeps alive between calls.
10
- *
11
- * @experimental This API is unstable and may change without notice.
12
- * @packageDocumentation
13
- */
14
1
  import * as ServiceMap from "effect/Context";
15
2
  // -----------------------------------------------------------------------------
16
3
  // Service tags
@@ -1,3 +1,5 @@
1
+ import { usableAcceptedCanonical } from "@agentxm/workspace-state";
2
+ import { LifecyclePostconditionViolated } from "../extensions/errors.js";
1
3
  /**
2
4
  * MCP server extension manager service.
3
5
  *
@@ -216,6 +218,22 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
216
218
  return yield* isObservedInstalled(ws, "mcp-server", target.name);
217
219
  }),
218
220
  materializeInstall,
221
+ acquireCanonical: materializeInstall,
222
+ materializeRetained: ({ target }) => Effect.gen(function* () {
223
+ const canonical = yield* usableAcceptedCanonical({
224
+ workspace: ws,
225
+ type: "mcp-server",
226
+ name: target.name,
227
+ });
228
+ if (Option.isNone(canonical) || canonical.value.ref.type !== "mcp-server") {
229
+ return yield* new LifecyclePostconditionViolated({
230
+ postcondition: "materialize-observable",
231
+ targetType: "mcp-server",
232
+ targetName: target.name,
233
+ });
234
+ }
235
+ return yield* materializeInstall({ ref: canonical.value.ref });
236
+ }),
219
237
  prepareSourceTransition: ({ ref }) => prepareAcceptedCanonicalTransition({
220
238
  workspace: ws,
221
239
  type: "mcp-server",
@@ -240,9 +258,9 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
240
258
  materializeDeactivate,
241
259
  configuredAgentOutcomes,
242
260
  configuredAgentOutcomesForEntry,
243
- upsertSettingsEntry: ({ ref, versionRange, materialization, }) => {
261
+ acceptedResolution: ({ ref, materialization, }) => {
244
262
  if (ref.refType !== "registry")
245
- return Effect.void.pipe(Effect.withSpan("McpServerManager.upsertSettingsEntry"));
263
+ return Effect.succeed(Option.none());
246
264
  const registryRef = ref;
247
265
  return validateExactResolvedVersion(`mcpServers.${ref.server.name}.resolvedVersion`, registryRef.version).pipe(Effect.flatMap(() => {
248
266
  const treeIntegrity = materialization.pipe(Option.flatMap((facts) => facts.treeIntegrity));
@@ -250,56 +268,25 @@ export const McpServerManagerLive = Layer.effect(McpServerManager, Effect.gen(fu
250
268
  return Effect.fail(new McpInstallStateMissing({ name: registryRef.server.name }));
251
269
  }
252
270
  const lockEntry = buildMcpServerLockEntry(registryRef, treeIntegrity.value);
253
- return ws.setMcpServer({
254
- name: ref.server.name,
255
- resolutionKey: mcpRegistryResolutionKey({
271
+ return Effect.succeed(Option.some({
272
+ key: mcpRegistryResolutionKey({
256
273
  authority: registryRef.source.location,
257
274
  owner: registryRef.owner,
258
275
  name: registryRef.server.name,
259
276
  }),
260
- lockEntry,
261
- versionRange,
262
- });
263
- }), Effect.withSpan("McpServerManager.upsertSettingsEntry"));
264
- },
265
- removeSettingsEntry: ({ target }) => ws
266
- .removeMcpServerSettings(target.name)
267
- .pipe(Effect.withSpan("McpServerManager.removeSettingsEntry")),
268
- upsertLockfileEntry: ({ ref, materialization, }) => {
269
- if (ref.refType !== "registry")
270
- return ws
271
- .removeMcpServerLock(ref.server.name)
272
- .pipe(Effect.withSpan("McpServerManager.upsertLockfileEntry"));
273
- const registryRef = ref;
274
- return validateExactResolvedVersion(`mcpServers.${ref.server.name}.resolvedVersion`, registryRef.version).pipe(Effect.flatMap(() => {
275
- const treeIntegrity = materialization.pipe(Option.flatMap((facts) => facts.treeIntegrity));
276
- if (Option.isNone(treeIntegrity)) {
277
- return Effect.fail(new McpInstallStateMissing({ name: registryRef.server.name }));
278
- }
279
- const lockEntry = buildMcpServerLockEntry(registryRef, treeIntegrity.value);
280
- return ws.setMcpServerLock({
281
- name: ref.server.name,
282
- resolutionKey: mcpRegistryResolutionKey({
283
- authority: registryRef.source.location,
284
- owner: registryRef.owner,
285
- name: registryRef.server.name,
286
- }),
287
- lockEntry,
288
- versionRange: Option.none(),
289
- });
290
- }), Effect.withSpan("McpServerManager.upsertLockfileEntry"));
277
+ entry: lockEntry,
278
+ }));
279
+ }), Effect.withSpan("McpServerManager.acceptedResolution"));
291
280
  },
292
- removeLockfileEntry: ({ materialization, }) => {
281
+ withdrawnResolutionKeys: ({ materialization, }) => {
293
282
  const removal = materialization.pipe(Option.flatMap((facts) => facts.removal));
294
283
  if (Option.isNone(removal)) {
295
- return Effect.void.pipe(Effect.withSpan("McpServerManager.removeLockfileEntry"));
284
+ return Effect.succeed([]);
296
285
  }
297
286
  if (removal.value.retainShared || Option.isNone(removal.value.resolutionKey)) {
298
- return Effect.void.pipe(Effect.withSpan("McpServerManager.removeLockfileEntry"));
287
+ return Effect.succeed([]);
299
288
  }
300
- return ws
301
- .removeMcpServerLock(removal.value.resolutionKey.value)
302
- .pipe(Effect.withSpan("McpServerManager.removeLockfileEntry"));
289
+ return Effect.succeed([removal.value.resolutionKey.value]);
303
290
  },
304
291
  };
305
292
  }));
@@ -1,3 +1,5 @@
1
+ import { usableAcceptedCanonical } from "@agentxm/workspace-state";
2
+ import { LifecyclePostconditionViolated } from "../extensions/errors.js";
1
3
  /**
2
4
  * Pack manager service.
3
5
  *
@@ -145,6 +147,7 @@ export const PackManagerLive = Layer.effect(PackManager, Effect.gen(function* ()
145
147
  return yield* isObservedInstalled(ws, "pack", target.name);
146
148
  }),
147
149
  materializeInstall,
150
+ acquireCanonical: materializeInstall,
148
151
  prepareSourceTransition: ({ ref }) => prepareAcceptedCanonicalTransition({
149
152
  workspace: ws,
150
153
  type: "pack",
@@ -161,29 +164,29 @@ export const PackManagerLive = Layer.effect(PackManager, Effect.gen(function* ()
161
164
  }),
162
165
  materializeUninstall,
163
166
  materializeDeactivate,
164
- upsertSettingsEntry: Effect.fn("PackManager.upsertSettingsEntry")(function* ({ ref, versionRange, materialization, }) {
165
- const args = yield* buildCurrentPackArgs(ref, versionRange, materialization);
166
- if (Option.isSome(args)) {
167
- yield* ws.setPack(args.value);
168
- }
169
- else {
170
- yield* ws.setPackEntry(ref.pack.name, {
171
- source: "workspace",
172
- enabled: true,
167
+ materializeRetained: ({ target }) => Effect.gen(function* () {
168
+ const canonical = yield* usableAcceptedCanonical({
169
+ workspace: ws,
170
+ type: "pack",
171
+ name: target.name,
172
+ });
173
+ if (Option.isNone(canonical) || canonical.value.ref.type !== "pack") {
174
+ return yield* new LifecyclePostconditionViolated({
175
+ postcondition: "materialize-observable",
176
+ targetType: "pack",
177
+ targetName: target.name,
173
178
  });
174
179
  }
180
+ return yield* materializeDeactivate({ target });
175
181
  }),
176
- removeSettingsEntry: ({ target }) => ws.removePackSettings(target.name).pipe(Effect.withSpan("PackManager.removeSettingsEntry")),
177
- upsertLockfileEntry: Effect.fn("PackManager.upsertLockfileEntry")(function* ({ ref, materialization, }) {
182
+ acceptedResolution: Effect.fn("PackManager.acceptedResolution")(function* ({ ref, materialization, }) {
178
183
  const args = yield* buildCurrentPackArgs(ref, Option.none(), materialization);
179
- if (Option.isSome(args)) {
180
- yield* ws.setPackLock(args.value);
181
- }
182
- else {
183
- yield* ws.removePackLock(ref.pack.name);
184
- }
184
+ return Option.map(args, ({ versionRange: _versionRange, ...entry }) => ({
185
+ key: ref.pack.name,
186
+ entry,
187
+ }));
185
188
  }),
186
- removeLockfileEntry: ({ target }) => ws.removePackLock(target.name).pipe(Effect.withSpan("PackManager.removeLockfileEntry")),
189
+ withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
187
190
  };
188
191
  }));
189
192
  //# sourceMappingURL=manager.js.map
@@ -1,8 +1,3 @@
1
- /**
2
- * Rule manager service.
3
- *
4
- * @experimental This API is unstable and may change without notice.
5
- */
6
1
  import * as FileSystem from "effect/FileSystem";
7
2
  import * as Layer from "effect/Layer";
8
3
  import * as Path from "effect/Path";