@agentxm/extension-materialization 0.28.14-preview.1789139351.5cd4595aa

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.
Files changed (89) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +16 -0
  3. package/dist/src/desired-state/errors.d.ts +22 -0
  4. package/dist/src/desired-state/errors.js +22 -0
  5. package/dist/src/desired-state/retained-materialization.d.ts +58 -0
  6. package/dist/src/desired-state/retained-materialization.js +119 -0
  7. package/dist/src/errors.d.ts +42 -0
  8. package/dist/src/errors.js +14 -0
  9. package/dist/src/extensions/canonical-directory.d.ts +106 -0
  10. package/dist/src/extensions/canonical-directory.js +210 -0
  11. package/dist/src/extensions/canonical-reuse.d.ts +43 -0
  12. package/dist/src/extensions/canonical-reuse.js +38 -0
  13. package/dist/src/extensions/copy-directory.d.ts +50 -0
  14. package/dist/src/extensions/copy-directory.js +78 -0
  15. package/dist/src/extensions/errors.d.ts +97 -0
  16. package/dist/src/extensions/errors.js +42 -0
  17. package/dist/src/extensions/materializable-from-disk.d.ts +37 -0
  18. package/dist/src/extensions/materializable-from-disk.js +89 -0
  19. package/dist/src/extensions/operations.d.ts +252 -0
  20. package/dist/src/extensions/operations.js +631 -0
  21. package/dist/src/hooks/errors.d.ts +33 -0
  22. package/dist/src/hooks/errors.js +18 -0
  23. package/dist/src/hooks/manager.d.ts +13 -0
  24. package/dist/src/hooks/manager.js +778 -0
  25. package/dist/src/index.d.ts +44 -0
  26. package/dist/src/index.js +56 -0
  27. package/dist/src/knowledge/errors.d.ts +75 -0
  28. package/dist/src/knowledge/errors.js +36 -0
  29. package/dist/src/knowledge/manager.d.ts +9 -0
  30. package/dist/src/knowledge/manager.js +709 -0
  31. package/dist/src/live.d.ts +20 -0
  32. package/dist/src/live.js +20 -0
  33. package/dist/src/manager-contract.d.ts +136 -0
  34. package/dist/src/manager-contract.js +19 -0
  35. package/dist/src/manager-registry-live.d.ts +11 -0
  36. package/dist/src/manager-registry-live.js +23 -0
  37. package/dist/src/manager-registry.d.ts +37 -0
  38. package/dist/src/manager-registry.js +22 -0
  39. package/dist/src/managers.d.ts +147 -0
  40. package/dist/src/managers.js +32 -0
  41. package/dist/src/mcps/artifact.d.ts +24 -0
  42. package/dist/src/mcps/artifact.js +74 -0
  43. package/dist/src/mcps/authored-materialization.d.ts +28 -0
  44. package/dist/src/mcps/authored-materialization.js +34 -0
  45. package/dist/src/mcps/errors.d.ts +105 -0
  46. package/dist/src/mcps/errors.js +40 -0
  47. package/dist/src/mcps/install-operation.d.ts +92 -0
  48. package/dist/src/mcps/install-operation.js +557 -0
  49. package/dist/src/mcps/manager.d.ts +15 -0
  50. package/dist/src/mcps/manager.js +253 -0
  51. package/dist/src/mcps/native-entry.d.ts +51 -0
  52. package/dist/src/mcps/native-entry.js +100 -0
  53. package/dist/src/mcps/secret-store-live.d.ts +14 -0
  54. package/dist/src/mcps/secret-store-live.js +58 -0
  55. package/dist/src/mcps/secret-store.d.ts +55 -0
  56. package/dist/src/mcps/secret-store.js +33 -0
  57. package/dist/src/packs/errors.d.ts +50 -0
  58. package/dist/src/packs/errors.js +24 -0
  59. package/dist/src/packs/manager.d.ts +16 -0
  60. package/dist/src/packs/manager.js +189 -0
  61. package/dist/src/projection-participants-live.d.ts +17 -0
  62. package/dist/src/projection-participants-live.js +66 -0
  63. package/dist/src/projection-step-failure.d.ts +19 -0
  64. package/dist/src/projection-step-failure.js +76 -0
  65. package/dist/src/registry-materialization.d.ts +52 -0
  66. package/dist/src/registry-materialization.js +67 -0
  67. package/dist/src/rules/errors.d.ts +31 -0
  68. package/dist/src/rules/errors.js +17 -0
  69. package/dist/src/rules/manager.d.ts +18 -0
  70. package/dist/src/rules/manager.js +492 -0
  71. package/dist/src/skills/errors.d.ts +42 -0
  72. package/dist/src/skills/errors.js +21 -0
  73. package/dist/src/skills/manager.d.ts +17 -0
  74. package/dist/src/skills/manager.js +339 -0
  75. package/dist/src/skills/materialization.d.ts +52 -0
  76. package/dist/src/skills/materialization.js +168 -0
  77. package/dist/src/skills/skill-artifact.d.ts +38 -0
  78. package/dist/src/skills/skill-artifact.js +70 -0
  79. package/dist/src/skills/source-hash.d.ts +2 -0
  80. package/dist/src/skills/source-hash.js +6 -0
  81. package/dist/src/subagents/errors.d.ts +43 -0
  82. package/dist/src/subagents/errors.js +20 -0
  83. package/dist/src/subagents/lock-entry-builder.d.ts +20 -0
  84. package/dist/src/subagents/lock-entry-builder.js +63 -0
  85. package/dist/src/subagents/manager.d.ts +17 -0
  86. package/dist/src/subagents/manager.js +736 -0
  87. package/dist/src/testing.d.ts +87 -0
  88. package/dist/src/testing.js +116 -0
  89. package/package.json +70 -0
@@ -0,0 +1,492 @@
1
+ /**
2
+ * Rule manager service.
3
+ *
4
+ * @experimental This API is unstable and may change without notice.
5
+ */
6
+ import * as Effect from "effect/Effect";
7
+ import { stripFileProtocol } from "@agentxm/registry-client";
8
+ import * as FileSystem from "effect/FileSystem";
9
+ import * as Layer from "effect/Layer";
10
+ import * as Option from "effect/Option";
11
+ import * as Ref from "effect/Ref";
12
+ import * as Path from "effect/Path";
13
+ import * as Schema from "effect/Schema";
14
+ import { RuleDefinitionInvalid, RuleInstallStateMissing } from "./errors.js";
15
+ import { activeContributors, applyProjectionPlans, planAggregateProjection, reconcileManagedRegionFile, projectionGeneration, assertInstructionTargetsSafe, assertInstructionsGitignoreSafe, observeInstructionProjection, reconcileInstructionTargets, resolveInstructionsConfig, } from "@agentxm/workspace-projection";
16
+ import { MARKER_KIND_POINT, MARKER_VERSION, serializeMarker } from "@agentxm/agent-integration";
17
+ import { decodeExtensionNameSync, formatFqn } from "@agentxm/extension-model/unstable/extensions";
18
+ import { canReuseInstalledPackage, materializeExternalPackageWithTreeIntegrity, } from "../extensions/canonical-directory.js";
19
+ import { enabledConfiguredEntries } from "@agentxm/workspace-state";
20
+ import { materializeRegistryPackageWithTreeIntegrity } from "../registry-materialization.js";
21
+ import { computeExtensionPathsForLayout } from "@agentxm/workspace-state";
22
+ import { RuleManager } from "../managers.js";
23
+ import { RULES_REGION_OWNER } from "@agentxm/workspace-projection";
24
+ import { parseFrontmatterEffect } from "@agentxm/extension-content";
25
+ import { computePackageContentHash } from "@agentxm/workspace-state";
26
+ import { computeMaterializedTreeIntegrity } from "@agentxm/workspace-state";
27
+ import {} from "@agentxm/extension-model/unstable/sources/source-hash";
28
+ import { acceptedRegistryVersionForRef, validateExactResolvedVersion, } from "@agentxm/workspace-state";
29
+ import { MaterializedFileTargetSchema } from "@agentxm/workspace-state";
30
+ import { gitSourceLockFields } from "@agentxm/workspace-state";
31
+ import { SourceHostProviders, WorkspaceCatalog } from "@agentxm/extension-sources";
32
+ import { makeWorkspaceRelativeSourcePath } from "@agentxm/extension-model/unstable/path-types";
33
+ import { removeIfExists } from "@agentxm/workspace-state";
34
+ import { makeWorkspaceRelativePath } from "@agentxm/extension-model/unstable/path-types";
35
+ import { decodeVersionSync } from "@agentxm/extension-model/unstable/version-constraints";
36
+ import { NO_MATERIALIZATION_OBSERVATION } from "../manager-contract.js";
37
+ import { WorkspaceMutations } from "@agentxm/workspace-state";
38
+ import { usableAcceptedCanonicalRef } from "@agentxm/workspace-state";
39
+ import { isObservedInstalled } from "@agentxm/workspace-state";
40
+ import { acceptedCanonicalObservation, prepareAcceptedCanonicalTransition, removableAcceptedCanonicalPath, } from "@agentxm/workspace-state";
41
+ import { RULE_BODY_FILENAME, RULE_EXTENSION_DIR, RULE_MANIFEST_FILENAME, RuleManifestSchema, } from "@agentxm/extension-model/unstable/rules/manifest-schema";
42
+ import {} from "@agentxm/extension-model/unstable/extensions/refs/rule";
43
+ const RULES_REGION = "rules";
44
+ const decodeRuleManifest = Schema.decodeUnknownEffect(RuleManifestSchema);
45
+ const decodeMaterializedTarget = Schema.decodeUnknownSync(MaterializedFileTargetSchema);
46
+ const registryRuleLockEntry = (ref, treeIntegrity) => ({
47
+ type: "registry",
48
+ sourceType: "registry",
49
+ packageFormat: "agentxm",
50
+ endpoint: ref.source.location,
51
+ extensionType: "rule",
52
+ workspaceName: ref.rule.name,
53
+ owner: ref.owner,
54
+ name: ref.name,
55
+ resolvedVersion: decodeVersionSync(ref.version),
56
+ integrity: Option.getOrElse(ref.integrity, () => ""),
57
+ sourceName: ref.source.name,
58
+ publisherBindingId: ref.publisherBindingId,
59
+ treeIntegrity,
60
+ });
61
+ const gitRuleLockEntry = (ref, contentIdentity, treeIntegrity) => ({
62
+ ...gitSourceLockFields(ref.source, "rule", ref.rule.name, Option.fromUndefinedOr(ref.sourcePath), ref.gitCommitSha, ref.gitTreeSha, contentIdentity, ref.owner, ref.name, treeIntegrity),
63
+ });
64
+ const localRuleLockEntry = (ref, workspaceRelativeLocalSourcePath, contentIdentity, treeIntegrity) => ({
65
+ type: "local",
66
+ sourceType: "local",
67
+ sourceName: "local",
68
+ extensionType: "rule",
69
+ workspaceName: ref.rule.name,
70
+ packageFormat: "agentxm",
71
+ packageOwner: ref.owner,
72
+ packageName: ref.name,
73
+ path: Option.getOrElse(workspaceRelativeLocalSourcePath, () => ref.source.path),
74
+ contentIdentity,
75
+ treeIntegrity,
76
+ });
77
+ const normalizeMarkdown = (content) => content
78
+ .replace(/\r\n?/g, "\n")
79
+ .split("\n")
80
+ .map((line) => line.trimEnd())
81
+ .join("\n")
82
+ .trim();
83
+ export const ruleMaterializationObservation = (managedTarget, instructionItems) => {
84
+ const agents = Array.from(new Set(instructionItems
85
+ .filter(({ agentId, health }) => health === "ok" && agentId !== "universal")
86
+ .map(({ agentId }) => agentId)));
87
+ return {
88
+ agents,
89
+ targets: [
90
+ {
91
+ path: managedTarget,
92
+ ...(agents.length === 0 ? {} : { agentIds: agents }),
93
+ },
94
+ ],
95
+ };
96
+ };
97
+ export const RuleManagerLive = Layer.effect(RuleManager, Effect.gen(function* () {
98
+ const ws = yield* WorkspaceMutations;
99
+ const fs = yield* FileSystem.FileSystem;
100
+ const path = yield* Path.Path;
101
+ const sources = yield* SourceHostProviders;
102
+ const catalog = yield* WorkspaceCatalog;
103
+ const baseDir = ws.baseDir;
104
+ const workspaceScope = ws.scope;
105
+ // The workspace facade and the source integration are this layer's own
106
+ // dependencies; the platform stays in `R` for every member.
107
+ const provide = (effect) => effect.pipe(Effect.provideService(WorkspaceMutations, ws), Effect.provideService(SourceHostProviders, sources), Effect.provideService(WorkspaceCatalog, catalog));
108
+ // The instructions region is an aggregate unit: it renders after desired
109
+ // state commits, so the closure reads back what the render projected.
110
+ const lastProjection = yield* Ref.make(NO_MATERIALIZATION_OBSERVATION);
111
+ const materializeFromRegistry = (ref, force) => Effect.gen(function* () {
112
+ const canonicalPath = computeExtensionPathsForLayout(path.join, ws.layout, ref, RULE_EXTENSION_DIR, ref.name).canonicalPath;
113
+ const lockedEntry = yield* ws.getLockedRuleEntry(ref.rule.name);
114
+ const lockedVersion = acceptedRegistryVersionForRef(lockedEntry, ref);
115
+ const reuse = yield* provide(canReuseInstalledPackage({
116
+ installedPath: canonicalPath,
117
+ force,
118
+ refVersion: ref.version,
119
+ hasIntegrity: Option.isSome(ref.integrity),
120
+ ...(lockedVersion === undefined ? {} : { lockedVersion }),
121
+ existsFailureDetail: (target) => `Failed to check if canonical rule package path exists: ${target}`,
122
+ }));
123
+ if (reuse && Option.isSome(lockedEntry)) {
124
+ const observedTree = yield* provide(computeMaterializedTreeIntegrity(canonicalPath));
125
+ if (observedTree === lockedEntry.value.treeIntegrity) {
126
+ return { packageRoot: canonicalPath, treeIntegrity: lockedEntry.value.treeIntegrity };
127
+ }
128
+ }
129
+ const materialized = yield* provide(materializeRegistryPackageWithTreeIntegrity({
130
+ baseDir,
131
+ destinationPath: canonicalPath,
132
+ sourceLocation: ref.source.location,
133
+ owner: ref.owner,
134
+ type: "rule",
135
+ name: ref.name,
136
+ version: ref.version,
137
+ integrity: ref.integrity,
138
+ messages: {
139
+ integrityMismatchDetail: `Integrity mismatch for rule:${ref.name}@${ref.version}`,
140
+ },
141
+ }));
142
+ return {
143
+ packageRoot: materialized.canonicalPath,
144
+ treeIntegrity: materialized.treeIntegrity,
145
+ };
146
+ });
147
+ const materializeFromExternal = (ref) => provide(materializeExternalPackageWithTreeIntegrity({
148
+ baseDir,
149
+ canonicalPath: computeExtensionPathsForLayout(path.join, ws.layout, ref, RULE_EXTENSION_DIR, ref.rule.name).canonicalPath,
150
+ sourceLocation: ref.location,
151
+ copyFailureCode: "validation",
152
+ copyFailureDetail: (canonicalPath) => `Failed to copy rule package files to ${canonicalPath}`,
153
+ }).pipe(Effect.map((materialized) => ({
154
+ packageRoot: materialized.canonicalPath,
155
+ treeIntegrity: materialized.treeIntegrity,
156
+ }))));
157
+ const materializePackage = (ref, force = false) => Effect.gen(function* () {
158
+ switch (ref.refType) {
159
+ case "registry":
160
+ return yield* materializeFromRegistry(ref, force);
161
+ case "git-hosted":
162
+ case "local":
163
+ return yield* materializeFromExternal(ref);
164
+ case "workspace": {
165
+ const expectedPath = computeExtensionPathsForLayout(path.join, ws.layout, ref, RULE_EXTENSION_DIR, ref.name).canonicalPath;
166
+ if (ref.scope !== ws.scope ||
167
+ path.resolve(ref.location) !== path.resolve(expectedPath)) {
168
+ return yield* new RuleDefinitionInvalid({
169
+ detail: `Invalid workspace rule source location: ${ref.location}`,
170
+ });
171
+ }
172
+ return {
173
+ packageRoot: ref.location,
174
+ treeIntegrity: yield* provide(computeMaterializedTreeIntegrity(ref.location)),
175
+ };
176
+ }
177
+ }
178
+ });
179
+ const readManifest = (packageRoot) => fs.readFileString(path.join(packageRoot, RULE_MANIFEST_FILENAME)).pipe(Effect.flatMap((content) => Effect.try({
180
+ try: () => JSON.parse(content),
181
+ catch: (error) => new RuleDefinitionInvalid({
182
+ detail: `Failed to parse ${RULE_MANIFEST_FILENAME}`,
183
+ cause: error,
184
+ }),
185
+ })), Effect.flatMap((content) => decodeRuleManifest(content)), Effect.mapError((error) => new RuleDefinitionInvalid({
186
+ detail: `Failed to read ${RULE_MANIFEST_FILENAME}`,
187
+ cause: error,
188
+ })));
189
+ const sourceFileTarget = () => Effect.gen(function* () {
190
+ const config = yield* ws.getInstructionsConfig();
191
+ const resolved = resolveInstructionsConfig(Option.isSome(config) && config.value !== false ? config.value : undefined);
192
+ const relative = makeWorkspaceRelativePath(path, baseDir, resolved.fileName);
193
+ if (Option.isNone(relative)) {
194
+ return yield* new RuleDefinitionInvalid({
195
+ detail: `Rule instruction source escapes workspace: ${resolved.fileName}`,
196
+ });
197
+ }
198
+ return {
199
+ relative: relative.value,
200
+ absolute: path.resolve(baseDir, relative.value),
201
+ };
202
+ });
203
+ const activeInstructions = () => Effect.gen(function* () {
204
+ const config = yield* ws.getInstructionsConfig();
205
+ if (Option.isNone(config) || config.value === false) {
206
+ return Option.none();
207
+ }
208
+ return Option.some({
209
+ config: resolveInstructionsConfig(config.value),
210
+ agents: yield* ws.getConfiguredAgents(),
211
+ });
212
+ });
213
+ const readRuleBody = (packageRoot) => fs.readFileString(path.join(packageRoot, "src", RULE_BODY_FILENAME)).pipe(Effect.flatMap((content) => parseFrontmatterEffect(content)), Effect.map((parsed) => normalizeMarkdown(parsed.body)), Effect.mapError((error) => new RuleDefinitionInvalid({
214
+ detail: `Failed to read src/${RULE_BODY_FILENAME}`,
215
+ cause: error,
216
+ })));
217
+ const renderRuleBlock = (args) => {
218
+ const header = args.manifest.title !== undefined && !args.body.startsWith("#")
219
+ ? `# ${args.manifest.title}\n\n`
220
+ : "";
221
+ const marker = serializeMarker({
222
+ kind: MARKER_KIND_POINT,
223
+ v: MARKER_VERSION,
224
+ pointKind: "rule",
225
+ ext: `${args.marker}@${args.manifest.version}`,
226
+ }, { kind: "block", open: "<!--", close: "-->" });
227
+ return `${marker}\n\n${header}${args.body}`;
228
+ };
229
+ const selectRuleContributors = (args) => provide(activeContributors({
230
+ layout: ws.layout,
231
+ path,
232
+ type: "rule",
233
+ extensionDir: RULE_EXTENSION_DIR,
234
+ graph: args.graph,
235
+ locked: args.locked,
236
+ })).pipe(Effect.flatMap((contributors) => Effect.forEach(contributors, (contributor) => Effect.gen(function* () {
237
+ const manifest = yield* readManifest(contributor.packageRoot);
238
+ const body = yield* readRuleBody(contributor.packageRoot);
239
+ const marker = Option.match(contributor.identityOwner, {
240
+ onSome: (owner) => formatFqn({
241
+ owner,
242
+ type: "rule",
243
+ name: decodeExtensionNameSync(contributor.node.name),
244
+ }),
245
+ onNone: () => formatFqn({ owner: manifest.owner, type: "rule", name: manifest.name }),
246
+ });
247
+ return { name: contributor.node.name, marker, manifest, body };
248
+ }), { concurrency: "unbounded" })), Effect.map((resolved) => {
249
+ const sorted = [...resolved].sort((a, b) => {
250
+ const byPriority = (a.manifest.priority ?? 100) - (b.manifest.priority ?? 100);
251
+ if (byPriority !== 0)
252
+ return byPriority;
253
+ return a.marker.localeCompare(b.marker);
254
+ });
255
+ return sorted;
256
+ }));
257
+ const reconcileRulesRegion = (args) => Effect.gen(function* () {
258
+ const { target } = args;
259
+ const contributors = args.input.contributors;
260
+ const rendered = contributors.map(renderRuleBlock).join("\n\n");
261
+ const generation = projectionGeneration([
262
+ "rule-instructions-region-v1",
263
+ target.relative,
264
+ RULES_REGION_OWNER,
265
+ ...contributors.flatMap((contributor) => [
266
+ contributor.name,
267
+ contributor.marker,
268
+ contributor.body,
269
+ JSON.stringify(contributor.manifest),
270
+ ]),
271
+ ]);
272
+ const instructions = args.instructions;
273
+ if (args.dryRun !== true && Option.isSome(instructions)) {
274
+ yield* provide(Effect.gen(function* () {
275
+ const snapshot = yield* observeInstructionProjection({
276
+ workspaceRoot: baseDir,
277
+ scope: workspaceScope,
278
+ configuredAgents: instructions.value.agents,
279
+ config: instructions.value.config,
280
+ });
281
+ yield* assertInstructionTargetsSafe(snapshot.status);
282
+ yield* assertInstructionsGitignoreSafe(baseDir);
283
+ }));
284
+ }
285
+ const reconciliation = yield* provide(reconcileManagedRegionFile({
286
+ targetPath: target.absolute,
287
+ displayPath: target.relative,
288
+ region: RULES_REGION,
289
+ owner: RULES_REGION_OWNER,
290
+ rendered,
291
+ generation,
292
+ ...(args.dryRun === undefined ? {} : { dryRun: args.dryRun }),
293
+ writeWhenMissing: true,
294
+ unsupportedTargetDetail: `Instruction source does not support managed regions: ${target.relative}`,
295
+ }));
296
+ const { changed, observedRegion } = reconciliation;
297
+ const materializedTarget = decodeMaterializedTarget({
298
+ target: target.relative,
299
+ mode: "managed-region",
300
+ region: RULES_REGION,
301
+ });
302
+ const projectionUnitObservation = {
303
+ unitId: "rule:instructions-region",
304
+ path: `${target.relative}#${RULES_REGION}`,
305
+ owner: RULES_REGION_OWNER,
306
+ present: Option.isSome(observedRegion),
307
+ current: !changed,
308
+ expectedContributors: contributors.map(({ marker }) => marker),
309
+ };
310
+ if (args.dryRun === true) {
311
+ return {
312
+ materializedTarget,
313
+ materialization: ruleMaterializationObservation(target.relative, []),
314
+ changed,
315
+ projectionUnitObservation,
316
+ };
317
+ }
318
+ const instructionItems = Option.isSome(instructions)
319
+ ? (yield* provide(reconcileInstructionTargets({
320
+ workspaceRoot: baseDir,
321
+ scope: workspaceScope,
322
+ configuredAgents: instructions.value.agents,
323
+ config: instructions.value.config,
324
+ }))).snapshot.status.items
325
+ : [];
326
+ const materialization = ruleMaterializationObservation(target.relative, instructionItems);
327
+ yield* Ref.set(lastProjection, materialization);
328
+ return { materializedTarget, materialization, changed, projectionUnitObservation };
329
+ });
330
+ const makeRulesProjectionPlan = () => Effect.gen(function* () {
331
+ const target = yield* sourceFileTarget();
332
+ const instructions = yield* activeInstructions();
333
+ const graph = yield* ws.getDesiredStateGraph();
334
+ const locked = yield* ws.getLockedRules();
335
+ return yield* planAggregateProjection({
336
+ unitId: "rule:instructions-region",
337
+ targetFile: target.absolute,
338
+ graph,
339
+ // Rule contributors are decided from desired state alone, so the
340
+ // instructions region never excludes one.
341
+ select: (completeGraph) => selectRuleContributors({ graph: completeGraph, locked }).pipe(Effect.map((contributors) => ({ contributors, exclusions: [] }))),
342
+ adapter: {
343
+ observe: (input) => reconcileRulesRegion({ input, target, instructions, dryRun: true }).pipe(Effect.map(({ projectionUnitObservation }) => projectionUnitObservation)),
344
+ apply: (input) => reconcileRulesRegion({ input, target, instructions }).pipe(Effect.asVoid),
345
+ },
346
+ });
347
+ });
348
+ const projectionPlans = () => makeRulesProjectionPlan().pipe(Effect.map((plan) => [plan]));
349
+ const applyRulesProjection = projectionPlans().pipe(Effect.flatMap(applyProjectionPlans));
350
+ const materializeInstall = Effect.fn("RuleManager.materializeInstall")(function* ({ ref, force, }) {
351
+ const materialized = yield* materializePackage(ref, force === true);
352
+ const packageRoot = materialized.packageRoot;
353
+ yield* readManifest(packageRoot);
354
+ const workspaceRelativeLocalSourcePath = ref.refType === "local"
355
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
356
+ : Option.none();
357
+ if (ref.refType === "local" && Option.isNone(workspaceRelativeLocalSourcePath)) {
358
+ return yield* new RuleDefinitionInvalid({
359
+ detail: `Local rule source path must stay within the workspace root: ${ref.source.path}`,
360
+ });
361
+ }
362
+ const sourceHash = yield* computePackageContentHash(packageRoot);
363
+ return {
364
+ observation: NO_MATERIALIZATION_OBSERVATION,
365
+ treeIntegrity: Option.some(materialized.treeIntegrity),
366
+ acquired: Option.some({
367
+ ref,
368
+ workspaceRelativeLocalSourcePath,
369
+ sourceHash,
370
+ treeIntegrity: materialized.treeIntegrity,
371
+ }),
372
+ };
373
+ });
374
+ const buildLockEntry = (ref, materialization) => Effect.gen(function* () {
375
+ const state = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.acquired)));
376
+ switch (ref.refType) {
377
+ case "registry":
378
+ return state === undefined
379
+ ? yield* new RuleInstallStateMissing({ name: ref.rule.name, kind: "tree-integrity" })
380
+ : Option.some(registryRuleLockEntry(ref, state.treeIntegrity));
381
+ case "git-hosted":
382
+ return state === undefined
383
+ ? yield* new RuleInstallStateMissing({
384
+ name: ref.rule.name,
385
+ kind: "content-identity",
386
+ })
387
+ : Option.some(gitRuleLockEntry(ref, state.sourceHash, state.treeIntegrity));
388
+ case "local":
389
+ return state === undefined
390
+ ? yield* new RuleInstallStateMissing({
391
+ name: ref.rule.name,
392
+ kind: "content-identity",
393
+ })
394
+ : Option.some(localRuleLockEntry(ref, state.workspaceRelativeLocalSourcePath, state.sourceHash, state.treeIntegrity));
395
+ case "workspace":
396
+ return Option.none();
397
+ }
398
+ });
399
+ // Canonical removal only. The shared operation flow re-renders the region
400
+ // after settings and lock removal, once the target has left the graph.
401
+ const withdrawn = {
402
+ observation: NO_MATERIALIZATION_OBSERVATION,
403
+ treeIntegrity: Option.none(),
404
+ acquired: Option.none(),
405
+ };
406
+ const materializeUninstall = Effect.fn("RuleManager.materializeUninstall")(function* ({ target, }) {
407
+ const canonical = yield* provide(acceptedCanonicalObservation({
408
+ workspace: ws,
409
+ type: "rule",
410
+ name: target.name,
411
+ }));
412
+ const packageRoot = removableAcceptedCanonicalPath(canonical);
413
+ if (Option.isSome(packageRoot)) {
414
+ yield* removeIfExists(fs, packageRoot.value);
415
+ }
416
+ return withdrawn;
417
+ });
418
+ // Deactivation retains canonical content; the caller updates settings
419
+ // first, so re-rendering the whole region drops this rule's contribution.
420
+ const materializeDeactivate = Effect.fn("RuleManager.materializeDeactivate")(() => applyRulesProjection.pipe(Effect.as(withdrawn)));
421
+ return {
422
+ type: "rule",
423
+ projectionPlans,
424
+ aggregateProjectionObservation: Ref.get(lastProjection),
425
+ isInstalled: ({ target }) => isObservedInstalled(ws, "rule", target.name).pipe(Effect.withSpan("RuleManager.isInstalled")),
426
+ materializeInstall,
427
+ prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
428
+ workspace: ws,
429
+ type: "rule",
430
+ name: ref.rule.name,
431
+ ref,
432
+ })),
433
+ getConfiguredSource: Effect.fn("RuleManager.getConfiguredSource")(function* ({ target }) {
434
+ const configured = yield* ws.getConfiguredRuleEntries();
435
+ return Option.fromUndefinedOr(configured[target.name]?.source);
436
+ }),
437
+ /**
438
+ * Every enabled entry's accepted canonical package, read from accepted
439
+ * resolution rather than re-resolved from source. Materialization
440
+ * realizes what the workspace already accepted; going back to the
441
+ * source would put an unrelated configured entry's release age between
442
+ * an operator and the extension they are authoring.
443
+ */
444
+ listMaterializable: Effect.fn("RuleManager.listMaterializable")(function* () {
445
+ const configured = yield* ws.getConfiguredRuleEntries();
446
+ const refs = yield* Effect.forEach(enabledConfiguredEntries(configured), ([name]) => provide(usableAcceptedCanonicalRef({ workspace: ws, type: "rule", name }).pipe(Effect.map(Option.filter((ref) => ref.type === "rule")))), { concurrency: "unbounded" });
447
+ return refs.flatMap((ref) => (Option.isSome(ref) ? [ref.value] : []));
448
+ }),
449
+ materializeUninstall,
450
+ materializeDeactivate,
451
+ upsertSettingsEntry: Effect.fn("RuleManager.upsertSettingsEntry")(function* ({ ref, versionRange, materialization, }) {
452
+ const lockEntry = yield* buildLockEntry(ref, materialization);
453
+ if (Option.isNone(lockEntry)) {
454
+ yield* ws.setRuleEntry(ref.rule.name, {
455
+ source: "workspace",
456
+ enabled: true,
457
+ });
458
+ return;
459
+ }
460
+ if (lockEntry.value.type === "registry") {
461
+ yield* validateExactResolvedVersion(`rules.${ref.rule.name}.resolvedVersion`, lockEntry.value.resolvedVersion);
462
+ }
463
+ yield* ws.setRule({
464
+ name: ref.rule.name,
465
+ lockEntry: lockEntry.value,
466
+ versionRange,
467
+ });
468
+ }),
469
+ removeSettingsEntry: Effect.fn("RuleManager.removeSettingsEntry")(function* ({ target }) {
470
+ yield* ws.removeRuleSettings(target.name);
471
+ }),
472
+ upsertLockfileEntry: Effect.fn("RuleManager.upsertLockfileEntry")(function* ({ ref, materialization, }) {
473
+ const lockEntry = yield* buildLockEntry(ref, materialization);
474
+ if (Option.isNone(lockEntry)) {
475
+ yield* ws.removeRuleLock(ref.rule.name);
476
+ return;
477
+ }
478
+ if (lockEntry.value.type === "registry") {
479
+ yield* validateExactResolvedVersion(`rules.${ref.rule.name}.resolvedVersion`, lockEntry.value.resolvedVersion);
480
+ }
481
+ yield* ws.setRuleLock({
482
+ name: ref.rule.name,
483
+ lockEntry: lockEntry.value,
484
+ versionRange: Option.none(),
485
+ });
486
+ }),
487
+ removeLockfileEntry: Effect.fn("RuleManager.removeLockfileEntry")(function* ({ target }) {
488
+ yield* ws.removeRuleLock(target.name);
489
+ }),
490
+ };
491
+ }));
492
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Typed failure family for the skill manager and materialization. Fields are
3
+ * domain facts; the application error boundary owns rendering, codes, and
4
+ * suggestions.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ */
8
+ import type { AxmSkillCompatibilityUnavailable, AxmSkillIncompatible } from "@agentxm/extension-resolution";
9
+ declare const SkillDefinitionInvalid_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
10
+ readonly _tag: "SkillDefinitionInvalid";
11
+ } & Readonly<A>;
12
+ /**
13
+ * A skill source or agent configuration did not validate. `detail` carries
14
+ * the site's fact sentence verbatim.
15
+ */
16
+ export declare class SkillDefinitionInvalid extends SkillDefinitionInvalid_base<{
17
+ readonly detail: string;
18
+ readonly cause?: unknown;
19
+ }> {
20
+ }
21
+ declare const SkillMaterializationFailed_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
22
+ readonly _tag: "SkillMaterializationFailed";
23
+ } & Readonly<A>;
24
+ /** A skill artifact filesystem step failed; `detail` carries the site's fact sentence. */
25
+ export declare class SkillMaterializationFailed extends SkillMaterializationFailed_base<{
26
+ readonly detail: string;
27
+ readonly cause: unknown;
28
+ }> {
29
+ }
30
+ declare const SkillInstallStateMissing_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
31
+ readonly _tag: "SkillInstallStateMissing";
32
+ } & Readonly<A>;
33
+ /** A lock entry was requested before install recorded the package state. */
34
+ export declare class SkillInstallStateMissing extends SkillInstallStateMissing_base<{
35
+ readonly name: string;
36
+ readonly kind: "tree-integrity" | "content-identity" | "external-resolution";
37
+ }> {
38
+ }
39
+ /** Every failure the skill module constructs. */
40
+ export type SkillManagerError = SkillDefinitionInvalid | SkillMaterializationFailed | SkillInstallStateMissing | AxmSkillCompatibilityUnavailable | AxmSkillIncompatible;
41
+ export {};
42
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Typed failure family for the skill manager and materialization. Fields are
3
+ * domain facts; the application error boundary owns rendering, codes, and
4
+ * suggestions.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ */
8
+ import * as Data from "effect/Data";
9
+ /**
10
+ * A skill source or agent configuration did not validate. `detail` carries
11
+ * the site's fact sentence verbatim.
12
+ */
13
+ export class SkillDefinitionInvalid extends Data.TaggedError("SkillDefinitionInvalid") {
14
+ }
15
+ /** A skill artifact filesystem step failed; `detail` carries the site's fact sentence. */
16
+ export class SkillMaterializationFailed extends Data.TaggedError("SkillMaterializationFailed") {
17
+ }
18
+ /** A lock entry was requested before install recorded the package state. */
19
+ export class SkillInstallStateMissing extends Data.TaggedError("SkillInstallStateMissing") {
20
+ }
21
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Skill extension manager service.
3
+ *
4
+ * Implements ExtensionManager<SkillExtensionRef> with native/non-native
5
+ * branching in materializeInstall and agent symlink creation for all
6
+ * configured agents.
7
+ *
8
+ * @experimental This API is unstable and may change without notice.
9
+ */
10
+ import * as FileSystem from "effect/FileSystem";
11
+ import * as Path from "effect/Path";
12
+ import * as Layer from "effect/Layer";
13
+ import { WorkspaceMutations } from "@agentxm/workspace-state";
14
+ import { SkillManager } from "../managers.js";
15
+ import { CodingAgentRepository } from "@agentxm/workspace-projection";
16
+ export declare const SkillManagerLive: Layer.Layer<SkillManager, never, FileSystem.FileSystem | Path.Path | WorkspaceMutations | CodingAgentRepository>;
17
+ //# sourceMappingURL=manager.d.ts.map