@agentxm/extension-lifecycle 0.28.4-bootstrap.0

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 (84) hide show
  1. package/LICENSE +110 -0
  2. package/dist/src/configured-entry-resolution.d.ts +110 -0
  3. package/dist/src/configured-entry-resolution.js +723 -0
  4. package/dist/src/errors.d.ts +39 -0
  5. package/dist/src/errors.js +45 -0
  6. package/dist/src/failure-adapter.d.ts +35 -0
  7. package/dist/src/failure-adapter.js +23 -0
  8. package/dist/src/hooks/configured-agent-outcomes-provider.d.ts +18 -0
  9. package/dist/src/hooks/configured-agent-outcomes-provider.js +39 -0
  10. package/dist/src/hooks/manager.d.ts +15 -0
  11. package/dist/src/hooks/manager.js +751 -0
  12. package/dist/src/index.d.ts +38 -0
  13. package/dist/src/index.js +41 -0
  14. package/dist/src/internal/fs-helpers.d.ts +12 -0
  15. package/dist/src/internal/fs-helpers.js +13 -0
  16. package/dist/src/internal/integrity.d.ts +13 -0
  17. package/dist/src/internal/integrity.js +17 -0
  18. package/dist/src/knowledge/manager.d.ts +11 -0
  19. package/dist/src/knowledge/manager.js +655 -0
  20. package/dist/src/live.d.ts +18 -0
  21. package/dist/src/live.js +18 -0
  22. package/dist/src/mcps/manager.d.ts +16 -0
  23. package/dist/src/mcps/manager.js +213 -0
  24. package/dist/src/mcps/operations/artifact.d.ts +24 -0
  25. package/dist/src/mcps/operations/artifact.js +74 -0
  26. package/dist/src/mcps/operations/disable.d.ts +18 -0
  27. package/dist/src/mcps/operations/disable.js +113 -0
  28. package/dist/src/mcps/operations/enable.d.ts +18 -0
  29. package/dist/src/mcps/operations/enable.js +218 -0
  30. package/dist/src/mcps/operations/install.d.ts +59 -0
  31. package/dist/src/mcps/operations/install.js +566 -0
  32. package/dist/src/mcps/operations/sync-outcome.d.ts +10 -0
  33. package/dist/src/mcps/operations/sync-outcome.js +27 -0
  34. package/dist/src/mcps/operations/uninstall.d.ts +41 -0
  35. package/dist/src/mcps/operations/uninstall.js +189 -0
  36. package/dist/src/packs/dependency-resolution.d.ts +65 -0
  37. package/dist/src/packs/dependency-resolution.js +414 -0
  38. package/dist/src/packs/expansion.d.ts +59 -0
  39. package/dist/src/packs/expansion.js +44 -0
  40. package/dist/src/packs/manager.d.ts +16 -0
  41. package/dist/src/packs/manager.js +181 -0
  42. package/dist/src/packs/operations/install.d.ts +67 -0
  43. package/dist/src/packs/operations/install.js +156 -0
  44. package/dist/src/packs/resolved-dependency.d.ts +29 -0
  45. package/dist/src/packs/resolved-dependency.js +28 -0
  46. package/dist/src/registry-materialization.d.ts +52 -0
  47. package/dist/src/registry-materialization.js +67 -0
  48. package/dist/src/resolution-progress.d.ts +18 -0
  49. package/dist/src/resolution-progress.js +11 -0
  50. package/dist/src/resolution-timeout.d.ts +5 -0
  51. package/dist/src/resolution-timeout.js +11 -0
  52. package/dist/src/rules/manager.d.ts +20 -0
  53. package/dist/src/rules/manager.js +475 -0
  54. package/dist/src/skills/manager.d.ts +18 -0
  55. package/dist/src/skills/manager.js +346 -0
  56. package/dist/src/skills/materialization.d.ts +54 -0
  57. package/dist/src/skills/materialization.js +158 -0
  58. package/dist/src/skills/operations/disable.d.ts +31 -0
  59. package/dist/src/skills/operations/disable.js +110 -0
  60. package/dist/src/skills/operations/enable.d.ts +31 -0
  61. package/dist/src/skills/operations/enable.js +113 -0
  62. package/dist/src/skills/operations/install-result.d.ts +33 -0
  63. package/dist/src/skills/operations/install-result.js +10 -0
  64. package/dist/src/skills/operations/install.d.ts +67 -0
  65. package/dist/src/skills/operations/install.js +590 -0
  66. package/dist/src/skills/operations/source-hash.d.ts +2 -0
  67. package/dist/src/skills/operations/source-hash.js +6 -0
  68. package/dist/src/skills/operations/uninstall.d.ts +40 -0
  69. package/dist/src/skills/operations/uninstall.js +157 -0
  70. package/dist/src/skills/utils.d.ts +11 -0
  71. package/dist/src/skills/utils.js +29 -0
  72. package/dist/src/subagents/manager.d.ts +18 -0
  73. package/dist/src/subagents/manager.js +677 -0
  74. package/dist/src/subagents/operations/artifact.d.ts +18 -0
  75. package/dist/src/subagents/operations/artifact.js +43 -0
  76. package/dist/src/subagents/operations/disable.d.ts +32 -0
  77. package/dist/src/subagents/operations/disable.js +116 -0
  78. package/dist/src/subagents/operations/enable.d.ts +30 -0
  79. package/dist/src/subagents/operations/enable.js +160 -0
  80. package/dist/src/workflows/install-command/workflow.d.ts +57 -0
  81. package/dist/src/workflows/install-command/workflow.js +41 -0
  82. package/dist/src/workflows/uninstall-command/workflow.d.ts +42 -0
  83. package/dist/src/workflows/uninstall-command/workflow.js +29 -0
  84. package/package.json +62 -0
@@ -0,0 +1,346 @@
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 HttpClient from "effect/unstable/http/HttpClient";
12
+ import * as Path from "effect/Path";
13
+ import * as Array from "effect/Array";
14
+ import * as Effect from "effect/Effect";
15
+ import * as Layer from "effect/Layer";
16
+ import * as Option from "effect/Option";
17
+ import { sourceToLockEntry } from "@agentxm/workspace-state";
18
+ import { configuredSkillsToDiskRefs } from "@agentxm/extension-workspace";
19
+ import { enabledConfiguredEntries } from "@agentxm/extension-workspace";
20
+ import { SourceHostProviders } from "@agentxm/extension-sources";
21
+ import { WorkspaceMutations } from "@agentxm/workspace-state";
22
+ import { sanitizeName } from "@agentxm/workspace-state";
23
+ import { computePackageContentHash } from "@agentxm/workspace-state";
24
+ import { stripFileProtocol } from "../internal/fs-helpers.js";
25
+ import { makeWorkspaceRelativeSourcePath } from "@agentxm/extension-model/unstable/path-types";
26
+ import { removeIfExists } from "@agentxm/workspace-state";
27
+ import { CodingAgentRepository, SkillManager, applyProjectionPlans, planSingletonProjection, SkillDefinitionInvalid, SkillInstallStateMissing, } from "@agentxm/extension-workspace";
28
+ import {} from "@agentxm/extension-model/unstable/agents/types";
29
+ import { acceptedRegistryVersionForRef, validateExactResolvedVersion, } from "@agentxm/workspace-state";
30
+ import { computeSkillSourceHash } from "./operations/source-hash.js";
31
+ import { ensureSkillAgentArtifact, materializeSkillCanonical, removeSkillAgentArtifact, } from "./materialization.js";
32
+ import { configuredRowsByName } from "@agentxm/workspace-state";
33
+ import { acceptedCanonicalObservation, prepareAcceptedCanonicalTransition, removableAcceptedCanonicalPath, usableAcceptedCanonicalRef, } from "@agentxm/workspace-state";
34
+ import { isObservedInstalled } from "@agentxm/workspace-state";
35
+ // -----------------------------------------------------------------------------
36
+ // Helpers
37
+ // -----------------------------------------------------------------------------
38
+ // Build skill lock entry from ref
39
+ const buildSkillLockEntry = (ref, workspaceRelativeLocalSourcePath, contentIdentity, treeIntegrity) => sourceToLockEntry({
40
+ ref,
41
+ sourceName: Option.none(),
42
+ contentIdentity,
43
+ treeIntegrity,
44
+ workspaceRelativeLocalSourcePath,
45
+ });
46
+ // -----------------------------------------------------------------------------
47
+ // Live Layer
48
+ // -----------------------------------------------------------------------------
49
+ export const SkillManagerLive = Layer.effect(SkillManager, Effect.gen(function* () {
50
+ const ws = yield* WorkspaceMutations;
51
+ const fs = yield* FileSystem.FileSystem;
52
+ const httpClient = yield* HttpClient.HttpClient;
53
+ const path = yield* Path.Path;
54
+ const sources = yield* SourceHostProviders;
55
+ const agentRepo = yield* CodingAgentRepository;
56
+ const baseDir = ws.baseDir;
57
+ // Build a layer to provide FileSystem + Path to inner effects
58
+ const fsPathLayer = Layer.mergeAll(Layer.succeed(FileSystem.FileSystem, fs), Layer.succeed(Path.Path, path));
59
+ const registryMaterializationLayer = Layer.merge(fsPathLayer, Layer.succeed(HttpClient.HttpClient, httpClient));
60
+ // Provide FileSystem + Path to an effect that needs them
61
+ const provide = (effect) => Effect.provide(effect, fsPathLayer);
62
+ const provideRegistry = (effect) => Effect.provide(effect, registryMaterializationLayer);
63
+ const lastSourceHashes = new Map();
64
+ const lastTreeIntegrities = new Map();
65
+ const lastMaterializations = new Map();
66
+ const materializeInstall = Effect.fn("SkillManager.materializeInstall")(function* ({ ref, force }) {
67
+ const sanitized = sanitizeName(ref.skill.name);
68
+ const lockedEntry = yield* ws.getLockedSkill(ref.skill.name);
69
+ const lockedVersion = ref.refType === "registry" ? acceptedRegistryVersionForRef(lockedEntry, ref) : undefined;
70
+ const materialized = yield* materializeSkillCanonical({
71
+ ref,
72
+ sanitizedName: sanitized,
73
+ fs,
74
+ pathService: path,
75
+ baseDir,
76
+ layout: ws.layout,
77
+ sources,
78
+ provide,
79
+ provideRegistry,
80
+ reuse: {
81
+ force: force === true,
82
+ lockedVersion,
83
+ lockedTreeIntegrity: Option.isSome(lockedEntry)
84
+ ? lockedEntry.value.treeIntegrity
85
+ : undefined,
86
+ },
87
+ });
88
+ const skillSrcPath = materialized.skillSrcPath;
89
+ const configuredAgents = yield* agentRepo
90
+ .getMaterializationAgents()
91
+ .pipe(Effect.provideService(WorkspaceMutations, ws));
92
+ const resolved = yield* Effect.forEach(configuredAgents, (agent) => agent.resolveEffectiveSkillsDir({ workspaceRoot: baseDir }).pipe(Effect.provide(fsPathLayer), Effect.map((outcome) => ({ agent, outcome }))), { concurrency: "unbounded" });
93
+ const misconfigured = Array.filter(resolved, ({ outcome }) => outcome._tag === "misconfigured");
94
+ if (misconfigured.length > 0) {
95
+ return yield* new SkillDefinitionInvalid({
96
+ detail: "One or more configured agents have invalid skills directory settings",
97
+ });
98
+ }
99
+ const installTargets = [];
100
+ for (const { agent, outcome } of resolved) {
101
+ if (outcome._tag === "supported") {
102
+ installTargets.push({ agentId: agent.id, dir: path.normalize(outcome.dir) });
103
+ }
104
+ }
105
+ const locations = new Map();
106
+ for (const target of installTargets) {
107
+ const existing = locations.get(target.dir);
108
+ if (existing === undefined) {
109
+ locations.set(target.dir, { dir: target.dir, agentIds: [target.agentId] });
110
+ }
111
+ else if (!existing.agentIds.includes(target.agentId)) {
112
+ existing.agentIds.push(target.agentId);
113
+ }
114
+ }
115
+ yield* applyProjectionPlans([...locations.values()].map((location) => {
116
+ const targetFile = path.join(location.dir, sanitized);
117
+ return planSingletonProjection({
118
+ unitId: "skill:agent-skill-directory",
119
+ targetFile,
120
+ contributor: ref,
121
+ adapter: {
122
+ observe: () => Effect.succeed({
123
+ unitId: "skill:agent-skill-directory",
124
+ path: path.relative(baseDir, targetFile),
125
+ present: false,
126
+ current: false,
127
+ expectedContributors: [ref.skill.name],
128
+ observedContributors: [],
129
+ }),
130
+ apply: () => ensureSkillAgentArtifact({
131
+ canonicalSkillSrcPath: skillSrcPath,
132
+ targetDir: location.dir,
133
+ sanitizedName: sanitized,
134
+ pathService: path,
135
+ baseDir,
136
+ provide,
137
+ }),
138
+ },
139
+ });
140
+ }));
141
+ const sourceHash = ref.refType === "workspace"
142
+ ? ref.sourceHash
143
+ : ref.refType === "registry"
144
+ ? yield* provide(computePackageContentHash(path.dirname(skillSrcPath)))
145
+ : yield* provide(computeSkillSourceHash(skillSrcPath));
146
+ lastSourceHashes.set(ref.skill.name, sourceHash);
147
+ if (ref.refType !== "workspace") {
148
+ if (materialized.treeIntegrity === undefined) {
149
+ return yield* new SkillInstallStateMissing({
150
+ name: ref.skill.name,
151
+ kind: "tree-integrity",
152
+ });
153
+ }
154
+ lastTreeIntegrities.set(ref.skill.name, materialized.treeIntegrity);
155
+ }
156
+ lastMaterializations.set(ref.skill.name, {
157
+ agents: Array.dedupe(installTargets
158
+ .map((target) => target.agentId)
159
+ .filter((agentId) => agentId !== "universal")),
160
+ targets: [...locations.values()].map((location) => {
161
+ const agentIds = location.agentIds.filter((agentId) => agentId !== "universal");
162
+ return {
163
+ path: path.relative(baseDir, path.join(location.dir, sanitized)),
164
+ ...(agentIds.length === 0 ? {} : { agentIds }),
165
+ };
166
+ }),
167
+ });
168
+ });
169
+ const makeMaterializeRemoval = (retainCanonical) => Effect.fn("SkillManager.materializeRemoval")(function* ({ target }) {
170
+ const sanitized = sanitizeName(target.name);
171
+ const configuredAgents = yield* agentRepo
172
+ .getMaterializationAgents()
173
+ .pipe(Effect.provideService(WorkspaceMutations, ws));
174
+ const resolved = yield* Effect.forEach(configuredAgents, (agent) => agent.resolveEffectiveSkillsDir({ workspaceRoot: baseDir }).pipe(Effect.provide(fsPathLayer), Effect.map((outcome) => ({ agent, outcome }))), { concurrency: "unbounded" });
175
+ const uninstallTargets = [];
176
+ for (const { outcome } of resolved) {
177
+ if (outcome._tag === "supported") {
178
+ uninstallTargets.push(path.normalize(outcome.dir));
179
+ }
180
+ }
181
+ const distinctDirs = Array.dedupe(uninstallTargets);
182
+ yield* applyProjectionPlans(distinctDirs.map((dir) => {
183
+ const targetFile = path.join(dir, sanitized);
184
+ return planSingletonProjection({
185
+ unitId: "skill:agent-skill-directory",
186
+ targetFile,
187
+ contributor: target,
188
+ adapter: {
189
+ observe: () => Effect.succeed({
190
+ unitId: "skill:agent-skill-directory",
191
+ path: path.relative(baseDir, targetFile),
192
+ present: true,
193
+ current: false,
194
+ expectedContributors: [],
195
+ observedContributors: [target.name],
196
+ }),
197
+ apply: () => removeSkillAgentArtifact({
198
+ fs,
199
+ pathService: path,
200
+ targetDir: dir,
201
+ sanitizedName: sanitized,
202
+ }),
203
+ },
204
+ });
205
+ }));
206
+ if (!retainCanonical) {
207
+ const canonical = yield* provide(acceptedCanonicalObservation({ workspace: ws, type: "skill", name: target.name }));
208
+ const packageRoot = removableAcceptedCanonicalPath(canonical);
209
+ if (Option.isSome(packageRoot))
210
+ yield* removeIfExists(fs, packageRoot.value);
211
+ }
212
+ });
213
+ const materializeUninstall = makeMaterializeRemoval(false);
214
+ const materializeDeactivate = makeMaterializeRemoval(true);
215
+ return {
216
+ type: "skill",
217
+ runTransaction: ws.runTransaction,
218
+ isInstalled: Effect.fn("SkillManager.isInstalled")(function* ({ target, }) {
219
+ return yield* isObservedInstalled(ws, "skill", target.name);
220
+ }),
221
+ materializeInstall,
222
+ prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
223
+ workspace: ws,
224
+ type: "skill",
225
+ name: ref.skill.name,
226
+ ref,
227
+ })),
228
+ getLastMaterialization: ({ target }) => Effect.succeed(lastMaterializations.get(target.name) ?? {
229
+ agents: [],
230
+ targets: [],
231
+ }),
232
+ getConfiguredSource: Effect.fn("SkillManager.getConfiguredSource")(function* ({ target }) {
233
+ const configured = yield* ws.getConfiguredSkillEntries();
234
+ const entry = configured[target.name];
235
+ if (entry?.origin === "bundled") {
236
+ return Option.some(`bundled:@agentxm/skills/${target.name}`);
237
+ }
238
+ return Option.fromUndefinedOr(entry?.source);
239
+ }),
240
+ listMaterializable: Effect.fn("SkillManager.listMaterializable")(function* () {
241
+ const configured = yield* ws.records
242
+ .rows("skill")
243
+ .pipe(Effect.map(configuredRowsByName));
244
+ const configuredEntries = yield* ws.getConfiguredSkillEntries();
245
+ const configuredWithoutBundled = Object.fromEntries(Object.entries(configured).filter(([name]) => configuredEntries[name]?.origin !== "bundled"));
246
+ const workspaceRefs = yield* configuredSkillsToDiskRefs({ fs, path, baseDir, scope: ws.scope, layout: ws.layout }, configuredWithoutBundled);
247
+ const trustedRefs = yield* Effect.forEach(enabledConfiguredEntries(configured), ([name]) => configuredEntries[name]?.origin === "bundled"
248
+ ? Effect.succeed(Option.none())
249
+ : provide(usableAcceptedCanonicalRef({ workspace: ws, type: "skill", name }).pipe(Effect.map(Option.filter((ref) => ref.type === "skill")))), { concurrency: "unbounded" });
250
+ const refsByName = new Map(workspaceRefs.map((ref) => [ref.skill.name, ref]));
251
+ for (const ref of trustedRefs) {
252
+ if (Option.isSome(ref))
253
+ refsByName.set(ref.value.skill.name, ref.value);
254
+ }
255
+ return [...refsByName.values()];
256
+ }),
257
+ materializeUninstall,
258
+ materializeDeactivate,
259
+ upsertSettingsEntry: Effect.fn("SkillManager.upsertSettingsEntry")(function* ({ ref, versionRange, }) {
260
+ const workspaceRelativeLocalSourcePath = ref.refType === "local"
261
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
262
+ : Option.none();
263
+ if (ref.refType === "local" && Option.isNone(workspaceRelativeLocalSourcePath)) {
264
+ return yield* new SkillDefinitionInvalid({
265
+ detail: `Local skill source path must stay within the workspace root: ${ref.source.path}`,
266
+ });
267
+ }
268
+ const sourceHash = lastSourceHashes.get(ref.skill.name);
269
+ const treeIntegrity = lastTreeIntegrities.get(ref.skill.name);
270
+ if (ref.refType === "workspace") {
271
+ return yield* ws.setSkillEntry(ref.skill.name, {
272
+ source: "workspace",
273
+ enabled: true,
274
+ });
275
+ }
276
+ if (sourceHash === undefined || treeIntegrity === undefined) {
277
+ return yield* new SkillInstallStateMissing({
278
+ name: ref.skill.name,
279
+ kind: "content-identity",
280
+ });
281
+ }
282
+ const lockEntry = buildSkillLockEntry(ref, workspaceRelativeLocalSourcePath, sourceHash, treeIntegrity);
283
+ if (lockEntry === undefined) {
284
+ return yield* new SkillInstallStateMissing({
285
+ name: ref.skill.name,
286
+ kind: "external-resolution",
287
+ });
288
+ }
289
+ if (lockEntry.type === "registry") {
290
+ yield* validateExactResolvedVersion(`skills.${ref.skill.name}.resolvedVersion`, lockEntry.resolvedVersion);
291
+ }
292
+ return yield* ws.setSkill({
293
+ name: ref.skill.name,
294
+ lockEntry,
295
+ versionRange,
296
+ });
297
+ }),
298
+ removeSettingsEntry: ({ target }) => ws
299
+ .removeSkillFromSettings(target.name)
300
+ .pipe(Effect.withSpan("SkillManager.removeSettingsEntry")),
301
+ upsertLockfileEntry: Effect.fn("SkillManager.upsertLockfileEntry")(function* ({ ref, }) {
302
+ const workspaceRelativeLocalSourcePath = ref.refType === "local"
303
+ ? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
304
+ : Option.none();
305
+ if (ref.refType === "local" && Option.isNone(workspaceRelativeLocalSourcePath)) {
306
+ return yield* new SkillDefinitionInvalid({
307
+ detail: `Local skill source path must stay within the workspace root: ${ref.source.path}`,
308
+ });
309
+ }
310
+ if (ref.refType === "workspace") {
311
+ yield* ws.removeSkillLock(ref.skill.name);
312
+ return;
313
+ }
314
+ const sourceHash = lastSourceHashes.get(ref.skill.name);
315
+ const treeIntegrity = lastTreeIntegrities.get(ref.skill.name);
316
+ if (sourceHash === undefined || treeIntegrity === undefined) {
317
+ return yield* new SkillInstallStateMissing({
318
+ name: ref.skill.name,
319
+ kind: "content-identity",
320
+ });
321
+ }
322
+ const lockEntry = buildSkillLockEntry(ref, workspaceRelativeLocalSourcePath, sourceHash, treeIntegrity);
323
+ if (lockEntry === undefined) {
324
+ return yield* new SkillInstallStateMissing({
325
+ name: ref.skill.name,
326
+ kind: "external-resolution",
327
+ });
328
+ }
329
+ if (lockEntry.type === "registry") {
330
+ yield* validateExactResolvedVersion(`skills.${ref.skill.name}.resolvedVersion`, lockEntry.resolvedVersion);
331
+ }
332
+ return yield* ws.setSkillLock({
333
+ name: ref.skill.name,
334
+ lockEntry,
335
+ versionRange: Option.none(),
336
+ });
337
+ }),
338
+ removeLockfileEntry: ({ target }) => ws
339
+ .removeSkillLock(target.name)
340
+ .pipe(Effect.withSpan("SkillManager.removeLockfileEntry")),
341
+ };
342
+ }));
343
+ // -----------------------------------------------------------------------------
344
+ // Internal materialization helpers
345
+ // -----------------------------------------------------------------------------
346
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1,54 @@
1
+ import * as FileSystem from "effect/FileSystem";
2
+ import * as HttpClient from "effect/unstable/http/HttpClient";
3
+ import * as Path from "effect/Path";
4
+ import * as Effect from "effect/Effect";
5
+ import { SkillMaterializationFailed } from "@agentxm/extension-workspace";
6
+ import { type TreeIntegrity } from "@agentxm/workspace-state";
7
+ import type { SourceHostProvidersService } from "@agentxm/extension-sources";
8
+ import type { SkillExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/skill";
9
+ import type { WorkspaceLayout } from "@agentxm/workspace-state";
10
+ export type ProvideFs = <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem | Path.Path>>;
11
+ export type ProvideRegistryMaterialization = <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<R, FileSystem.FileSystem | HttpClient.HttpClient | Path.Path>>;
12
+ /** Reuse inputs sourced from the caller's operation context and lockfile. */
13
+ export type CanonicalReuseContext = {
14
+ readonly force: boolean;
15
+ readonly lockedVersion: string | undefined;
16
+ readonly lockedTreeIntegrity: TreeIntegrity | undefined;
17
+ };
18
+ export interface MaterializedSkillCanonical {
19
+ readonly skillSrcPath: string;
20
+ readonly treeIntegrity?: TreeIntegrity;
21
+ }
22
+ export declare const materializeSkillCanonical: (args: {
23
+ readonly ref: SkillExtensionRef;
24
+ readonly sanitizedName: string;
25
+ readonly fs: FileSystem.FileSystem;
26
+ readonly pathService: Path.Path;
27
+ readonly baseDir: string;
28
+ readonly layout: WorkspaceLayout;
29
+ readonly sources: SourceHostProvidersService;
30
+ readonly provide: ProvideFs;
31
+ readonly provideRegistry: ProvideRegistryMaterialization;
32
+ readonly reuse?: CanonicalReuseContext;
33
+ }) => Effect.Effect<{
34
+ skillSrcPath: string & import("effect/Brand").Brand<"AbsolutePath">;
35
+ treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
36
+ }, import("@agentxm/extension-workspace").CanonicalDirectoryReplacementError | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/extension-workspace").PackageCopyFailed | import("@agentxm/extension-workspace").CanonicalPackageProbeFailed | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible, never> | Effect.Effect<{
37
+ skillSrcPath: string & import("effect/Brand").Brand<"AbsolutePath">;
38
+ treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
39
+ }, import("@agentxm/extension-workspace").CoupledDependencyFailure | import("@agentxm/extension-workspace").ArchiveIntegrityMismatch | import("@agentxm/extension-workspace").CanonicalDirectoryReplacementError | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/extension-workspace").CanonicalPackageProbeFailed | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible, never> | Effect.Effect<MaterializedSkillCanonical, import("@agentxm/workspace-state").PathTraversalDetected | import("@agentxm/extension-workspace").AxmSkillCompatibilityUnavailable | import("@agentxm/extension-workspace").AxmSkillIncompatible, never>;
40
+ export declare const ensureSkillAgentArtifact: (args: {
41
+ readonly canonicalSkillSrcPath: string;
42
+ readonly targetDir: string;
43
+ readonly sanitizedName: string;
44
+ readonly pathService: Path.Path;
45
+ readonly baseDir: string;
46
+ readonly provide: ProvideFs;
47
+ }) => Effect.Effect<void, SkillMaterializationFailed, never>;
48
+ export declare const removeSkillAgentArtifact: (args: {
49
+ readonly fs: FileSystem.FileSystem;
50
+ readonly pathService: Path.Path;
51
+ readonly targetDir: string;
52
+ readonly sanitizedName: string;
53
+ }) => Effect.Effect<void, SkillMaterializationFailed, never>;
54
+ //# sourceMappingURL=materialization.d.ts.map
@@ -0,0 +1,158 @@
1
+ import * as FileSystem from "effect/FileSystem";
2
+ import * as HttpClient from "effect/unstable/http/HttpClient";
3
+ import * as Path from "effect/Path";
4
+ import * as Effect from "effect/Effect";
5
+ import * as Option from "effect/Option";
6
+ import { SkillMaterializationFailed, validateAxmSkillCandidate, } from "@agentxm/extension-workspace";
7
+ import { canReuseExternalPackage, canReuseInstalledPackage, materializeExternalPackageWithTreeIntegrity, } from "@agentxm/extension-workspace";
8
+ import { materializeRegistryPackageWithTreeIntegrity } from "../registry-materialization.js";
9
+ import { validatePathSafety } from "@agentxm/workspace-state";
10
+ import { computeMaterializedTreeIntegrity } from "@agentxm/workspace-state";
11
+ import { copyExtensionDirectory } from "@agentxm/extension-workspace";
12
+ import { computeSkillPathsForLayout } from "@agentxm/workspace-state";
13
+ import { stripFileProtocol } from "../internal/fs-helpers.js";
14
+ import { isPathSafe } from "@agentxm/workspace-state";
15
+ import { createSymlink } from "@agentxm/workspace-state";
16
+ import { protectWorkspacePath } from "@agentxm/workspace-state";
17
+ const replaceExternalCanonical = (fs, pathService, baseDir, sanitizedName, sourcePath, copyTarget, reuse, provide) => Effect.gen(function* () {
18
+ const useExisting = yield* provide(canReuseExternalPackage({
19
+ installedPath: copyTarget,
20
+ force: reuse.force,
21
+ existsFailureDetail: (target) => `Failed to check if canonical path exists: ${target}`,
22
+ }));
23
+ if (useExisting && reuse.lockedTreeIntegrity !== undefined) {
24
+ const observedTree = yield* provide(computeMaterializedTreeIntegrity(copyTarget));
25
+ if (observedTree === reuse.lockedTreeIntegrity)
26
+ return reuse.lockedTreeIntegrity;
27
+ }
28
+ const materialized = yield* provide(materializeExternalPackageWithTreeIntegrity({
29
+ baseDir,
30
+ canonicalPath: copyTarget,
31
+ sourceLocation: sourcePath,
32
+ copyFailureCode: "internal",
33
+ copyFailureDetail: (target) => `Failed to copy skill files to ${target}`,
34
+ }));
35
+ return materialized.treeIntegrity;
36
+ });
37
+ const materializeGitHosted = (ref, sanitizedName, fs, pathService, baseDir, layout, provide, reuse) => Effect.gen(function* () {
38
+ const { canonicalPath, skillSrcPath } = computeSkillPathsForLayout(pathService.join, layout, ref, sanitizedName);
39
+ yield* validatePathSafety(pathService, baseDir, canonicalPath);
40
+ const packageRoot = stripFileProtocol(ref.location);
41
+ const sourceSkillPath = ref.portable === true ? packageRoot : pathService.join(packageRoot, "src");
42
+ yield* provide(validateAxmSkillCandidate({
43
+ ref,
44
+ packageRoot,
45
+ skillSourcePath: sourceSkillPath,
46
+ }));
47
+ const isSelfCopy = pathService.resolve(packageRoot) === pathService.resolve(canonicalPath);
48
+ const treeIntegrity = isSelfCopy
49
+ ? yield* provide(computeMaterializedTreeIntegrity(canonicalPath))
50
+ : yield* replaceExternalCanonical(fs, pathService, baseDir, sanitizedName, packageRoot, canonicalPath, reuse, provide);
51
+ return { skillSrcPath, treeIntegrity };
52
+ });
53
+ const materializeLocal = (ref, sanitizedName, fs, pathService, baseDir, layout, provide, reuse) => Effect.gen(function* () {
54
+ const { canonicalPath, skillSrcPath } = computeSkillPathsForLayout(pathService.join, layout, ref, sanitizedName);
55
+ yield* validatePathSafety(pathService, baseDir, canonicalPath);
56
+ const packageRoot = stripFileProtocol(ref.location);
57
+ const sourceSkillPath = ref.portable === true ? packageRoot : pathService.join(packageRoot, "src");
58
+ yield* provide(validateAxmSkillCandidate({
59
+ ref,
60
+ packageRoot,
61
+ skillSourcePath: sourceSkillPath,
62
+ }));
63
+ const isSelfCopy = pathService.resolve(packageRoot) === pathService.resolve(canonicalPath);
64
+ const treeIntegrity = isSelfCopy
65
+ ? yield* provide(computeMaterializedTreeIntegrity(canonicalPath))
66
+ : yield* replaceExternalCanonical(fs, pathService, baseDir, sanitizedName, packageRoot, canonicalPath, reuse, provide);
67
+ return { skillSrcPath, treeIntegrity };
68
+ });
69
+ const materializeRegistry = (ref, sanitizedName, fs, pathService, baseDir, layout, provide, provideRegistry, reuse) => Effect.scoped(Effect.gen(function* () {
70
+ const source = ref;
71
+ const { canonicalPath, skillSrcPath } = computeSkillPathsForLayout(pathService.join, layout, source, sanitizedName);
72
+ yield* validatePathSafety(pathService, baseDir, canonicalPath);
73
+ const useExisting = yield* provide(canReuseInstalledPackage({
74
+ installedPath: canonicalPath,
75
+ force: reuse.force,
76
+ refVersion: ref.version,
77
+ hasIntegrity: Option.isSome(ref.integrity),
78
+ ...(reuse.lockedVersion === undefined ? {} : { lockedVersion: reuse.lockedVersion }),
79
+ existsFailureDetail: (target) => `Failed to check if canonical path exists: ${target}`,
80
+ }));
81
+ if (useExisting && reuse.lockedTreeIntegrity !== undefined) {
82
+ const observedTree = yield* provide(computeMaterializedTreeIntegrity(canonicalPath));
83
+ if (observedTree === reuse.lockedTreeIntegrity) {
84
+ yield* provide(validateAxmSkillCandidate({
85
+ ref,
86
+ packageRoot: canonicalPath,
87
+ skillSourcePath: skillSrcPath,
88
+ }));
89
+ return { skillSrcPath, treeIntegrity: reuse.lockedTreeIntegrity };
90
+ }
91
+ }
92
+ const materialized = yield* provideRegistry(materializeRegistryPackageWithTreeIntegrity({
93
+ baseDir,
94
+ destinationPath: canonicalPath,
95
+ sourceLocation: ref.source.location,
96
+ owner: ref.owner,
97
+ type: "skill",
98
+ name: ref.name,
99
+ version: ref.version,
100
+ integrity: ref.integrity,
101
+ messages: {
102
+ integrityMismatchDetail: `Integrity mismatch for ${ref.name}@${ref.version}`,
103
+ },
104
+ validate: (stagingPath) => provide(validateAxmSkillCandidate({
105
+ ref,
106
+ packageRoot: stagingPath,
107
+ skillSourcePath: pathService.join(stagingPath, "src"),
108
+ }).pipe(Effect.asVoid)),
109
+ }));
110
+ return { skillSrcPath, treeIntegrity: materialized.treeIntegrity };
111
+ }));
112
+ const materializeWorkspace = (ref, pathService, baseDir, provide) => Effect.gen(function* () {
113
+ yield* validatePathSafety(pathService, baseDir, ref.location);
114
+ const skillSourcePath = pathService.join(ref.location, "src");
115
+ yield* provide(validateAxmSkillCandidate({
116
+ ref,
117
+ packageRoot: ref.location,
118
+ skillSourcePath,
119
+ }));
120
+ const materialized = { skillSrcPath: skillSourcePath };
121
+ return materialized;
122
+ });
123
+ export const materializeSkillCanonical = (args) => {
124
+ switch (args.ref.refType) {
125
+ case "git-hosted":
126
+ return materializeGitHosted(args.ref, args.sanitizedName, args.fs, args.pathService, args.baseDir, args.layout, args.provide, args.reuse ?? { force: false, lockedVersion: undefined, lockedTreeIntegrity: undefined });
127
+ case "local":
128
+ return materializeLocal(args.ref, args.sanitizedName, args.fs, args.pathService, args.baseDir, args.layout, args.provide, args.reuse ?? { force: false, lockedVersion: undefined, lockedTreeIntegrity: undefined });
129
+ case "registry":
130
+ return materializeRegistry(args.ref, args.sanitizedName, args.fs, args.pathService, args.baseDir, args.layout, args.provide, args.provideRegistry, args.reuse ?? { force: false, lockedVersion: undefined, lockedTreeIntegrity: undefined });
131
+ case "workspace":
132
+ return materializeWorkspace(args.ref, args.pathService, args.baseDir, args.provide);
133
+ }
134
+ };
135
+ export const ensureSkillAgentArtifact = (args) => Effect.gen(function* () {
136
+ const agentSkillPath = args.pathService.join(args.targetDir, args.sanitizedName);
137
+ if (!isPathSafe(args.pathService, args.baseDir, agentSkillPath)) {
138
+ return;
139
+ }
140
+ yield* args.provide(createSymlink({
141
+ target: args.canonicalSkillSrcPath,
142
+ link: agentSkillPath,
143
+ }).pipe(Effect.catch(() => copyExtensionDirectory(args.canonicalSkillSrcPath, agentSkillPath).pipe(
144
+ // If the copy fallback also fails, surface it — otherwise sync
145
+ // reports success with no materialized skill artifact.
146
+ Effect.mapError((cause) => new SkillMaterializationFailed({
147
+ detail: `Failed to materialize skill artifact at ${agentSkillPath}`,
148
+ cause,
149
+ }))))));
150
+ });
151
+ export const removeSkillAgentArtifact = (args) => {
152
+ const target = args.pathService.join(args.targetDir, args.sanitizedName);
153
+ return protectWorkspacePath(target).pipe(Effect.andThen(args.fs.remove(target, { recursive: true, force: true })), Effect.mapError((cause) => new SkillMaterializationFailed({
154
+ detail: `Failed to remove skill artifact at ${target}`,
155
+ cause,
156
+ })));
157
+ };
158
+ //# sourceMappingURL=materialization.js.map
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Disable skill executor — removes agent symlinks but preserves canonical files.
3
+ *
4
+ * Materialized artifacts are observed directly. Accepted-resolution rows identify
5
+ * source content but do not prove that canonical or projected files exist.
6
+ *
7
+ * @experimental This API is unstable and may change without notice.
8
+ */
9
+ import * as FileSystem from "effect/FileSystem";
10
+ import * as Path from "effect/Path";
11
+ import { LifecycleFailureAdapter } from "../../failure-adapter.js";
12
+ import type { OperationHandler } from "@agentxm/workspace-operations";
13
+ import type { Operation } from "@agentxm/workspace-operations";
14
+ import { WorkspaceMutations } from "@agentxm/workspace-state";
15
+ /**
16
+ * Disable a skill (remove files but keep settings/lockfile entry).
17
+ *
18
+ * @experimental This API is unstable and may change without notice.
19
+ */
20
+ export type DisableSkillOperation = Operation<"disable-skill", {
21
+ readonly skillName: string;
22
+ }>;
23
+ /**
24
+ * Disable-skill operation handler.
25
+ *
26
+ * Determines lifecycle from the workspace read model, removes observable
27
+ * materialized targets, and promotes implicit pack members to a direct disabled
28
+ * preference. Canonical files are preserved for later re-enablement.
29
+ */
30
+ export declare const disableSkill: OperationHandler<DisableSkillOperation, FileSystem.FileSystem | Path.Path | WorkspaceMutations | LifecycleFailureAdapter>;
31
+ //# sourceMappingURL=disable.d.ts.map