@agentxm/extension-lifecycle 0.30.1 → 0.30.2
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/index.d.ts +0 -3
- package/dist/src/index.js +0 -2
- package/dist/src/install/install-extensions.d.ts +5 -5
- package/dist/src/install/install-extensions.js +2 -1
- package/dist/src/mcps/install/plan.d.ts +1 -1
- package/dist/src/mcps/install/plan.js +58 -1
- package/dist/src/packs/uninstall/plan.js +1 -1
- package/dist/src/skills/application/index.d.ts +42 -0
- package/dist/src/skills/application/index.js +37 -0
- package/dist/src/skills/domain/selection.d.ts +22 -0
- package/dist/src/skills/domain/selection.js +18 -0
- package/dist/src/skills/install/artifact.d.ts +4 -0
- package/dist/src/skills/install/artifact.js +29 -0
- package/dist/src/skills/install/plan.d.ts +15 -4
- package/dist/src/skills/install/plan.js +112 -96
- package/dist/src/skills/uninstall/plan.js +9 -2
- package/dist/src/step-failure.js +12 -0
- package/dist/src/subagents/application/index.d.ts +42 -0
- package/dist/src/subagents/application/index.js +37 -0
- package/dist/src/subagents/domain/selection.d.ts +22 -0
- package/dist/src/subagents/domain/selection.js +18 -0
- package/dist/src/subagents/install/plan.d.ts +2 -2
- package/dist/src/subagents/install/plan.js +1 -2
- package/dist/src/subagents/uninstall/plan.js +1 -1
- package/dist/src/testing.d.ts +3 -2
- package/dist/src/testing.js +7 -5
- package/dist/src/uninstall/root-intent.d.ts +1 -0
- package/dist/src/uninstall/root-intent.js +1 -0
- package/dist/src/uninstall/undesired-target.d.ts +26 -0
- package/dist/src/uninstall/undesired-target.js +56 -0
- package/dist/src/uninstall/uninstall-extensions.js +30 -6
- package/dist/src/update/selective/plan.d.ts +2 -3
- package/dist/src/update/selective/plan.js +2 -2
- package/dist/src/update/selective/requirements.d.ts +2 -6
- package/dist/src/update/selective/requirements.js +1 -3
- package/dist/src/update/selective/selection.js +1 -1
- package/dist/src/update/selective/skills.d.ts +2 -4
- package/dist/src/update/selective/skills.js +22 -26
- package/dist/src/update/selective/subagents.d.ts +1 -2
- package/dist/src/update/selective/subagents.js +9 -8
- package/dist/src/update/selective/use-case.d.ts +7 -7
- package/dist/src/update/selective/vocabulary.d.ts +1 -1
- package/dist/src/update/selector.js +1 -1
- package/package.json +24 -16
- package/dist/src/glob.d.ts +0 -35
- package/dist/src/glob.js +0 -75
- package/dist/src/install/selection-interaction.d.ts +0 -46
- package/dist/src/install/selection-interaction.js +0 -22
- package/dist/src/skills/install/selection.d.ts +0 -26
- package/dist/src/skills/install/selection.js +0 -41
- package/dist/src/skills/operations/install-result.d.ts +0 -33
- package/dist/src/skills/operations/install-result.js +0 -10
- package/dist/src/skills/operations/install.d.ts +0 -66
- package/dist/src/skills/operations/install.js +0 -582
- package/dist/src/subagents/install/selection.d.ts +0 -26
- package/dist/src/subagents/install/selection.js +0 -37
package/dist/src/index.d.ts
CHANGED
|
@@ -15,7 +15,6 @@ export type { SetActivationExecutionFailure } from "./activation/errors.js";
|
|
|
15
15
|
export { PUBLISHER_CHANGE_CONDITION_ID, publisherChangeRiskCondition, withPublisherTrust, withPublisherTrustConditions, } from "./publisher-binding.js";
|
|
16
16
|
export { InstallExtensions, prepareInstallExtensions, previewOrApplyInstallExtensions, type InstallDiagnostics, type InstallExtensionsCandidate, type InstallExtensionsFailure, type InstallExtensionsRequest, type InstallSubject, } from "./install/install-extensions.js";
|
|
17
17
|
export { installRefused, type HookInstallIntent, type InstallStepRequirements, type KnowledgeInstallIntent, type McpServerInstallIntent, type PackInstallIntent, type PackRecoveryDependencyResolver, type PrepareInstallRequirements, type ResolveInstallRequirements, type RuleInstallIntent, type SkillInstallIntent, type SubagentInstallIntent, } from "./install/vocabulary.js";
|
|
18
|
-
export { ExtensionSelectionCancelled, ExtensionSelectionInteraction, type ExtensionSelectionFailure, type ExtensionSelectionInteractionService, } from "./install/selection-interaction.js";
|
|
19
18
|
export { resolveRootInstallIntent, rootInstallableTypeSegments, RootInstallableTypeSegmentSchema, type RootInstallIntent, type RootInstallableType, type RootInstallableTypeSegment, } from "./install/root-intent.js";
|
|
20
19
|
export { installCommandFor, perTypeInstallPluralSegments } from "./install/per-type-install.js";
|
|
21
20
|
export { buildConfiguredInstallPlan, buildConfiguredPackInstallPlan, type ConfiguredInstallPlanResult, type ConfiguredInstallRequirements, type ConfiguredInstallableType, } from "./install/configured.js";
|
|
@@ -38,8 +37,6 @@ export { resolveRootUninstallIntent, rootUninstallableTypeSegments, type RootUni
|
|
|
38
37
|
export { PACK_UNINSTALL_GRAPH_BLOCKER_ID, packUninstallRecoveryIdentifiers, planPackUninstallGraphReadiness, type PackRetirement, type PackUninstallGraphReadiness, } from "./packs/uninstall/readiness.js";
|
|
39
38
|
export { type PackUninstallIntent, type PackUninstallRequirements, type ResolvedPackUninstallTarget, } from "./packs/uninstall/plan.js";
|
|
40
39
|
export { getSkillDisplayName } from "./skills/utils.js";
|
|
41
|
-
export { gitHostedSkillArtifactSource, installSkill, type InstallSkillOperation, type InstallSkillOperationArgs, } from "./skills/operations/install.js";
|
|
42
|
-
export type { InstallResult } from "./skills/operations/install-result.js";
|
|
43
40
|
export { enableSkill, type EnableSkillOperation } from "./skills/operations/enable.js";
|
|
44
41
|
export { disableSkill, type DisableSkillOperation } from "./skills/operations/disable.js";
|
|
45
42
|
export { uninstallMcpServer, type UninstallMcpServerOperation, type UninstallMcpServerOperationArgs, } from "./mcps/operations/uninstall.js";
|
package/dist/src/index.js
CHANGED
|
@@ -17,7 +17,6 @@ export { PUBLISHER_CHANGE_CONDITION_ID, publisherChangeRiskCondition, withPublis
|
|
|
17
17
|
// already declares, as one use case behind every command spelling.
|
|
18
18
|
export { InstallExtensions, prepareInstallExtensions, previewOrApplyInstallExtensions, } from "./install/install-extensions.js";
|
|
19
19
|
export { installRefused, } from "./install/vocabulary.js";
|
|
20
|
-
export { ExtensionSelectionCancelled, ExtensionSelectionInteraction, } from "./install/selection-interaction.js";
|
|
21
20
|
export { resolveRootInstallIntent, rootInstallableTypeSegments, RootInstallableTypeSegmentSchema, } from "./install/root-intent.js";
|
|
22
21
|
export { installCommandFor, perTypeInstallPluralSegments } from "./install/per-type-install.js";
|
|
23
22
|
export { buildConfiguredInstallPlan, buildConfiguredPackInstallPlan, } from "./install/configured.js";
|
|
@@ -45,7 +44,6 @@ export { PACK_UNINSTALL_GRAPH_BLOCKER_ID, packUninstallRecoveryIdentifiers, plan
|
|
|
45
44
|
export {} from "./packs/uninstall/plan.js";
|
|
46
45
|
// Skill lifecycle operations
|
|
47
46
|
export { getSkillDisplayName } from "./skills/utils.js";
|
|
48
|
-
export { gitHostedSkillArtifactSource, installSkill, } from "./skills/operations/install.js";
|
|
49
47
|
export { enableSkill } from "./skills/operations/enable.js";
|
|
50
48
|
export { disableSkill } from "./skills/operations/disable.js";
|
|
51
49
|
// MCP server lifecycle operations. Installation itself is a materialization
|
|
@@ -23,8 +23,8 @@ import type { ExtensionResolutionFailed } from "@agentxm/extension-resolution";
|
|
|
23
23
|
import type { ExtensionLifecycleFailed } from "../errors.js";
|
|
24
24
|
import { BundledAxmSkillAsset } from "../skills/install/bundled.js";
|
|
25
25
|
import { type ConfiguredInstallRequirements } from "./configured.js";
|
|
26
|
-
import type
|
|
27
|
-
import {
|
|
26
|
+
import { SkillSelectionInteraction, type SkillSelectionFailure } from "../skills/application/index.js";
|
|
27
|
+
import { SubagentSelectionInteraction, type SubagentSelectionFailure } from "../subagents/application/index.js";
|
|
28
28
|
import { type InstallExecutionFailure, type InstallStepRequirements, type PrepareInstallRequirements } from "./vocabulary.js";
|
|
29
29
|
/**
|
|
30
30
|
* What the request installs: the extensions the workspace already declares,
|
|
@@ -91,9 +91,9 @@ export interface InstallExtensionsCandidate {
|
|
|
91
91
|
* A configured entry's resolution refusal travels with its own category and
|
|
92
92
|
* sentence rather than as this feature's generic refusal.
|
|
93
93
|
*/
|
|
94
|
-
export type InstallExtensionsFailure = ExtensionLifecycleFailed | ExtensionResolutionFailed |
|
|
94
|
+
export type InstallExtensionsFailure = ExtensionLifecycleFailed | ExtensionResolutionFailed | SkillSelectionFailure | SubagentSelectionFailure | InstallExecutionFailure;
|
|
95
95
|
/** Settle an install without writing anything. */
|
|
96
|
-
export declare const prepareInstallExtensions: (request: InstallExtensionsRequest) => Effect.Effect<InstallExtensionsCandidate, InstallExtensionsFailure, PrepareInstallRequirements | ConfiguredInstallRequirements |
|
|
96
|
+
export declare const prepareInstallExtensions: (request: InstallExtensionsRequest) => Effect.Effect<InstallExtensionsCandidate, InstallExtensionsFailure, PrepareInstallRequirements | ConfiguredInstallRequirements | SkillSelectionInteraction | SubagentSelectionInteraction | BundledAxmSkillAsset>;
|
|
97
97
|
/**
|
|
98
98
|
* Everything resolving a settled install writes through: the candidate's own
|
|
99
99
|
* step requirements plus the operation boundary that presents, journals, and
|
|
@@ -104,7 +104,7 @@ export type ResolveInstallExtensionsRequirements = PrepareInstallRequirements |
|
|
|
104
104
|
export declare const previewOrApplyInstallExtensions: (candidate: InstallExtensionsCandidate, execution: PlanExecution) => Effect.Effect<OperationResolution, InstallExtensionsFailure, ResolveInstallExtensionsRequirements>;
|
|
105
105
|
/** The install use case: settle a request, then preview or apply it. */
|
|
106
106
|
export declare const InstallExtensions: {
|
|
107
|
-
readonly prepare: (request: InstallExtensionsRequest) => Effect.Effect<InstallExtensionsCandidate, InstallExtensionsFailure, PrepareInstallRequirements | ConfiguredInstallRequirements |
|
|
107
|
+
readonly prepare: (request: InstallExtensionsRequest) => Effect.Effect<InstallExtensionsCandidate, InstallExtensionsFailure, PrepareInstallRequirements | ConfiguredInstallRequirements | SkillSelectionInteraction | SubagentSelectionInteraction | BundledAxmSkillAsset>;
|
|
108
108
|
readonly previewOrApply: (candidate: InstallExtensionsCandidate, execution: PlanExecution) => Effect.Effect<OperationResolution, InstallExtensionsFailure, ResolveInstallExtensionsRequirements>;
|
|
109
109
|
};
|
|
110
110
|
//# sourceMappingURL=install-extensions.d.ts.map
|
|
@@ -31,7 +31,8 @@ import { BundledAxmSkillAsset, planBundledAxmSkillInstall } from "../skills/inst
|
|
|
31
31
|
import { buildConfiguredInstallPlan } from "./configured.js";
|
|
32
32
|
import { formatRegistryProbe } from "./registry-source-resolution.js";
|
|
33
33
|
import { resolveRootInstallIntent } from "./root-intent.js";
|
|
34
|
-
import {
|
|
34
|
+
import { SkillSelectionInteraction, } from "../skills/application/index.js";
|
|
35
|
+
import { SubagentSelectionInteraction, } from "../subagents/application/index.js";
|
|
35
36
|
import { installRefused, } from "./vocabulary.js";
|
|
36
37
|
const EMPTY_DIAGNOSTICS = { resolutionLines: [], companionPackages: [] };
|
|
37
38
|
const emptyPlan = (request) => ({
|
|
@@ -14,7 +14,7 @@ import { type ExtensionName, type Handle } from "@agentxm/extension-model/unstab
|
|
|
14
14
|
import type { McpServerExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/mcp-server";
|
|
15
15
|
import type { RegistrySource } from "@agentxm/extension-model/unstable/sources/types";
|
|
16
16
|
import { type Plan } from "@agentxm/workspace-operations";
|
|
17
|
-
import
|
|
17
|
+
import { ExtensionLifecycleFailed } from "../../errors.js";
|
|
18
18
|
import { type InstallStepRequirements, type McpServerInstallIntent, type ResolveInstallRequirements } from "../../install/vocabulary.js";
|
|
19
19
|
/** An MCP install request after grammar parsing, before anything is discovered. */
|
|
20
20
|
export interface ParsedMcpServerInstallRequest {
|
|
@@ -9,16 +9,21 @@
|
|
|
9
9
|
* @experimental This API is unstable and may change without notice.
|
|
10
10
|
*/
|
|
11
11
|
import * as Effect from "effect/Effect";
|
|
12
|
+
import * as FileSystem from "effect/FileSystem";
|
|
13
|
+
import * as Path from "effect/Path";
|
|
12
14
|
import * as Option from "effect/Option";
|
|
13
15
|
import * as Result from "effect/Result";
|
|
14
16
|
import * as Schema from "effect/Schema";
|
|
15
|
-
import { installMcpServer } from "@agentxm/workspace-reconciliation";
|
|
17
|
+
import { installMcpServer, readMcpServerManifest } from "@agentxm/workspace-reconciliation";
|
|
18
|
+
import { materializeRegistryPackage } from "@agentxm/extension-materialization";
|
|
19
|
+
import { validateManifestMcpServerTargets } from "@agentxm/agent-integration";
|
|
16
20
|
import { CONFIGURABLE_AGENTS_BY_ID, } from "@agentxm/extension-model/unstable/agent-capabilities";
|
|
17
21
|
import { ExtensionNameSchema, } from "@agentxm/extension-model/unstable/extensions";
|
|
18
22
|
import { intersectVersionConstraints } from "@agentxm/extension-model/unstable/version-constraints";
|
|
19
23
|
import { SourceHostProviders, resolveSource } from "@agentxm/extension-sources";
|
|
20
24
|
import { operationPresentation } from "@agentxm/workspace-operations";
|
|
21
25
|
import { WorkspaceMutations, mcpRegistryResolutionKey } from "@agentxm/workspace-state";
|
|
26
|
+
import { ExtensionLifecycleFailed } from "../../errors.js";
|
|
22
27
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
23
28
|
import { registryLoginSuggestions } from "../../install/registry-login-suggestion.js";
|
|
24
29
|
import { parseRegistryInstallTarget } from "../../install/registry-install-target.js";
|
|
@@ -267,6 +272,58 @@ export const planMcpServerInstall = Effect.fn("InstallExtensions.planMcpServer")
|
|
|
267
272
|
});
|
|
268
273
|
}
|
|
269
274
|
}
|
|
275
|
+
yield* Effect.scoped(Effect.gen(function* () {
|
|
276
|
+
const fs = yield* FileSystem.FileSystem;
|
|
277
|
+
const path = yield* Path.Path;
|
|
278
|
+
const ref = intent.ref;
|
|
279
|
+
// Inspect verified package bytes outside the workspace. A fresh install
|
|
280
|
+
// has no canonical manifest for the usual projection inspection to read.
|
|
281
|
+
const manifestPath = ref.refType === "registry"
|
|
282
|
+
? yield* Effect.gen(function* () {
|
|
283
|
+
const scratch = yield* fs.makeTempDirectoryScoped({ prefix: "axm-mcp-preflight-" });
|
|
284
|
+
return yield* materializeRegistryPackage({
|
|
285
|
+
baseDir: scratch,
|
|
286
|
+
destinationPath: path.join(scratch, "package"),
|
|
287
|
+
sourceLocation: ref.source.location,
|
|
288
|
+
owner: ref.owner,
|
|
289
|
+
type: "mcp-server",
|
|
290
|
+
name: ref.name,
|
|
291
|
+
version: ref.version,
|
|
292
|
+
integrity: ref.integrity,
|
|
293
|
+
messages: {
|
|
294
|
+
integrityMismatchDetail: `Integrity mismatch for ${ref.name}@${ref.version}`,
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
})
|
|
298
|
+
: ref.location;
|
|
299
|
+
const manifest = yield* readMcpServerManifest(manifestPath);
|
|
300
|
+
if (Option.isNone(manifest)) {
|
|
301
|
+
return yield* installRefused({
|
|
302
|
+
category: "validation",
|
|
303
|
+
detail: `Cannot read MCP manifest for ${intent.localName}`,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
const entries = yield* ws.getConfiguredMcpServerEntries();
|
|
307
|
+
yield* validateManifestMcpServerTargets({
|
|
308
|
+
manifest: manifest.value,
|
|
309
|
+
agentIds: yield* ws.getConfiguredAgents(),
|
|
310
|
+
scope: ws.scope,
|
|
311
|
+
serverName: intent.localName,
|
|
312
|
+
values: { ...entries[intent.localName]?.env, ...intent.env },
|
|
313
|
+
enabled: entries[intent.localName]?.enabled ?? true,
|
|
314
|
+
}).pipe(Effect.mapError((cause) => installRefused({
|
|
315
|
+
category: "conflict",
|
|
316
|
+
detail: cause.reason,
|
|
317
|
+
recover: "Use an MCP package whose transport and symbolic inputs are supported by every configured reader of the shared target.",
|
|
318
|
+
cause,
|
|
319
|
+
})));
|
|
320
|
+
})).pipe(Effect.mapError((cause) => cause instanceof ExtensionLifecycleFailed
|
|
321
|
+
? cause
|
|
322
|
+
: installRefused({
|
|
323
|
+
category: "validation",
|
|
324
|
+
detail: `Cannot inspect MCP package ${intent.localName}`,
|
|
325
|
+
cause,
|
|
326
|
+
})));
|
|
270
327
|
return {
|
|
271
328
|
_tag: "Plan",
|
|
272
329
|
name: "Install MCP server",
|
|
@@ -19,7 +19,7 @@ import { buildUninstallOperation, prepareUninstallArtifact, collectCleanupStep,
|
|
|
19
19
|
import { parseExtensionFqnParts, } from "@agentxm/extension-model/unstable/extensions";
|
|
20
20
|
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
21
21
|
import { WorkspaceMutations, decodeDesiredExtensionIdentity, } from "@agentxm/workspace-state";
|
|
22
|
-
import { expandGlob } from "
|
|
22
|
+
import { expandGlob } from "@agentxm/extension-model/unstable/extensions/name-patterns";
|
|
23
23
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
24
24
|
import { buildAggregateProjectionStep } from "../../install/aggregate-projection-step.js";
|
|
25
25
|
import { installRefused } from "../../install/vocabulary.js";
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills selection application contract and workflow.
|
|
3
|
+
* @experimental This API is unstable and may change without notice.
|
|
4
|
+
*/
|
|
5
|
+
import * as Array from "effect/Array";
|
|
6
|
+
import * as Context from "effect/Context";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import type { SkillExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/skill";
|
|
9
|
+
import { type SkillSelectionRequest } from "../domain/selection.js";
|
|
10
|
+
declare const SkillSelectionNotFound_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 & {
|
|
11
|
+
readonly _tag: "SkillSelectionNotFound";
|
|
12
|
+
} & Readonly<A>;
|
|
13
|
+
export declare class SkillSelectionNotFound extends SkillSelectionNotFound_base<{
|
|
14
|
+
readonly requested: ReadonlyArray<string>;
|
|
15
|
+
readonly available: ReadonlyArray<string>;
|
|
16
|
+
}> {
|
|
17
|
+
}
|
|
18
|
+
declare const SkillSelectionCancelled_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 & {
|
|
19
|
+
readonly _tag: "SkillSelectionCancelled";
|
|
20
|
+
} & Readonly<A>;
|
|
21
|
+
export declare class SkillSelectionCancelled extends SkillSelectionCancelled_base<{
|
|
22
|
+
readonly message: string;
|
|
23
|
+
}> {
|
|
24
|
+
}
|
|
25
|
+
declare const SkillSelectionUnavailable_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 & {
|
|
26
|
+
readonly _tag: "SkillSelectionUnavailable";
|
|
27
|
+
} & Readonly<A>;
|
|
28
|
+
/** The interface could not obtain a choice; this is distinct from cancellation. */
|
|
29
|
+
export declare class SkillSelectionUnavailable extends SkillSelectionUnavailable_base<{
|
|
30
|
+
readonly cause?: unknown;
|
|
31
|
+
}> {
|
|
32
|
+
}
|
|
33
|
+
declare const SkillSelectionInteraction_base: Context.ServiceClass<SkillSelectionInteraction, "@agentxm/extension-lifecycle/skills/SkillSelectionInteraction", {
|
|
34
|
+
readonly select: (candidates: Array.NonEmptyReadonlyArray<SkillExtensionRef>) => Effect.Effect<ReadonlyArray<SkillExtensionRef>, SkillSelectionCancelled | SkillSelectionUnavailable>;
|
|
35
|
+
}>;
|
|
36
|
+
export declare class SkillSelectionInteraction extends SkillSelectionInteraction_base {
|
|
37
|
+
}
|
|
38
|
+
export type SkillSelectionFailure = SkillSelectionNotFound | SkillSelectionCancelled | SkillSelectionUnavailable;
|
|
39
|
+
/** Resolve the skill-owned decision through this capability's interaction port. */
|
|
40
|
+
export declare const determineSkillsToInstall: (candidates: readonly [SkillExtensionRef, ...SkillExtensionRef[]], request: SkillSelectionRequest) => Effect.Effect<readonly SkillExtensionRef[], SkillSelectionFailure, SkillSelectionInteraction>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills selection application contract and workflow.
|
|
3
|
+
* @experimental This API is unstable and may change without notice.
|
|
4
|
+
*/
|
|
5
|
+
import * as Array from "effect/Array";
|
|
6
|
+
import * as Context from "effect/Context";
|
|
7
|
+
import * as Data from "effect/Data";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import { decideSkillSelection } from "../domain/selection.js";
|
|
10
|
+
export class SkillSelectionNotFound extends Data.TaggedError("SkillSelectionNotFound") {
|
|
11
|
+
}
|
|
12
|
+
export class SkillSelectionCancelled extends Data.TaggedError("SkillSelectionCancelled") {
|
|
13
|
+
}
|
|
14
|
+
/** The interface could not obtain a choice; this is distinct from cancellation. */
|
|
15
|
+
export class SkillSelectionUnavailable extends Data.TaggedError("SkillSelectionUnavailable") {
|
|
16
|
+
}
|
|
17
|
+
export class SkillSelectionInteraction extends Context.Service()("@agentxm/extension-lifecycle/skills/SkillSelectionInteraction") {
|
|
18
|
+
}
|
|
19
|
+
/** Resolve the skill-owned decision through this capability's interaction port. */
|
|
20
|
+
export const determineSkillsToInstall = Effect.fn("Skills.determineSelection")(function* (candidates, request) {
|
|
21
|
+
const names = Array.map(candidates, (ref) => ref.skill.name);
|
|
22
|
+
const decision = decideSkillSelection(names, request);
|
|
23
|
+
switch (decision.kind) {
|
|
24
|
+
case "selected":
|
|
25
|
+
return candidates.filter((ref) => decision.names.includes(ref.skill.name));
|
|
26
|
+
case "unmatched":
|
|
27
|
+
return yield* new SkillSelectionNotFound({
|
|
28
|
+
requested: request.requestedSkills,
|
|
29
|
+
available: names,
|
|
30
|
+
});
|
|
31
|
+
case "choice-required": {
|
|
32
|
+
const interaction = yield* SkillSelectionInteraction;
|
|
33
|
+
return yield* interaction.select(candidates);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as Array from "effect/Array";
|
|
2
|
+
export interface SkillSelectionRequest {
|
|
3
|
+
readonly requestedSkills: ReadonlyArray<string>;
|
|
4
|
+
readonly all: boolean;
|
|
5
|
+
readonly nonInteractive: boolean;
|
|
6
|
+
}
|
|
7
|
+
export type SkillSelectionDecision = {
|
|
8
|
+
readonly kind: "selected";
|
|
9
|
+
readonly names: ReadonlyArray<string>;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "choice-required";
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "unmatched";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Select this request's skills, preserving source order.
|
|
17
|
+
* Named patterns take precedence over all/unattended selection. The existing
|
|
18
|
+
* union-of-matches behavior is retained; unresolved selector requirements remain
|
|
19
|
+
* explicit in the installation specification.
|
|
20
|
+
*/
|
|
21
|
+
export declare const decideSkillSelection: (names: Array.NonEmptyReadonlyArray<string>, request: SkillSelectionRequest) => SkillSelectionDecision;
|
|
22
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expandGlobs } from "@agentxm/extension-model/unstable/extensions/name-patterns";
|
|
2
|
+
/**
|
|
3
|
+
* Select this request's skills, preserving source order.
|
|
4
|
+
* Named patterns take precedence over all/unattended selection. The existing
|
|
5
|
+
* union-of-matches behavior is retained; unresolved selector requirements remain
|
|
6
|
+
* explicit in the installation specification.
|
|
7
|
+
*/
|
|
8
|
+
export const decideSkillSelection = (names, request) => {
|
|
9
|
+
if (request.requestedSkills.length > 0) {
|
|
10
|
+
const matched = expandGlobs(request.requestedSkills, names);
|
|
11
|
+
return matched.length === 0 ? { kind: "unmatched" } : { kind: "selected", names: matched };
|
|
12
|
+
}
|
|
13
|
+
if (request.all || request.nonInteractive || names.length === 1) {
|
|
14
|
+
return { kind: "selected", names };
|
|
15
|
+
}
|
|
16
|
+
return { kind: "choice-required" };
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SkillExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/skill";
|
|
2
|
+
import type { JobStepArtifactSource } from "@agentxm/workspace-operations";
|
|
3
|
+
export declare const gitHostedSkillArtifactSource: (ref: SkillExtensionRef) => JobStepArtifactSource | undefined;
|
|
4
|
+
//# sourceMappingURL=artifact.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
const gitHostedSourceOrigin = (ref) => {
|
|
3
|
+
const source = ref.source;
|
|
4
|
+
switch (source.type) {
|
|
5
|
+
case "github":
|
|
6
|
+
case "gitlab":
|
|
7
|
+
case "bitbucket":
|
|
8
|
+
return `${source.url.origin}/${source.owner}/${source.repo}`;
|
|
9
|
+
case "azurerepos":
|
|
10
|
+
return `${source.url.origin}/${source.organization}/${source.project}/_git/${source.repo}`;
|
|
11
|
+
case "git":
|
|
12
|
+
return source.url.href;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const gitHostedSkillArtifactSource = (ref) => {
|
|
16
|
+
if (ref.refType !== "git-hosted")
|
|
17
|
+
return undefined;
|
|
18
|
+
const gitTreeHash = ref.gitTreeSha;
|
|
19
|
+
const gitRef = Option.getOrUndefined(ref.source.ref);
|
|
20
|
+
const directory = ref.sourcePath === undefined || ref.sourcePath.length === 0 ? "." : ref.sourcePath;
|
|
21
|
+
return {
|
|
22
|
+
type: ref.source.type,
|
|
23
|
+
origin: gitHostedSourceOrigin(ref),
|
|
24
|
+
...(gitRef !== undefined ? { ref: gitRef } : {}),
|
|
25
|
+
directory,
|
|
26
|
+
gitTreeHash,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=artifact.js.map
|
|
@@ -21,12 +21,11 @@ import { type PackageUrlParts } from "@agentxm/extension-model/unstable/packagin
|
|
|
21
21
|
import type { Source } from "@agentxm/extension-model/unstable/sources/types";
|
|
22
22
|
import type { VersionRange } from "@agentxm/extension-model/unstable/version-constraints";
|
|
23
23
|
import { type SourceResolutionFailure } from "@agentxm/extension-sources";
|
|
24
|
-
import { type Plan } from "@agentxm/workspace-operations";
|
|
24
|
+
import { type Plan, type PlannedJobStep } from "@agentxm/workspace-operations";
|
|
25
25
|
import type { ExtensionLifecycleFailed } from "../../errors.js";
|
|
26
26
|
import type { RegistryLookupProbe } from "../../install/registry-source-resolution.js";
|
|
27
|
-
import type { ExtensionSelectionCancelled } from "../../install/selection-interaction.js";
|
|
28
|
-
import { ExtensionSelectionInteraction } from "../../install/selection-interaction.js";
|
|
29
27
|
import { type InstallStepRequirements, type ResolveInstallRequirements, type SkillInstallIntent } from "../../install/vocabulary.js";
|
|
28
|
+
import { SkillSelectionInteraction, type SkillSelectionFailure } from "../application/index.js";
|
|
30
29
|
/** A skill source after grammar parsing, before anything is discovered. */
|
|
31
30
|
export interface ParsedSkillInstallRequest {
|
|
32
31
|
readonly source: Source;
|
|
@@ -66,7 +65,19 @@ export declare const parseSkillInstallRequest: (args: SkillInstallArgs) => Effec
|
|
|
66
65
|
/** Discover the skills the parsed source offers, or refuse when it has none. */
|
|
67
66
|
export declare const discoverSkillRefs: (request: ParsedSkillInstallRequest) => Effect.Effect<ReadonlyArray<SkillExtensionRef>, ExtensionLifecycleFailed, ResolveInstallRequirements>;
|
|
68
67
|
/** Settle which of the discovered skills this request installs. */
|
|
69
|
-
export declare const finalizeSkillInstallIntent: (request: ParsedSkillInstallRequest, discovered: ReadonlyArray<SkillExtensionRef>) => Effect.Effect<SkillInstallIntent, ExtensionLifecycleFailed |
|
|
68
|
+
export declare const finalizeSkillInstallIntent: (request: ParsedSkillInstallRequest, discovered: ReadonlyArray<SkillExtensionRef>) => Effect.Effect<SkillInstallIntent, ExtensionLifecycleFailed | SkillSelectionFailure, SkillSelectionInteraction>;
|
|
69
|
+
/** Installing declares intent; an update advances the resolution of existing intent. */
|
|
70
|
+
export type SkillInstallationStepInput = {
|
|
71
|
+
readonly ref: SkillExtensionRef;
|
|
72
|
+
readonly force: boolean;
|
|
73
|
+
} & ({
|
|
74
|
+
readonly operation: "install";
|
|
75
|
+
readonly versionRange: Option.Option<VersionRange>;
|
|
76
|
+
} | {
|
|
77
|
+
readonly operation: "update";
|
|
78
|
+
});
|
|
79
|
+
/** One skill-owned application step, shared by install and selective update. */
|
|
80
|
+
export declare const planSkillInstallationStep: (input: SkillInstallationStepInput) => Effect.Effect<PlannedJobStep<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SkillManager | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient>;
|
|
70
81
|
/** The closures a settled skill intent becomes. */
|
|
71
82
|
export declare const planSkillInstall: (intent: SkillInstallIntent) => Effect.Effect<Plan<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SkillManager | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient>;
|
|
72
83
|
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -28,11 +28,10 @@ import { createRegistryClient } from "@agentxm/registry-client";
|
|
|
28
28
|
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
29
29
|
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
30
30
|
import { WorkspaceMutations, sanitizeName } from "@agentxm/workspace-state";
|
|
31
|
-
import { gitHostedSkillArtifactSource } from "
|
|
31
|
+
import { gitHostedSkillArtifactSource } from "./artifact.js";
|
|
32
32
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
33
|
-
import { ExtensionSelectionInteraction } from "../../install/selection-interaction.js";
|
|
34
33
|
import { installRefused, } from "../../install/vocabulary.js";
|
|
35
|
-
import { determineSkillsToInstall } from "
|
|
34
|
+
import { determineSkillsToInstall, SkillSelectionInteraction, } from "../application/index.js";
|
|
36
35
|
import { resolveSkillInstallSource } from "./source.js";
|
|
37
36
|
const noSkillsFoundHowToFix = (source) => {
|
|
38
37
|
if (source.type === "registry") {
|
|
@@ -317,104 +316,121 @@ const targetChangeBeforeInstall = (args) => Effect.gen(function* () {
|
|
|
317
316
|
const exists = yield* fs.exists(args.linkPath).pipe(Effect.catch(() => Effect.succeed(false)));
|
|
318
317
|
return exists ? "updated" : "created";
|
|
319
318
|
});
|
|
320
|
-
/**
|
|
321
|
-
export const
|
|
319
|
+
/** One skill-owned application step, shared by install and selective update. */
|
|
320
|
+
export const planSkillInstallationStep = (input) => Effect.gen(function* () {
|
|
322
321
|
const ws = yield* WorkspaceMutations;
|
|
323
322
|
const skillManager = yield* SkillManager;
|
|
324
323
|
const agentRepo = yield* CodingAgentRepository;
|
|
325
324
|
const fs = yield* FileSystem.FileSystem;
|
|
326
325
|
const path = yield* Path.Path;
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
326
|
+
const ref = input.ref;
|
|
327
|
+
const previousLockEntry = yield* ws
|
|
328
|
+
.getLockedSkill(ref.skill.name)
|
|
329
|
+
.pipe(Effect.catch(() => Effect.succeed(Option.none())));
|
|
330
|
+
const previousVersion = Option.match(previousLockEntry, {
|
|
331
|
+
onNone: () => undefined,
|
|
332
|
+
onSome: previousResolvedVersion,
|
|
333
|
+
});
|
|
334
|
+
const { skillSrcPath } = yield* ws.getSkillDir(ref.skill.name, skillPathSourceFor(ref));
|
|
335
|
+
const sourceHashBeforeInstall = Option.match(previousLockEntry, {
|
|
336
|
+
onNone: () => undefined,
|
|
337
|
+
onSome: previousSourceHash,
|
|
338
|
+
}) ??
|
|
339
|
+
(yield* Effect.gen(function* () {
|
|
340
|
+
const exists = yield* fs
|
|
341
|
+
.exists(skillSrcPath)
|
|
342
|
+
.pipe(Effect.catch(() => Effect.succeed(false)));
|
|
343
|
+
if (!exists)
|
|
344
|
+
return undefined;
|
|
345
|
+
return yield* computeSkillSourceHash(skillSrcPath);
|
|
346
|
+
}));
|
|
347
|
+
const configuredAgents = yield* agentRepo.getMaterializationAgents();
|
|
348
|
+
const resolvedAgents = yield* Effect.forEach(configuredAgents, (agent) => agent
|
|
349
|
+
.resolveEffectiveSkillsDir({ workspaceRoot: ws.baseDir })
|
|
350
|
+
.pipe(Effect.map((outcome) => ({ agentId: agent.id, outcome }))), { concurrency: "unbounded" });
|
|
351
|
+
const unknownAgents = yield* agentRepo.getUnknownConfiguredAgentIds();
|
|
352
|
+
const skippedAgents = resolvedAgents.flatMap(({ agentId, outcome }) => outcome._tag === "unsupported" || outcome._tag === "disabled"
|
|
353
|
+
? [`${agentId}: ${outcome.reason}`]
|
|
354
|
+
: []);
|
|
355
|
+
const warnings = [
|
|
356
|
+
...(unknownAgents.length === 0
|
|
357
|
+
? []
|
|
358
|
+
: [`Skipping unknown configured agents: ${unknownAgents.join(", ")}`]),
|
|
359
|
+
...(skippedAgents.length === 0
|
|
360
|
+
? []
|
|
361
|
+
: [`Skipping non-installable configured agents: ${skippedAgents.join(", ")}`]),
|
|
362
|
+
];
|
|
363
|
+
const sanitizedName = sanitizeName(ref.skill.name);
|
|
364
|
+
const installableTargets = resolvedAgents.flatMap(({ agentId, outcome }) => outcome._tag === "supported" ? [{ agentId, targetDir: path.normalize(outcome.dir) }] : []);
|
|
365
|
+
const targetLocations = yield* groupInstallTargetsByDirectory(installableTargets, ws.baseDir);
|
|
366
|
+
const artifactAgents = artifactAgentIdsFromTargets(installableTargets);
|
|
367
|
+
const targets = yield* Effect.forEach(targetLocations, (location) => {
|
|
368
|
+
const linkPath = path.join(location.targetDir, sanitizedName);
|
|
369
|
+
return targetChangeBeforeInstall({
|
|
370
|
+
linkPath,
|
|
371
|
+
canonicalSkillSrcPath: skillSrcPath,
|
|
372
|
+
}).pipe(Effect.map((change) => {
|
|
373
|
+
const agentIds = artifactTargetAgentIds(location.agentIds);
|
|
374
|
+
return {
|
|
375
|
+
path: path.relative(ws.baseDir, linkPath),
|
|
376
|
+
change,
|
|
377
|
+
...(agentIds.length > 0 ? { agentIds } : {}),
|
|
378
|
+
};
|
|
379
|
+
}));
|
|
380
|
+
}, { concurrency: "unbounded" });
|
|
381
|
+
const firstTarget = targets[0];
|
|
382
|
+
const rawDisplayPath = firstTarget === undefined ? path.relative(ws.baseDir, skillSrcPath) : firstTarget.path;
|
|
383
|
+
const version = ref.refType === "registry" ? ref.version : undefined;
|
|
384
|
+
const installedBefore = yield* skillManager
|
|
385
|
+
.isInstalled({ target: { type: "skill", name: ref.skill.name } })
|
|
386
|
+
.pipe(Effect.catch(() => Effect.succeed(false)));
|
|
387
|
+
const releaseAgeWarning = input.operation === "install"
|
|
388
|
+
? yield* brandNewReleaseAgeWarning(ref, installedBefore)
|
|
389
|
+
: Option.none();
|
|
390
|
+
const step = withPlanWarning(buildInstallOperation(skillManager, {
|
|
391
|
+
toStepFailure: lifecycleStepFailure,
|
|
392
|
+
ref,
|
|
393
|
+
...(input.operation === "install"
|
|
394
|
+
? { declaration: { name: ref.skill.name, versionRange: input.versionRange } }
|
|
395
|
+
: {}),
|
|
396
|
+
force: input.force,
|
|
397
|
+
installedBefore: Effect.succeed(installedBefore),
|
|
398
|
+
buildArtifact: ({ installedBefore }) => Effect.gen(function* () {
|
|
399
|
+
const fileCount = yield* countFiles(fs, path, skillSrcPath);
|
|
400
|
+
const currentSourceHash = yield* computeSkillSourceHash(skillSrcPath);
|
|
401
|
+
const sameVersion = previousVersion === version;
|
|
402
|
+
const sameSource = sourceHashBeforeInstall === currentSourceHash;
|
|
403
|
+
const fallbackChange = !installedBefore
|
|
404
|
+
? "created"
|
|
405
|
+
: sameVersion && sameSource
|
|
406
|
+
? "unchanged"
|
|
407
|
+
: "updated";
|
|
408
|
+
const artifactChange = artifactChangeFromTargets(fallbackChange, targets);
|
|
409
|
+
const sourceDetails = gitHostedSkillArtifactSource(ref);
|
|
410
|
+
return {
|
|
411
|
+
path: rawDisplayPath.length === 0 ? "." : rawDisplayPath,
|
|
412
|
+
scope: ws.scope,
|
|
413
|
+
agents: artifactAgents,
|
|
414
|
+
...(version !== undefined ? { version } : {}),
|
|
415
|
+
change: artifactChange,
|
|
416
|
+
...(previousVersion !== undefined && previousVersion !== version
|
|
417
|
+
? { previousVersion }
|
|
418
|
+
: {}),
|
|
419
|
+
fileCount,
|
|
420
|
+
...(targets.length > 0 ? { targets } : {}),
|
|
421
|
+
...(sourceDetails !== undefined ? { source: sourceDetails } : {}),
|
|
422
|
+
};
|
|
423
|
+
}),
|
|
424
|
+
}), releaseAgeWarning);
|
|
425
|
+
return warnings.length === 0 ? step : withPlanWarning(step, Option.some(warnings.join("; ")));
|
|
426
|
+
}).pipe(Effect.mapError((cause) => installRefused({
|
|
427
|
+
category: "internal",
|
|
428
|
+
detail: `Skill install planning failed for ${input.ref.skill.name}`,
|
|
429
|
+
cause,
|
|
430
|
+
})), Effect.withSpan("SkillInstallation.planStep"));
|
|
431
|
+
/** The closures a settled skill intent becomes. */
|
|
432
|
+
export const planSkillInstall = Effect.fn("InstallExtensions.planSkills")(function* (intent) {
|
|
433
|
+
const steps = yield* Effect.forEach(intent.skillsToInstall, (entry) => planSkillInstallationStep({ ...entry, operation: "install", force: intent.force === true }), { concurrency: 1 });
|
|
418
434
|
return {
|
|
419
435
|
_tag: "Plan",
|
|
420
436
|
name: intent.skillsToInstall.length === 0
|