@agentxm/extension-materialization 0.29.2 → 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,71 +1,5 @@
1
- /**
2
- * In-memory materialization doubles for feature tests: a manager whose
3
- * surfaces are recorded rather than written, a retention policy that never
4
- * retains, and a structural serialization of a recipe's typed failure into the
5
- * plan-step vocabulary.
6
- *
7
- * These are deliberately behavioural, not mocks of a shape: a test asserts on
8
- * what the recorder observed, so a contract change that stops calling a
9
- * surface fails the test rather than passing silently.
10
- *
11
- * @experimental This API is unstable and may change without notice.
12
- * @packageDocumentation
13
- */
14
1
  import * as Layer from "effect/Layer";
15
- import * as Option from "effect/Option";
16
- import { StepFailure } from "@agentxm/workspace-operations";
17
- import type { ExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
18
- import type { ExtensionTarget } from "@agentxm/workspace-state";
19
- import type { ExtensionManager, ManagerRequirements, MaterializationFacts } from "./manager-contract.js";
20
- import type { CallerStepFailure, UninstallRetentionPolicy } from "./extensions/operations.js";
21
2
  import { McpSecretStore } from "./mcps/secret-store.js";
22
- /** The manager surfaces one recipe touched, in the order it touched them. */
23
- export type RecordedManagerSurface = "materializeInstall" | "materializeUninstall" | "materializeDeactivate" | "upsertSettingsEntry" | "removeSettingsEntry" | "upsertLockfileEntry" | "removeLockfileEntry" | "listMaterializable" | "projectionPlans";
24
- /** What an in-memory manager observed while a recipe ran against it. */
25
- export interface RecordedManager<TRef extends ExtensionRef> {
26
- readonly manager: ExtensionManager<TRef, MaterializationFacts, ManagerRequirements>;
27
- /** Every surface the recipe touched, in call order. */
28
- readonly surfaces: ReadonlyArray<RecordedManagerSurface>;
29
- /** Targets currently declared in the recorder's settings. */
30
- readonly configured: ReadonlySet<string>;
31
- /** Targets currently observable as installed. */
32
- readonly installed: ReadonlySet<string>;
33
- }
34
- /**
35
- * A materialization that observed nothing and acquired nothing. It satisfies
36
- * every per-type facts shape, so a feature test that never asserts on content
37
- * identity uses one value for any manager.
38
- */
39
- export declare const NO_MATERIALIZATION_FACTS: {
40
- readonly observation: import("./manager-contract.js").MaterializationObservation;
41
- readonly treeIntegrity: Option.Option<never>;
42
- readonly sourceHash: Option.Option<never>;
43
- readonly acquired: Option.Option<never>;
44
- readonly removal: Option.Option<never>;
45
- };
46
- /**
47
- * A manager whose authoritative surfaces are in-memory sets. `materializable`
48
- * is what `listMaterializable` answers; anything installed through the
49
- * recorder becomes both configured and observable.
50
- */
51
- export declare const makeRecordedManager: <TRef extends ExtensionRef>(args: {
52
- readonly type: TRef["type"];
53
- readonly materializable: ReadonlyArray<TRef>;
54
- /** Targets already installed and declared before the recipe runs. */
55
- readonly installed?: ReadonlyArray<string>;
56
- }) => RecordedManager<TRef>;
57
- /** A retention policy that never retains a package for an installed Pack. */
58
- export declare const noUninstallRetention: UninstallRetentionPolicy;
59
- /** A retention policy that always retains, for the retained-by-pack settlement. */
60
- export declare const alwaysUninstallRetention: UninstallRetentionPolicy;
61
- /**
62
- * A structural serialization of a recipe's typed failure: the producing
63
- * family's own detail sentence under an `internal` category. Assertions bind
64
- * to this mapping, not to the application boundary's wording.
65
- */
66
- export declare const structuralStepFailure: <F>(failure: CallerStepFailure<F>) => StepFailure;
67
- /** A retention target's plan-step identity, for assertions on recorded steps. */
68
- export declare const recordedTargetName: (target: ExtensionTarget) => string;
69
3
  /** What an in-memory credential store recorded, and the layer that backs it. */
70
4
  export interface MemoryMcpSecretStore {
71
5
  readonly layer: Layer.Layer<McpSecretStore>;
@@ -1,96 +1,8 @@
1
- /**
2
- * In-memory materialization doubles for feature tests: a manager whose
3
- * surfaces are recorded rather than written, a retention policy that never
4
- * retains, and a structural serialization of a recipe's typed failure into the
5
- * plan-step vocabulary.
6
- *
7
- * These are deliberately behavioural, not mocks of a shape: a test asserts on
8
- * what the recorder observed, so a contract change that stops calling a
9
- * surface fails the test rather than passing silently.
10
- *
11
- * @experimental This API is unstable and may change without notice.
12
- * @packageDocumentation
13
- */
1
+ /** Deterministic credential storage for materialization consumers. */
14
2
  import * as Effect from "effect/Effect";
15
3
  import * as Layer from "effect/Layer";
16
4
  import * as Option from "effect/Option";
17
- import { StepFailure } from "@agentxm/workspace-operations";
18
- import { NO_MATERIALIZATION_OBSERVATION } from "./manager-contract.js";
19
- import { targetFromRef } from "./extensions/operations.js";
20
5
  import { McpSecretStore } from "./mcps/secret-store.js";
21
- /**
22
- * A materialization that observed nothing and acquired nothing. It satisfies
23
- * every per-type facts shape, so a feature test that never asserts on content
24
- * identity uses one value for any manager.
25
- */
26
- export const NO_MATERIALIZATION_FACTS = {
27
- observation: NO_MATERIALIZATION_OBSERVATION,
28
- treeIntegrity: Option.none(),
29
- sourceHash: Option.none(),
30
- acquired: Option.none(),
31
- removal: Option.none(),
32
- };
33
- const NO_FACTS = NO_MATERIALIZATION_FACTS;
34
- /**
35
- * A manager whose authoritative surfaces are in-memory sets. `materializable`
36
- * is what `listMaterializable` answers; anything installed through the
37
- * recorder becomes both configured and observable.
38
- */
39
- export const makeRecordedManager = (args) => {
40
- const surfaces = [];
41
- const configured = new Set(args.installed ?? []);
42
- const installed = new Set(args.installed ?? []);
43
- const record = (surface, effect) => Effect.sync(() => surfaces.push(surface)).pipe(Effect.andThen(effect));
44
- const manager = {
45
- type: args.type,
46
- isInstalled: ({ target }) => Effect.succeed(installed.has(target.name)),
47
- materializeInstall: ({ ref }) => record("materializeInstall", Effect.sync(() => {
48
- installed.add(targetFromRef(ref).name);
49
- return NO_FACTS;
50
- })),
51
- materializeUninstall: ({ target }) => record("materializeUninstall", Effect.sync(() => {
52
- installed.delete(target.name);
53
- return NO_FACTS;
54
- })),
55
- materializeDeactivate: () => record("materializeDeactivate", Effect.succeed(NO_FACTS)),
56
- getConfiguredSource: ({ target }) => Effect.succeed(configured.has(target.name) ? Option.some("workspace:") : Option.none()),
57
- isConfigured: ({ target }) => Effect.succeed(configured.has(target.name)),
58
- listMaterializable: () => record("listMaterializable", Effect.succeed(args.materializable)),
59
- upsertSettingsEntry: ({ ref }) => record("upsertSettingsEntry", Effect.sync(() => {
60
- configured.add(targetFromRef(ref).name);
61
- })),
62
- removeSettingsEntry: ({ target }) => record("removeSettingsEntry", Effect.sync(() => {
63
- configured.delete(target.name);
64
- })),
65
- upsertLockfileEntry: () => record("upsertLockfileEntry", Effect.void),
66
- removeLockfileEntry: () => record("removeLockfileEntry", Effect.void),
67
- };
68
- return { manager, surfaces, configured, installed };
69
- };
70
- /** A retention policy that never retains a package for an installed Pack. */
71
- export const noUninstallRetention = {
72
- isRequiredByInstalledPack: () => Effect.succeed(false),
73
- };
74
- /** A retention policy that always retains, for the retained-by-pack settlement. */
75
- export const alwaysUninstallRetention = {
76
- isRequiredByInstalledPack: () => Effect.succeed(true),
77
- };
78
- /**
79
- * A structural serialization of a recipe's typed failure: the producing
80
- * family's own detail sentence under an `internal` category. Assertions bind
81
- * to this mapping, not to the application boundary's wording.
82
- */
83
- export const structuralStepFailure = (failure) => {
84
- const detail = typeof failure === "object" &&
85
- failure !== null &&
86
- "detail" in failure &&
87
- typeof failure.detail === "string"
88
- ? failure.detail
89
- : String(failure);
90
- return new StepFailure({ category: "internal", detail, cause: failure });
91
- };
92
- /** A retention target's plan-step identity, for assertions on recorded steps. */
93
- export const recordedTargetName = (target) => target.name;
94
6
  /**
95
7
  * An in-memory credential store for MCP install tests.
96
8
  *
package/package.json CHANGED
@@ -4,16 +4,17 @@
4
4
  "url": "https://github.com/agentxm/axm/issues"
5
5
  },
6
6
  "dependencies": {
7
- "@agentxm/agent-integration": "^0.29.2",
8
- "@agentxm/extension-content": "^0.29.2",
9
- "@agentxm/extension-model": "^0.29.2",
10
- "@agentxm/extension-resolution": "^0.29.2",
11
- "@agentxm/extension-sources": "^0.29.2",
12
- "@agentxm/registry-client": "^0.29.2",
13
- "@agentxm/workspace-operations": "^0.29.2",
14
- "@agentxm/workspace-projection": "^0.29.2",
15
- "@agentxm/workspace-state": "^0.29.2",
16
- "@agentxm/workspace-transactions": "^0.29.2",
7
+ "@agentxm/agent-integration": "^0.30.1",
8
+ "@agentxm/cli-maintenance": "^0.30.1",
9
+ "@agentxm/extension-content": "^0.30.1",
10
+ "@agentxm/extension-model": "^0.30.1",
11
+ "@agentxm/extension-resolution": "^0.30.1",
12
+ "@agentxm/extension-sources": "^0.30.1",
13
+ "@agentxm/registry-client": "^0.30.1",
14
+ "@agentxm/workspace-operations": "^0.30.1",
15
+ "@agentxm/workspace-projection": "^0.30.1",
16
+ "@agentxm/workspace-state": "^0.30.1",
17
+ "@agentxm/workspace-transactions": "^0.30.1",
17
18
  "@napi-rs/keyring": "^1.3.0",
18
19
  "effect": "4.0.0-rc.115"
19
20
  },
@@ -24,27 +25,23 @@
24
25
  "@fast-check/vitest": "^0.5.0",
25
26
  "@typescript/native": "npm:typescript@^7.0.2",
26
27
  "typescript": "npm:@typescript/typescript6@^6.0.2",
27
- "vitest": "^5.0.0",
28
- "yaml": "^2.9.0"
28
+ "vitest": "^5.0.0"
29
29
  },
30
30
  "engines": {
31
31
  "node": ">=22.19.0"
32
32
  },
33
33
  "exports": {
34
34
  ".": {
35
- "axm-source": "./src/index.ts",
36
- "default": "./dist/src/index.js",
37
- "types": "./dist/src/index.d.ts"
35
+ "types": "./dist/src/index.d.ts",
36
+ "default": "./dist/src/index.js"
38
37
  },
39
38
  "./live": {
40
- "axm-source": "./src/live.ts",
41
- "default": "./dist/src/live.js",
42
- "types": "./dist/src/live.d.ts"
39
+ "types": "./dist/src/live.d.ts",
40
+ "default": "./dist/src/live.js"
43
41
  },
44
42
  "./testing": {
45
- "axm-source": "./src/testing.ts",
46
- "default": "./dist/src/testing.js",
47
- "types": "./dist/src/testing.d.ts"
43
+ "types": "./dist/src/testing.d.ts",
44
+ "default": "./dist/src/testing.js"
48
45
  }
49
46
  },
50
47
  "files": [
@@ -67,5 +64,5 @@
67
64
  },
68
65
  "sideEffects": false,
69
66
  "type": "module",
70
- "version": "0.29.2"
67
+ "version": "0.30.1"
71
68
  }
@@ -1,22 +0,0 @@
1
- /**
2
- * Failures deciding a retained re-materialization.
3
- *
4
- * @experimental This API is unstable and may change without notice.
5
- */
6
- import * as Schema from "effect/Schema";
7
- declare const RetainedContentUnusable_base: Schema.Class<RetainedContentUnusable, Schema.TaggedStruct<"RetainedContentUnusable", {
8
- readonly extensionType: Schema.String;
9
- readonly name: Schema.String;
10
- /** The canonical observation status that made restoration impossible. */
11
- readonly status: Schema.String;
12
- }>, import("effect/Cause").YieldableError>;
13
- /**
14
- * A retained transition asked to restore content the workspace accepted, and
15
- * that content is not usable. The transition refuses rather than re-resolving
16
- * the source, because re-acquiring is a different decision than restoring.
17
- */
18
- export declare class RetainedContentUnusable extends RetainedContentUnusable_base {
19
- get detail(): string;
20
- }
21
- export {};
22
- //# sourceMappingURL=errors.d.ts.map
@@ -1,22 +0,0 @@
1
- /**
2
- * Failures deciding a retained re-materialization.
3
- *
4
- * @experimental This API is unstable and may change without notice.
5
- */
6
- import * as Schema from "effect/Schema";
7
- /**
8
- * A retained transition asked to restore content the workspace accepted, and
9
- * that content is not usable. The transition refuses rather than re-resolving
10
- * the source, because re-acquiring is a different decision than restoring.
11
- */
12
- export class RetainedContentUnusable extends Schema.TaggedError()("RetainedContentUnusable", {
13
- extensionType: Schema.String,
14
- name: Schema.String,
15
- /** The canonical observation status that made restoration impossible. */
16
- status: Schema.String,
17
- }) {
18
- get detail() {
19
- return `Cannot rematerialize retained ${this.extensionType} ${this.name}: canonical content is ${this.status}`;
20
- }
21
- }
22
- //# sourceMappingURL=errors.js.map
@@ -1,58 +0,0 @@
1
- /**
2
- * Re-materializing what desired state already accepted.
3
- *
4
- * A retained transition — re-enabling a Pack whose members were left on disk
5
- * — must never re-resolve a source. The accepted resolution and the canonical
6
- * content it names are the only inputs: if that content is not usable the
7
- * transition refuses rather than silently reaching for the network, because
8
- * "enable what you already have" and "acquire something new" are different
9
- * decisions with different risk.
10
- *
11
- * This lives in the materialization capability, not in a feature, because
12
- * both the workspace sync sweep and Pack activation re-materialize accepted
13
- * content, and a feature may not import a peer feature.
14
- *
15
- * @experimental This API is unstable and may change without notice.
16
- */
17
- import * as Effect from "effect/Effect";
18
- import * as FileSystem from "effect/FileSystem";
19
- import * as Path from "effect/Path";
20
- import type { ExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
21
- import type { McpServerExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/mcp-server";
22
- import { CodingAgentRepository, type ProjectionParticipantRequirements } from "@agentxm/workspace-projection";
23
- import { WorkspaceMutations, type DesiredExtensionNode } from "@agentxm/workspace-state";
24
- import type { JobStepResult, PlannedJobStep, StepFailure } from "@agentxm/workspace-operations";
25
- import { RetainedContentUnusable } from "./errors.js";
26
- import type { ExtensionManagerFailure } from "../errors.js";
27
- import { ExtensionManagers } from "../manager-registry.js";
28
- import type { ManagerRequirements } from "../manager-contract.js";
29
- import { type RecipeRequirements, type StepFailureAdapter } from "../extensions/operations.js";
30
- import { SubagentManager } from "../managers.js";
31
- /** Services a retained materialize step needs when it runs. */
32
- export type RetainedMaterializeRequirements = CodingAgentRepository | ExtensionManagers | ManagerRequirements | RecipeRequirements | SubagentManager | WorkspaceMutations;
33
- /**
34
- * How an MCP server member is re-projected. MCP servers are realized into
35
- * each agent's native configuration rather than into a canonical tree, so the
36
- * caller hands in the install operation it already owns.
37
- */
38
- export type RunRetainedMcpServerInstall<R> = (args: {
39
- readonly ref: McpServerExtensionRef;
40
- }) => Effect.Effect<JobStepResult, StepFailure, R>;
41
- export interface RetainedMaterializeSteps<R> {
42
- /** Every accepted ref the selection covers, whether or not it needs work. */
43
- readonly refs: ReadonlyArray<ExtensionRef>;
44
- readonly steps: ReadonlyArray<PlannedJobStep<R | RetainedMaterializeRequirements>>;
45
- }
46
- /** Every failure deciding retained re-materialization can surface. */
47
- export type RetainedMaterializeFailure = RetainedContentUnusable | ExtensionManagerFailure;
48
- /**
49
- * Build the steps that bring the given desired nodes back to their accepted
50
- * materialization. Nodes whose projections are already current contribute no
51
- * step, so a retained transition that changes nothing plans nothing.
52
- */
53
- export declare const collectRetainedMaterializeSteps: <R = never>(args: {
54
- readonly nodes: ReadonlyArray<DesiredExtensionNode>;
55
- readonly runMcpServerInstall: RunRetainedMcpServerInstall<R>;
56
- readonly adapter: StepFailureAdapter;
57
- }) => Effect.Effect<RetainedMaterializeSteps<R>, RetainedMaterializeFailure, CodingAgentRepository | ExtensionManagers | FileSystem.FileSystem | Path.Path | ProjectionParticipantRequirements | SubagentManager | WorkspaceMutations>;
58
- //# sourceMappingURL=retained-materialization.d.ts.map
@@ -1,119 +0,0 @@
1
- /**
2
- * Re-materializing what desired state already accepted.
3
- *
4
- * A retained transition — re-enabling a Pack whose members were left on disk
5
- * — must never re-resolve a source. The accepted resolution and the canonical
6
- * content it names are the only inputs: if that content is not usable the
7
- * transition refuses rather than silently reaching for the network, because
8
- * "enable what you already have" and "acquire something new" are different
9
- * decisions with different risk.
10
- *
11
- * This lives in the materialization capability, not in a feature, because
12
- * both the workspace sync sweep and Pack activation re-materialize accepted
13
- * content, and a feature may not import a peer feature.
14
- *
15
- * @experimental This API is unstable and may change without notice.
16
- */
17
- import * as Effect from "effect/Effect";
18
- import * as FileSystem from "effect/FileSystem";
19
- import * as Option from "effect/Option";
20
- import * as Path from "effect/Path";
21
- import { CodingAgentRepository, isObservedMaterializationCurrent, } from "@agentxm/workspace-projection";
22
- import { acceptedCanonicalObservation, acceptedResolutionRef, isSourcedDesiredExtension, usableAcceptedCanonical, WorkspaceMutations, } from "@agentxm/workspace-state";
23
- import { RetainedContentUnusable } from "./errors.js";
24
- import { ExtensionManagers } from "../manager-registry.js";
25
- import { buildMaterializeOperation, targetFromRef, toStepKey, } from "../extensions/operations.js";
26
- import { SubagentManager } from "../managers.js";
27
- /**
28
- * Build the steps that bring the given desired nodes back to their accepted
29
- * materialization. Nodes whose projections are already current contribute no
30
- * step, so a retained transition that changes nothing plans nothing.
31
- */
32
- export const collectRetainedMaterializeSteps = (args) => Effect.gen(function* () {
33
- const ws = yield* WorkspaceMutations;
34
- const fs = yield* FileSystem.FileSystem;
35
- const path = yield* Path.Path;
36
- const agentRepo = yield* CodingAgentRepository;
37
- const managers = yield* ExtensionManagers;
38
- const subagentManager = yield* SubagentManager;
39
- const configuredAgentIds = yield* ws.getConfiguredAgents();
40
- const selected = args.nodes
41
- .filter(isSourcedDesiredExtension)
42
- .filter((node) => node.enabled && node.type !== "pack");
43
- const reconciled = yield* Effect.forEach(selected, (node) => Effect.gen(function* () {
44
- const canonical = yield* acceptedCanonicalObservation({
45
- workspace: ws,
46
- type: node.type,
47
- name: node.name,
48
- });
49
- const status = Option.isSome(canonical)
50
- ? canonical.value.observation.status
51
- : "missing-resolution";
52
- const usable = status === "usable"
53
- ? yield* usableAcceptedCanonical({
54
- workspace: ws,
55
- type: node.type,
56
- name: node.name,
57
- })
58
- : Option.none();
59
- const ref = Option.isSome(usable)
60
- ? Option.some(usable.value.ref)
61
- : yield* acceptedResolutionRef({
62
- workspace: ws,
63
- type: node.type,
64
- name: node.name,
65
- });
66
- if (Option.isNone(ref)) {
67
- return yield* new RetainedContentUnusable({
68
- extensionType: node.type,
69
- name: node.name,
70
- status,
71
- });
72
- }
73
- const current = yield* isObservedMaterializationCurrent({
74
- workspace: ws,
75
- node,
76
- configuredAgentIds,
77
- agents: agentRepo,
78
- subagents: subagentManager,
79
- resolvedRef: ref.value,
80
- fs,
81
- path,
82
- });
83
- return { node, ref: ref.value, materialize: status !== "usable" || !current };
84
- }), { concurrency: "unbounded" });
85
- const steps = reconciled
86
- .filter(({ materialize }) => materialize)
87
- .map(({ node, ref }) => {
88
- if (ref.type === "mcp-server") {
89
- const target = targetFromRef(ref);
90
- return {
91
- key: toStepKey(target),
92
- label: `${node.type} ${node.name}`,
93
- readiness: "ready",
94
- run: args.runMcpServerInstall({ ref }),
95
- };
96
- }
97
- const common = {
98
- toStepFailure: args.adapter.toStepFailure,
99
- label: `${node.type} ${node.name}`,
100
- message: `Restored ${node.type} ${node.name}`,
101
- };
102
- switch (ref.type) {
103
- case "skill":
104
- return buildMaterializeOperation(managers.skill, { ...common, ref });
105
- case "subagent":
106
- return buildMaterializeOperation(managers.subagent, { ...common, ref });
107
- case "rule":
108
- return buildMaterializeOperation(managers.rule, { ...common, ref });
109
- case "hook":
110
- return buildMaterializeOperation(managers.hook, { ...common, ref });
111
- case "knowledge":
112
- return buildMaterializeOperation(managers.knowledge, { ...common, ref });
113
- case "pack":
114
- return buildMaterializeOperation(managers.pack, { ...common, ref });
115
- }
116
- });
117
- return { refs: reconciled.map(({ ref }) => ref), steps };
118
- });
119
- //# sourceMappingURL=retained-materialization.js.map