@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.
- package/dist/src/hooks/manager.d.ts +0 -5
- package/dist/src/hooks/manager.js +73 -36
- package/dist/src/index.d.ts +1 -6
- package/dist/src/index.js +0 -9
- package/dist/src/knowledge/manager.d.ts +0 -1
- package/dist/src/knowledge/manager.js +36 -29
- package/dist/src/manager-contract.d.ts +16 -18
- package/dist/src/managers.d.ts +12 -1
- package/dist/src/managers.js +0 -13
- package/dist/src/mcps/manager.js +29 -42
- package/dist/src/packs/manager.js +21 -18
- package/dist/src/rules/manager.d.ts +0 -5
- package/dist/src/rules/manager.js +22 -32
- package/dist/src/skills/errors.d.ts +2 -1
- package/dist/src/skills/manager.js +45 -53
- package/dist/src/skills/materialization.d.ts +2 -2
- package/dist/src/skills/skill-artifact.d.ts +5 -1
- package/dist/src/subagents/manager.js +31 -52
- package/dist/src/testing.d.ts +0 -66
- package/dist/src/testing.js +1 -89
- package/package.json +19 -22
- package/dist/src/desired-state/errors.d.ts +0 -22
- package/dist/src/desired-state/errors.js +0 -22
- package/dist/src/desired-state/retained-materialization.d.ts +0 -58
- package/dist/src/desired-state/retained-materialization.js +0 -119
- package/dist/src/extensions/operations.d.ts +0 -252
- package/dist/src/extensions/operations.js +0 -631
- package/dist/src/mcps/authored-materialization.d.ts +0 -28
- package/dist/src/mcps/authored-materialization.js +0 -34
- package/dist/src/mcps/install-operation.d.ts +0 -92
- package/dist/src/mcps/install-operation.js +0 -548
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { usableAcceptedCanonical } from "@agentxm/workspace-state";
|
|
2
|
+
import { LifecyclePostconditionViolated } from "../extensions/errors.js";
|
|
1
3
|
/**
|
|
2
4
|
* Rule manager service.
|
|
3
5
|
*
|
|
@@ -424,6 +426,22 @@ export const RuleManagerLive = Layer.effect(RuleManager, Effect.gen(function* ()
|
|
|
424
426
|
aggregateProjectionObservation: Ref.get(lastProjection),
|
|
425
427
|
isInstalled: ({ target }) => isObservedInstalled(ws, "rule", target.name).pipe(Effect.withSpan("RuleManager.isInstalled")),
|
|
426
428
|
materializeInstall,
|
|
429
|
+
acquireCanonical: materializeInstall,
|
|
430
|
+
materializeRetained: ({ target }) => Effect.gen(function* () {
|
|
431
|
+
const canonical = yield* usableAcceptedCanonical({
|
|
432
|
+
workspace: ws,
|
|
433
|
+
type: "rule",
|
|
434
|
+
name: target.name,
|
|
435
|
+
});
|
|
436
|
+
if (Option.isNone(canonical) || canonical.value.ref.type !== "rule") {
|
|
437
|
+
return yield* new LifecyclePostconditionViolated({
|
|
438
|
+
postcondition: "materialize-observable",
|
|
439
|
+
targetType: "rule",
|
|
440
|
+
targetName: target.name,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
return yield* materializeInstall({ ref: canonical.value.ref });
|
|
444
|
+
}),
|
|
427
445
|
prepareSourceTransition: ({ ref }) => provide(prepareAcceptedCanonicalTransition({
|
|
428
446
|
workspace: ws,
|
|
429
447
|
type: "rule",
|
|
@@ -448,45 +466,17 @@ export const RuleManagerLive = Layer.effect(RuleManager, Effect.gen(function* ()
|
|
|
448
466
|
}),
|
|
449
467
|
materializeUninstall,
|
|
450
468
|
materializeDeactivate,
|
|
451
|
-
|
|
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, }) {
|
|
469
|
+
acceptedResolution: Effect.fn("RuleManager.acceptedResolution")(function* ({ ref, materialization, }) {
|
|
473
470
|
const lockEntry = yield* buildLockEntry(ref, materialization);
|
|
474
471
|
if (Option.isNone(lockEntry)) {
|
|
475
|
-
|
|
476
|
-
return;
|
|
472
|
+
return Option.none();
|
|
477
473
|
}
|
|
478
474
|
if (lockEntry.value.type === "registry") {
|
|
479
475
|
yield* validateExactResolvedVersion(`rules.${ref.rule.name}.resolvedVersion`, lockEntry.value.resolvedVersion);
|
|
480
476
|
}
|
|
481
|
-
|
|
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);
|
|
477
|
+
return Option.some({ key: ref.rule.name, entry: lockEntry.value });
|
|
489
478
|
}),
|
|
479
|
+
withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
|
|
490
480
|
};
|
|
491
481
|
}));
|
|
492
482
|
//# sourceMappingURL=manager.js.map
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @experimental This API is unstable and may change without notice.
|
|
7
7
|
*/
|
|
8
|
-
import type { AxmSkillCompatibilityUnavailable
|
|
8
|
+
import type { AxmSkillCompatibilityUnavailable } from "@agentxm/cli-maintenance/official-skill/application";
|
|
9
|
+
import type { AxmSkillIncompatible } from "@agentxm/cli-maintenance/official-skill/domain";
|
|
9
10
|
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
11
|
readonly _tag: "SkillDefinitionInvalid";
|
|
11
12
|
} & Readonly<A>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { usableAcceptedCanonical } from "@agentxm/workspace-state";
|
|
2
|
+
import { LifecyclePostconditionViolated } from "../extensions/errors.js";
|
|
1
3
|
/**
|
|
2
4
|
* Skill extension manager service.
|
|
3
5
|
*
|
|
@@ -216,6 +218,45 @@ export const SkillManagerLive = Layer.effect(SkillManager, Effect.gen(function*
|
|
|
216
218
|
return yield* isObservedInstalled(ws, "skill", target.name);
|
|
217
219
|
}),
|
|
218
220
|
materializeInstall,
|
|
221
|
+
acquireCanonical: ({ ref, force }) => Effect.gen(function* () {
|
|
222
|
+
const locked = yield* ws.getLockedSkill(ref.skill.name);
|
|
223
|
+
const materialized = yield* materializeSkillCanonical({
|
|
224
|
+
ref,
|
|
225
|
+
sanitizedName: sanitizeName(ref.skill.name),
|
|
226
|
+
baseDir,
|
|
227
|
+
layout: ws.layout,
|
|
228
|
+
reuse: {
|
|
229
|
+
force: force === true,
|
|
230
|
+
lockedVersion: ref.refType === "registry" ? acceptedRegistryVersionForRef(locked, ref) : undefined,
|
|
231
|
+
lockedTreeIntegrity: Option.isSome(locked) ? locked.value.treeIntegrity : undefined,
|
|
232
|
+
},
|
|
233
|
+
});
|
|
234
|
+
const sourceHash = ref.refType === "workspace"
|
|
235
|
+
? ref.sourceHash
|
|
236
|
+
: ref.refType === "registry"
|
|
237
|
+
? yield* computePackageContentHash(path.dirname(materialized.skillSrcPath))
|
|
238
|
+
: yield* computeSkillSourceHash(materialized.skillSrcPath);
|
|
239
|
+
return {
|
|
240
|
+
sourceHash: Option.some(sourceHash),
|
|
241
|
+
treeIntegrity: Option.fromUndefinedOr(materialized.treeIntegrity),
|
|
242
|
+
observation: { agents: [], targets: [] },
|
|
243
|
+
};
|
|
244
|
+
}),
|
|
245
|
+
materializeRetained: ({ target }) => Effect.gen(function* () {
|
|
246
|
+
const canonical = yield* usableAcceptedCanonical({
|
|
247
|
+
workspace: ws,
|
|
248
|
+
type: "skill",
|
|
249
|
+
name: target.name,
|
|
250
|
+
});
|
|
251
|
+
if (Option.isNone(canonical) || canonical.value.ref.type !== "skill") {
|
|
252
|
+
return yield* new LifecyclePostconditionViolated({
|
|
253
|
+
postcondition: "materialize-observable",
|
|
254
|
+
targetType: "skill",
|
|
255
|
+
targetName: target.name,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return yield* materializeInstall({ ref: canonical.value.ref });
|
|
259
|
+
}),
|
|
219
260
|
prepareSourceTransition: ({ ref }) => prepareAcceptedCanonicalTransition({
|
|
220
261
|
workspace: ws,
|
|
221
262
|
type: "skill",
|
|
@@ -249,49 +290,7 @@ export const SkillManagerLive = Layer.effect(SkillManager, Effect.gen(function*
|
|
|
249
290
|
}),
|
|
250
291
|
materializeUninstall,
|
|
251
292
|
materializeDeactivate,
|
|
252
|
-
|
|
253
|
-
const workspaceRelativeLocalSourcePath = ref.refType === "local"
|
|
254
|
-
? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
|
|
255
|
-
: Option.none();
|
|
256
|
-
if (ref.refType === "local" && Option.isNone(workspaceRelativeLocalSourcePath)) {
|
|
257
|
-
return yield* new SkillDefinitionInvalid({
|
|
258
|
-
detail: `Local skill source path must stay within the workspace root: ${ref.source.path}`,
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
const sourceHash = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.sourceHash)));
|
|
262
|
-
const treeIntegrity = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.treeIntegrity)));
|
|
263
|
-
if (ref.refType === "workspace") {
|
|
264
|
-
return yield* ws.setSkillEntry(ref.skill.name, {
|
|
265
|
-
source: "workspace",
|
|
266
|
-
enabled: true,
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
if (sourceHash === undefined || treeIntegrity === undefined) {
|
|
270
|
-
return yield* new SkillInstallStateMissing({
|
|
271
|
-
name: ref.skill.name,
|
|
272
|
-
kind: "content-identity",
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
const lockEntry = buildSkillLockEntry(ref, workspaceRelativeLocalSourcePath, sourceHash, treeIntegrity);
|
|
276
|
-
if (lockEntry === undefined) {
|
|
277
|
-
return yield* new SkillInstallStateMissing({
|
|
278
|
-
name: ref.skill.name,
|
|
279
|
-
kind: "external-resolution",
|
|
280
|
-
});
|
|
281
|
-
}
|
|
282
|
-
if (lockEntry.type === "registry") {
|
|
283
|
-
yield* validateExactResolvedVersion(`skills.${ref.skill.name}.resolvedVersion`, lockEntry.resolvedVersion);
|
|
284
|
-
}
|
|
285
|
-
return yield* ws.setSkill({
|
|
286
|
-
name: ref.skill.name,
|
|
287
|
-
lockEntry,
|
|
288
|
-
versionRange,
|
|
289
|
-
});
|
|
290
|
-
}),
|
|
291
|
-
removeSettingsEntry: ({ target }) => ws
|
|
292
|
-
.removeSkillFromSettings(target.name)
|
|
293
|
-
.pipe(Effect.withSpan("SkillManager.removeSettingsEntry")),
|
|
294
|
-
upsertLockfileEntry: Effect.fn("SkillManager.upsertLockfileEntry")(function* ({ ref, materialization, }) {
|
|
293
|
+
acceptedResolution: Effect.fn("SkillManager.acceptedResolution")(function* ({ ref, materialization, }) {
|
|
295
294
|
const workspaceRelativeLocalSourcePath = ref.refType === "local"
|
|
296
295
|
? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
|
|
297
296
|
: Option.none();
|
|
@@ -301,8 +300,7 @@ export const SkillManagerLive = Layer.effect(SkillManager, Effect.gen(function*
|
|
|
301
300
|
});
|
|
302
301
|
}
|
|
303
302
|
if (ref.refType === "workspace") {
|
|
304
|
-
|
|
305
|
-
return;
|
|
303
|
+
return Option.none();
|
|
306
304
|
}
|
|
307
305
|
const sourceHash = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.sourceHash)));
|
|
308
306
|
const treeIntegrity = Option.getOrUndefined(materialization.pipe(Option.flatMap((facts) => facts.treeIntegrity)));
|
|
@@ -322,15 +320,9 @@ export const SkillManagerLive = Layer.effect(SkillManager, Effect.gen(function*
|
|
|
322
320
|
if (lockEntry.type === "registry") {
|
|
323
321
|
yield* validateExactResolvedVersion(`skills.${ref.skill.name}.resolvedVersion`, lockEntry.resolvedVersion);
|
|
324
322
|
}
|
|
325
|
-
return
|
|
326
|
-
name: ref.skill.name,
|
|
327
|
-
lockEntry,
|
|
328
|
-
versionRange: Option.none(),
|
|
329
|
-
});
|
|
323
|
+
return Option.some({ key: ref.skill.name, entry: lockEntry });
|
|
330
324
|
}),
|
|
331
|
-
|
|
332
|
-
.removeSkillLock(target.name)
|
|
333
|
-
.pipe(Effect.withSpan("SkillManager.removeLockfileEntry")),
|
|
325
|
+
withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
|
|
334
326
|
};
|
|
335
327
|
}));
|
|
336
328
|
// -----------------------------------------------------------------------------
|
|
@@ -35,10 +35,10 @@ export declare const materializeSkillCanonical: (args: {
|
|
|
35
35
|
}) => Effect.Effect<{
|
|
36
36
|
skillSrcPath: string & import("effect/Brand").Brand<"AbsolutePath">;
|
|
37
37
|
treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
38
|
-
}, import("../index.ts").CanonicalPackageProbeFailed | import("../index.ts").PackageCopyFailed | import("@agentxm/
|
|
38
|
+
}, import("../index.ts").CanonicalPackageProbeFailed | import("../index.ts").PackageCopyFailed | import("@agentxm/cli-maintenance/official-skill/application").AxmSkillCompatibilityUnavailable | import("@agentxm/cli-maintenance/official-skill/domain").AxmSkillIncompatible | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("../extensions/canonical-directory.js").CanonicalDirectoryReplacementError, FileSystem.FileSystem | Path.Path> | Effect.Effect<{
|
|
39
39
|
skillSrcPath: string & import("effect/Brand").Brand<"AbsolutePath">;
|
|
40
40
|
treeIntegrity: string & import("effect/Brand").Brand<"TreeIntegrity">;
|
|
41
|
-
}, import("../index.ts").CanonicalPackageProbeFailed | import("../index.ts").ArchiveIntegrityMismatch | import("@agentxm/
|
|
41
|
+
}, import("../index.ts").CanonicalPackageProbeFailed | import("../index.ts").ArchiveIntegrityMismatch | import("@agentxm/cli-maintenance/official-skill/application").AxmSkillCompatibilityUnavailable | import("@agentxm/cli-maintenance/official-skill/domain").AxmSkillIncompatible | import("@agentxm/workspace-state").MaterializedTreeInvalid | import("@agentxm/registry-client").RegistryClientFailure | import("../extensions/canonical-directory.js").CanonicalDirectoryReplacementError, FileSystem.FileSystem | Path.Path | import("effect/unstable/http/HttpClient").HttpClient> | Effect.Effect<MaterializedSkillCanonical, import("@agentxm/workspace-state").PathTraversalDetected | import("@agentxm/cli-maintenance/official-skill/application").AxmSkillCompatibilityUnavailable | import("@agentxm/cli-maintenance/official-skill/domain").AxmSkillIncompatible, FileSystem.FileSystem | Path.Path>;
|
|
42
42
|
export declare const ensureSkillAgentArtifact: (args: {
|
|
43
43
|
readonly canonicalSkillSrcPath: string;
|
|
44
44
|
readonly targetDir: string;
|
|
@@ -29,7 +29,11 @@ export declare const skillArtifactFromTargets: (args: {
|
|
|
29
29
|
readonly change: JobStepArtifact["change"];
|
|
30
30
|
readonly workspaceTargets?: ReadonlyArray<JobStepArtifactTarget>;
|
|
31
31
|
}) => Effect.Effect<{
|
|
32
|
-
targets?: JobStepArtifactTarget
|
|
32
|
+
targets?: (JobStepArtifactTarget | {
|
|
33
|
+
agentIds?: readonly string[];
|
|
34
|
+
path: string;
|
|
35
|
+
change: "created" | "updated" | "removed" | "unchanged";
|
|
36
|
+
})[];
|
|
33
37
|
path: string;
|
|
34
38
|
scope: "project" | "user";
|
|
35
39
|
agents: readonly string[];
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { usableAcceptedCanonical } from "@agentxm/workspace-state";
|
|
2
|
+
import { LifecyclePostconditionViolated } from "../extensions/errors.js";
|
|
1
3
|
/**
|
|
2
4
|
* Subagent extension manager service.
|
|
3
5
|
*
|
|
@@ -633,6 +635,31 @@ export const SubagentManagerLive = Layer.effect(SubagentManager, Effect.gen(func
|
|
|
633
635
|
return yield* isObservedInstalled(ws, "subagent", target.name);
|
|
634
636
|
}),
|
|
635
637
|
materializeInstall,
|
|
638
|
+
acquireCanonical: ({ ref, force }) => Effect.gen(function* () {
|
|
639
|
+
const { sanitized, paths: { canonicalPath, subagentSrcPath }, } = getCanonicalPaths(ref);
|
|
640
|
+
const treeIntegrity = yield* materializeCanonical(ref, sanitized, canonicalPath, subagentSrcPath, force === true);
|
|
641
|
+
yield* readSubagentContent(subagentSrcPath, ref.subagent.name);
|
|
642
|
+
return {
|
|
643
|
+
sourceHash: Option.some(yield* computePackageContentHash(canonicalPath)),
|
|
644
|
+
treeIntegrity: Option.fromUndefinedOr(treeIntegrity),
|
|
645
|
+
observation: { agents: [], targets: [] },
|
|
646
|
+
};
|
|
647
|
+
}),
|
|
648
|
+
materializeRetained: ({ target }) => Effect.gen(function* () {
|
|
649
|
+
const canonical = yield* usableAcceptedCanonical({
|
|
650
|
+
workspace: ws,
|
|
651
|
+
type: "subagent",
|
|
652
|
+
name: target.name,
|
|
653
|
+
});
|
|
654
|
+
if (Option.isNone(canonical) || canonical.value.ref.type !== "subagent") {
|
|
655
|
+
return yield* new LifecyclePostconditionViolated({
|
|
656
|
+
postcondition: "materialize-observable",
|
|
657
|
+
targetType: "subagent",
|
|
658
|
+
targetName: target.name,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
return yield* materializeInstall({ ref: canonical.value.ref });
|
|
662
|
+
}),
|
|
636
663
|
prepareSourceTransition: ({ ref }) => prepareAcceptedCanonicalTransition({
|
|
637
664
|
workspace: ws,
|
|
638
665
|
type: "subagent",
|
|
@@ -651,7 +678,7 @@ export const SubagentManagerLive = Layer.effect(SubagentManager, Effect.gen(func
|
|
|
651
678
|
}),
|
|
652
679
|
materializeUninstall,
|
|
653
680
|
materializeDeactivate,
|
|
654
|
-
|
|
681
|
+
acceptedResolution: Effect.fn("SubagentManager.acceptedResolution")(function* ({ ref, materialization, }) {
|
|
655
682
|
const workspaceRelativeLocalSourcePath = ref.refType === "local"
|
|
656
683
|
? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
|
|
657
684
|
: Option.none();
|
|
@@ -660,50 +687,8 @@ export const SubagentManagerLive = Layer.effect(SubagentManager, Effect.gen(func
|
|
|
660
687
|
detail: `Local subagent source path must stay within the workspace root: ${ref.source.path}`,
|
|
661
688
|
});
|
|
662
689
|
}
|
|
663
|
-
const state = acquiredState(materialization);
|
|
664
690
|
if (ref.refType === "workspace") {
|
|
665
|
-
return
|
|
666
|
-
source: "workspace",
|
|
667
|
-
enabled: true,
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
if (state === undefined) {
|
|
671
|
-
return yield* new SubagentInstallStateMissing({
|
|
672
|
-
name: ref.subagent.name,
|
|
673
|
-
kind: "content-identity",
|
|
674
|
-
});
|
|
675
|
-
}
|
|
676
|
-
const lockEntry = buildSubagentLockEntry(ref, state.sourceHash, state.treeIntegrity, workspaceRelativeLocalSourcePath);
|
|
677
|
-
if (lockEntry === undefined) {
|
|
678
|
-
return yield* new SubagentInstallStateMissing({
|
|
679
|
-
name: ref.subagent.name,
|
|
680
|
-
kind: "external-resolution",
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
if (lockEntry.type === "registry") {
|
|
684
|
-
yield* validateExactResolvedVersion(`subagents.${ref.subagent.name}.resolvedVersion`, lockEntry.resolvedVersion);
|
|
685
|
-
}
|
|
686
|
-
return yield* ws.setSubagent({
|
|
687
|
-
name: ref.subagent.name,
|
|
688
|
-
lockEntry,
|
|
689
|
-
versionRange,
|
|
690
|
-
});
|
|
691
|
-
}),
|
|
692
|
-
removeSettingsEntry: ({ target }) => ws
|
|
693
|
-
.removeSubagentSettings(target.name)
|
|
694
|
-
.pipe(Effect.withSpan("SubagentManager.removeSettingsEntry")),
|
|
695
|
-
upsertLockfileEntry: Effect.fn("SubagentManager.upsertLockfileEntry")(function* ({ ref, materialization, }) {
|
|
696
|
-
const workspaceRelativeLocalSourcePath = ref.refType === "local"
|
|
697
|
-
? makeWorkspaceRelativeSourcePath(path, baseDir, ref.sourcePath ?? stripFileProtocol(ref.location))
|
|
698
|
-
: Option.none();
|
|
699
|
-
if (ref.refType === "local" && Option.isNone(workspaceRelativeLocalSourcePath)) {
|
|
700
|
-
return yield* new SubagentDefinitionInvalid({
|
|
701
|
-
detail: `Local subagent source path must stay within the workspace root: ${ref.source.path}`,
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
if (ref.refType === "workspace") {
|
|
705
|
-
yield* ws.removeSubagentLock(ref.subagent.name);
|
|
706
|
-
return;
|
|
691
|
+
return Option.none();
|
|
707
692
|
}
|
|
708
693
|
const state = acquiredState(materialization);
|
|
709
694
|
if (state === undefined) {
|
|
@@ -722,15 +707,9 @@ export const SubagentManagerLive = Layer.effect(SubagentManager, Effect.gen(func
|
|
|
722
707
|
if (lockEntry.type === "registry") {
|
|
723
708
|
yield* validateExactResolvedVersion(`subagents.${ref.subagent.name}.resolvedVersion`, lockEntry.resolvedVersion);
|
|
724
709
|
}
|
|
725
|
-
return
|
|
726
|
-
name: ref.subagent.name,
|
|
727
|
-
lockEntry,
|
|
728
|
-
versionRange: Option.none(),
|
|
729
|
-
});
|
|
710
|
+
return Option.some({ key: ref.subagent.name, entry: lockEntry });
|
|
730
711
|
}),
|
|
731
|
-
|
|
732
|
-
.removeSubagentLock(target.name)
|
|
733
|
-
.pipe(Effect.withSpan("SubagentManager.removeLockfileEntry")),
|
|
712
|
+
withdrawnResolutionKeys: ({ target }) => Effect.succeed([target.name]),
|
|
734
713
|
};
|
|
735
714
|
}));
|
|
736
715
|
//# sourceMappingURL=manager.js.map
|
package/dist/src/testing.d.ts
CHANGED
|
@@ -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>;
|
package/dist/src/testing.js
CHANGED
|
@@ -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.
|
|
8
|
-
"@agentxm/
|
|
9
|
-
"@agentxm/extension-
|
|
10
|
-
"@agentxm/extension-
|
|
11
|
-
"@agentxm/extension-
|
|
12
|
-
"@agentxm/
|
|
13
|
-
"@agentxm/
|
|
14
|
-
"@agentxm/workspace-
|
|
15
|
-
"@agentxm/workspace-
|
|
16
|
-
"@agentxm/workspace-
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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
|
-
"
|
|
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.
|
|
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
|