@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.
Files changed (56) hide show
  1. package/dist/src/index.d.ts +0 -3
  2. package/dist/src/index.js +0 -2
  3. package/dist/src/install/install-extensions.d.ts +5 -5
  4. package/dist/src/install/install-extensions.js +2 -1
  5. package/dist/src/mcps/install/plan.d.ts +1 -1
  6. package/dist/src/mcps/install/plan.js +58 -1
  7. package/dist/src/packs/uninstall/plan.js +1 -1
  8. package/dist/src/skills/application/index.d.ts +42 -0
  9. package/dist/src/skills/application/index.js +37 -0
  10. package/dist/src/skills/domain/selection.d.ts +22 -0
  11. package/dist/src/skills/domain/selection.js +18 -0
  12. package/dist/src/skills/install/artifact.d.ts +4 -0
  13. package/dist/src/skills/install/artifact.js +29 -0
  14. package/dist/src/skills/install/plan.d.ts +15 -4
  15. package/dist/src/skills/install/plan.js +112 -96
  16. package/dist/src/skills/uninstall/plan.js +9 -2
  17. package/dist/src/step-failure.js +12 -0
  18. package/dist/src/subagents/application/index.d.ts +42 -0
  19. package/dist/src/subagents/application/index.js +37 -0
  20. package/dist/src/subagents/domain/selection.d.ts +22 -0
  21. package/dist/src/subagents/domain/selection.js +18 -0
  22. package/dist/src/subagents/install/plan.d.ts +2 -2
  23. package/dist/src/subagents/install/plan.js +1 -2
  24. package/dist/src/subagents/uninstall/plan.js +1 -1
  25. package/dist/src/testing.d.ts +3 -2
  26. package/dist/src/testing.js +7 -5
  27. package/dist/src/uninstall/root-intent.d.ts +1 -0
  28. package/dist/src/uninstall/root-intent.js +1 -0
  29. package/dist/src/uninstall/undesired-target.d.ts +26 -0
  30. package/dist/src/uninstall/undesired-target.js +56 -0
  31. package/dist/src/uninstall/uninstall-extensions.js +30 -6
  32. package/dist/src/update/selective/plan.d.ts +2 -3
  33. package/dist/src/update/selective/plan.js +2 -2
  34. package/dist/src/update/selective/requirements.d.ts +2 -6
  35. package/dist/src/update/selective/requirements.js +1 -3
  36. package/dist/src/update/selective/selection.js +1 -1
  37. package/dist/src/update/selective/skills.d.ts +2 -4
  38. package/dist/src/update/selective/skills.js +22 -26
  39. package/dist/src/update/selective/subagents.d.ts +1 -2
  40. package/dist/src/update/selective/subagents.js +9 -8
  41. package/dist/src/update/selective/use-case.d.ts +7 -7
  42. package/dist/src/update/selective/vocabulary.d.ts +1 -1
  43. package/dist/src/update/selector.js +1 -1
  44. package/package.json +24 -16
  45. package/dist/src/glob.d.ts +0 -35
  46. package/dist/src/glob.js +0 -75
  47. package/dist/src/install/selection-interaction.d.ts +0 -46
  48. package/dist/src/install/selection-interaction.js +0 -22
  49. package/dist/src/skills/install/selection.d.ts +0 -26
  50. package/dist/src/skills/install/selection.js +0 -41
  51. package/dist/src/skills/operations/install-result.d.ts +0 -33
  52. package/dist/src/skills/operations/install-result.js +0 -10
  53. package/dist/src/skills/operations/install.d.ts +0 -66
  54. package/dist/src/skills/operations/install.js +0 -582
  55. package/dist/src/subagents/install/selection.d.ts +0 -26
  56. package/dist/src/subagents/install/selection.js +0 -37
@@ -15,9 +15,10 @@ import * as Path from "effect/Path";
15
15
  import { SkillManager, skillArtifactFromTargets, } from "@agentxm/extension-materialization";
16
16
  import { buildUninstallOperation } from "@agentxm/workspace-reconciliation";
17
17
  import { resolveInstalledIdentifierNameOrInput } from "@agentxm/extension-sources";
18
+ import { parseExtensionFqnParts } from "@agentxm/extension-model/unstable/extensions";
18
19
  import { CodingAgentRepository } from "@agentxm/workspace-projection";
19
20
  import { WorkspaceMutations, acquiredExtensionDisplayPathFromLockEntry, installedRowsByName, sanitizeName, } from "@agentxm/workspace-state";
20
- import { expandGlob } from "../../glob.js";
21
+ import { expandGlob } from "@agentxm/extension-model/unstable/extensions/name-patterns";
21
22
  import { lifecycleStepFailure } from "../../step-failure.js";
22
23
  import { installRefused, } from "../../install/vocabulary.js";
23
24
  import { makeWorkspaceRetentionPolicy } from "@agentxm/workspace-reconciliation";
@@ -48,13 +49,19 @@ export const parseSkillUninstallRequest = Effect.fn("UninstallExtensions.parseSk
48
49
  if (selector.includes("*") && matched.length === 0) {
49
50
  return { targets: [] };
50
51
  }
52
+ // An identifier nothing installed answers to still names one skill: its
53
+ // extension name, never the fully qualified spelling as a name.
54
+ const unresolvedName = (input) => {
55
+ const fqn = parseExtensionFqnParts(input);
56
+ return fqn?.type === "skill" ? fqn.name : input;
57
+ };
51
58
  const names = matched.length > 0
52
59
  ? matched
53
60
  : [
54
61
  yield* resolveInstalledIdentifierNameOrInput({
55
62
  input: selector,
56
63
  resourceType: "skill",
57
- }).pipe(Effect.mapError((cause) => installRefused({
64
+ }).pipe(Effect.map((name) => (name === selector.trim() ? unresolvedName(name) : name)), Effect.mapError((cause) => installRefused({
58
65
  category: "not_found",
59
66
  detail: `Skill "${selector}" could not be resolved`,
60
67
  cause,
@@ -85,6 +85,18 @@ const carriedFailure = (failure) => {
85
85
  export const lifecycleStepFailure = (failure) => {
86
86
  if (failure instanceof StepFailure)
87
87
  return failure;
88
+ if (failure._tag === "McpSharedTargetConflict") {
89
+ return new StepFailure({
90
+ category: "conflict",
91
+ detail: failure.reason,
92
+ suggestions: [
93
+ {
94
+ description: "Use an MCP package whose transport and symbolic inputs are supported by every configured reader of the shared target.",
95
+ },
96
+ ],
97
+ cause: failure,
98
+ });
99
+ }
88
100
  if (failure instanceof ExtensionLifecycleFailed) {
89
101
  return new StepFailure({
90
102
  category: failure.category,
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Subagents 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 { SubagentExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/subagent";
9
+ import { type SubagentSelectionRequest } from "../domain/selection.js";
10
+ declare const SubagentSelectionNotFound_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: "SubagentSelectionNotFound";
12
+ } & Readonly<A>;
13
+ export declare class SubagentSelectionNotFound extends SubagentSelectionNotFound_base<{
14
+ readonly requested: ReadonlyArray<string>;
15
+ readonly available: ReadonlyArray<string>;
16
+ }> {
17
+ }
18
+ declare const SubagentSelectionCancelled_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: "SubagentSelectionCancelled";
20
+ } & Readonly<A>;
21
+ export declare class SubagentSelectionCancelled extends SubagentSelectionCancelled_base<{
22
+ readonly message: string;
23
+ }> {
24
+ }
25
+ declare const SubagentSelectionUnavailable_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: "SubagentSelectionUnavailable";
27
+ } & Readonly<A>;
28
+ /** The interface could not obtain a choice; this is distinct from cancellation. */
29
+ export declare class SubagentSelectionUnavailable extends SubagentSelectionUnavailable_base<{
30
+ readonly cause?: unknown;
31
+ }> {
32
+ }
33
+ declare const SubagentSelectionInteraction_base: Context.ServiceClass<SubagentSelectionInteraction, "@agentxm/extension-lifecycle/subagents/SubagentSelectionInteraction", {
34
+ readonly select: (candidates: Array.NonEmptyReadonlyArray<SubagentExtensionRef>) => Effect.Effect<ReadonlyArray<SubagentExtensionRef>, SubagentSelectionCancelled | SubagentSelectionUnavailable>;
35
+ }>;
36
+ export declare class SubagentSelectionInteraction extends SubagentSelectionInteraction_base {
37
+ }
38
+ export type SubagentSelectionFailure = SubagentSelectionNotFound | SubagentSelectionCancelled | SubagentSelectionUnavailable;
39
+ /** Resolve the subagent-owned decision through this capability's interaction port. */
40
+ export declare const determineSubagentsToInstall: (candidates: readonly [SubagentExtensionRef, ...SubagentExtensionRef[]], request: SubagentSelectionRequest) => Effect.Effect<readonly SubagentExtensionRef[], SubagentSelectionFailure, SubagentSelectionInteraction>;
41
+ export {};
42
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Subagents 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 { decideSubagentSelection } from "../domain/selection.js";
10
+ export class SubagentSelectionNotFound extends Data.TaggedError("SubagentSelectionNotFound") {
11
+ }
12
+ export class SubagentSelectionCancelled extends Data.TaggedError("SubagentSelectionCancelled") {
13
+ }
14
+ /** The interface could not obtain a choice; this is distinct from cancellation. */
15
+ export class SubagentSelectionUnavailable extends Data.TaggedError("SubagentSelectionUnavailable") {
16
+ }
17
+ export class SubagentSelectionInteraction extends Context.Service()("@agentxm/extension-lifecycle/subagents/SubagentSelectionInteraction") {
18
+ }
19
+ /** Resolve the subagent-owned decision through this capability's interaction port. */
20
+ export const determineSubagentsToInstall = Effect.fn("Subagents.determineSelection")(function* (candidates, request) {
21
+ const names = Array.map(candidates, (ref) => ref.subagent.name);
22
+ const decision = decideSubagentSelection(names, request);
23
+ switch (decision.kind) {
24
+ case "selected":
25
+ return candidates.filter((ref) => decision.names.includes(ref.subagent.name));
26
+ case "unmatched":
27
+ return yield* new SubagentSelectionNotFound({
28
+ requested: request.requestedSubagents,
29
+ available: names,
30
+ });
31
+ case "choice-required": {
32
+ const interaction = yield* SubagentSelectionInteraction;
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 SubagentSelectionRequest {
3
+ readonly requestedSubagents: ReadonlyArray<string>;
4
+ readonly all: boolean;
5
+ readonly nonInteractive: boolean;
6
+ }
7
+ export type SubagentSelectionDecision = {
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 subagents, 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 decideSubagentSelection: (names: Array.NonEmptyReadonlyArray<string>, request: SubagentSelectionRequest) => SubagentSelectionDecision;
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 subagents, 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 decideSubagentSelection = (names, request) => {
9
+ if (request.requestedSubagents.length > 0) {
10
+ const matched = expandGlobs(request.requestedSubagents, 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
@@ -18,8 +18,8 @@ import { type SourceResolutionFailure } from "@agentxm/extension-sources";
18
18
  import { type Plan } from "@agentxm/workspace-operations";
19
19
  import type { ExtensionLifecycleFailed } from "../../errors.js";
20
20
  import { type RegistryLookupProbe } from "../../install/registry-source-resolution.js";
21
- import { ExtensionSelectionInteraction, type ExtensionSelectionCancelled } from "../../install/selection-interaction.js";
22
21
  import { type InstallStepRequirements, type ResolveInstallRequirements, type SubagentInstallIntent } from "../../install/vocabulary.js";
22
+ import { SubagentSelectionInteraction, type SubagentSelectionFailure } from "../application/index.js";
23
23
  /** A subagent source after grammar parsing, before anything is discovered. */
24
24
  export interface ParsedSubagentInstallRequest {
25
25
  readonly source: Source;
@@ -43,7 +43,7 @@ export declare const parseSubagentInstallRequest: (args: SubagentInstallArgs) =>
43
43
  /** Discover the subagents the parsed source offers, or refuse when it has none. */
44
44
  export declare const discoverSubagentRefs: (request: ParsedSubagentInstallRequest) => Effect.Effect<ReadonlyArray<SubagentExtensionRef>, ExtensionLifecycleFailed, ResolveInstallRequirements>;
45
45
  /** Settle which of the discovered subagents this request installs. */
46
- export declare const finalizeSubagentInstallIntent: (request: ParsedSubagentInstallRequest, discovered: ReadonlyArray<SubagentExtensionRef>) => Effect.Effect<SubagentInstallIntent, ExtensionLifecycleFailed | ExtensionSelectionCancelled, ExtensionSelectionInteraction>;
46
+ export declare const finalizeSubagentInstallIntent: (request: ParsedSubagentInstallRequest, discovered: ReadonlyArray<SubagentExtensionRef>) => Effect.Effect<SubagentInstallIntent, ExtensionLifecycleFailed | SubagentSelectionFailure, SubagentSelectionInteraction>;
47
47
  /** The closures a settled subagent intent becomes. */
48
48
  export declare const planSubagentInstall: (intent: SubagentInstallIntent) => Effect.Effect<Plan<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SubagentManager>;
49
49
  //# sourceMappingURL=plan.d.ts.map
@@ -19,9 +19,8 @@ import { CodingAgentRepository } from "@agentxm/workspace-projection";
19
19
  import { WorkspaceMutations } from "@agentxm/workspace-state";
20
20
  import { lifecycleStepFailure } from "../../step-failure.js";
21
21
  import { sourceFailureDetail, } from "../../install/registry-source-resolution.js";
22
- import { ExtensionSelectionInteraction, } from "../../install/selection-interaction.js";
23
22
  import { installRefused, } from "../../install/vocabulary.js";
24
- import { determineSubagentsToInstall } from "./selection.js";
23
+ import { determineSubagentsToInstall, SubagentSelectionInteraction, } from "../application/index.js";
25
24
  import { resolveSubagentInstallSource } from "./source.js";
26
25
  const isRemoteReadNotImplemented = (error) => sourceFailureDetail(error).includes("not implemented");
27
26
  const discoverHowToFix = (source, error) => {
@@ -13,7 +13,7 @@ import { NO_MATERIALIZATION_OBSERVATION, SubagentManager, } from "@agentxm/exten
13
13
  import { buildUninstallOperation } from "@agentxm/workspace-reconciliation";
14
14
  import { parseExtensionFqnParts } from "@agentxm/extension-model/unstable/extensions";
15
15
  import { WorkspaceMutations, acquiredExtensionDisplayPathFromLockEntry, } from "@agentxm/workspace-state";
16
- import { expandGlob } from "../../glob.js";
16
+ import { expandGlob } from "@agentxm/extension-model/unstable/extensions/name-patterns";
17
17
  import { lifecycleStepFailure } from "../../step-failure.js";
18
18
  import { installRefused } from "../../install/vocabulary.js";
19
19
  import { makeWorkspaceRetentionPolicy } from "@agentxm/workspace-reconciliation";
@@ -18,7 +18,8 @@ import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace
18
18
  import { ReleaseAgePosture } from "@agentxm/extension-resolution";
19
19
  import { SourceHostProviders } from "@agentxm/extension-sources";
20
20
  import { type ResolvePlanInteractionTestState } from "@agentxm/workspace-operations/testing";
21
- import { ExtensionSelectionInteraction } from "./install/selection-interaction.js";
21
+ import { SkillSelectionInteraction } from "./skills/application/index.js";
22
+ import { SubagentSelectionInteraction } from "./subagents/application/index.js";
22
23
  import { BundledAxmSkillAsset } from "./skills/install/bundled.js";
23
24
  import { StepFailureConversion } from "./step-failure-conversion.js";
24
25
  /**
@@ -99,7 +100,7 @@ export declare const makeLifecycleFixture: (options?: LifecycleFixtureOptions) =
99
100
  readonly type: "skill" | "subagent";
100
101
  readonly offered: ReadonlyArray<string>;
101
102
  }>;
102
- provide: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | import("@agentxm/workspace-state").WorkspaceMutationsError, import("effect/FileSystem").FileSystem | import("effect/Path").Path | Exclude<R, StepFailureConversion | import("@agentxm/workspace-state").WorkspaceMutations | import("@agentxm/workspace-transactions").WorkspaceTransactionScope | import("@agentxm/workspace-projection").CodingAgentRepository | import("@agentxm/agent-integration").NativeWriteAuthority | HttpClient.HttpClient | import("@agentxm/workspace-state").ConfiguredAgentOutcomesProvider | import("@agentxm/extension-materialization").ExtensionManagers | import("@agentxm/extension-materialization").HookManager | import("@agentxm/extension-materialization").SkillManager | import("@agentxm/extension-materialization").RuleManager | import("@agentxm/extension-materialization").KnowledgeManager | import("@agentxm/extension-materialization").PackManager | import("@agentxm/extension-materialization").McpServerManager | import("@agentxm/workspace-transactions").FootprintRecorder | import("@agentxm/workspace-operations").OperationJournal | import("@agentxm/extension-materialization").McpSecretStore | ReleaseAgePosture | import("@agentxm/workspace-operations").ResolvePlanInteraction | SourceHostProviders | import("@agentxm/extension-materialization").SubagentManager | import("@agentxm/extension-sources").WorkspaceCatalog | import("@agentxm/workspace-projection").WorkspaceInvariantFacts | ExtensionSelectionInteraction | BundledAxmSkillAsset | AgentExecutableResolver | import("@agentxm/workspace-state/live").WorkspaceStateServices | import("@agentxm/workspace-projection").ProjectionParticipants>>;
103
+ provide: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E | import("@agentxm/workspace-state").WorkspaceMutationsError, import("effect/FileSystem").FileSystem | import("effect/Path").Path | Exclude<R, StepFailureConversion | import("@agentxm/workspace-state").WorkspaceMutations | import("@agentxm/workspace-transactions").WorkspaceTransactionScope | import("@agentxm/workspace-projection").CodingAgentRepository | import("@agentxm/agent-integration").NativeWriteAuthority | HttpClient.HttpClient | import("@agentxm/workspace-state").ConfiguredAgentOutcomesProvider | import("@agentxm/extension-materialization").ExtensionManagers | import("@agentxm/extension-materialization").HookManager | import("@agentxm/extension-materialization").SkillManager | import("@agentxm/extension-materialization").RuleManager | import("@agentxm/extension-materialization").KnowledgeManager | import("@agentxm/extension-materialization").PackManager | import("@agentxm/extension-materialization").McpServerManager | import("@agentxm/workspace-transactions").FootprintRecorder | import("@agentxm/workspace-operations").OperationJournal | import("@agentxm/extension-materialization").McpSecretStore | ReleaseAgePosture | import("@agentxm/workspace-operations").ResolvePlanInteraction | SourceHostProviders | import("@agentxm/extension-materialization").SubagentManager | import("@agentxm/extension-sources").WorkspaceCatalog | import("@agentxm/workspace-projection").WorkspaceInvariantFacts | SkillSelectionInteraction | SubagentSelectionInteraction | BundledAxmSkillAsset | AgentExecutableResolver | import("@agentxm/workspace-state/live").WorkspaceStateServices | import("@agentxm/workspace-projection").ProjectionParticipants>>;
103
104
  cleanup: () => void;
104
105
  };
105
106
  export type LifecycleFixture = ReturnType<typeof makeLifecycleFixture>;
@@ -31,7 +31,8 @@ import { PlanInvocationTest, ResolvePlanInteractionTest, } from "@agentxm/worksp
31
31
  import { CodingAgentRepositoryLive, NativeWriteAuthorityLive, WorkspaceCatalogLive, WorkspaceInvariantFactsLive, } from "@agentxm/workspace-projection/live";
32
32
  import { layer as WorkspaceLayerLive } from "@agentxm/workspace-state/live";
33
33
  import { ExtensionLifecycleFailed } from "./errors.js";
34
- import { ExtensionSelectionInteraction } from "./install/selection-interaction.js";
34
+ import { SkillSelectionInteraction } from "./skills/application/index.js";
35
+ import { SubagentSelectionInteraction } from "./subagents/application/index.js";
35
36
  import { BundledAxmSkillAsset } from "./skills/install/bundled.js";
36
37
  import { StepFailureConversion } from "./step-failure-conversion.js";
37
38
  /**
@@ -156,19 +157,20 @@ export const makeLifecycleFixture = (options = {}) => {
156
157
  }),
157
158
  });
158
159
  const selectionCalls = [];
159
- const selection = Layer.succeed(ExtensionSelectionInteraction, {
160
- selectSkills: (candidates) => {
160
+ const selection = Layer.mergeAll(Layer.succeed(SkillSelectionInteraction, {
161
+ select: (candidates) => {
161
162
  selectionCalls.push({ type: "skill", offered: candidates.map((ref) => ref.skill.name) });
162
163
  return Effect.succeed(options.select === "none" ? [] : candidates);
163
164
  },
164
- selectSubagents: (candidates) => {
165
+ }), Layer.succeed(SubagentSelectionInteraction, {
166
+ select: (candidates) => {
165
167
  selectionCalls.push({
166
168
  type: "subagent",
167
169
  offered: candidates.map((ref) => ref.subagent.name),
168
170
  });
169
171
  return Effect.succeed(options.select === "none" ? [] : candidates);
170
172
  },
171
- });
173
+ }));
172
174
  const environment = ConfigProvider.layer(ConfigProvider.fromEnv({ env: { AXM_USER_HOME: home } }));
173
175
  // Activation never fetches a source: it turns on what the workspace already
174
176
  // acquired. A fixture that could fetch would be describing installation.
@@ -5,6 +5,7 @@ export declare const rootUninstallableTypeSegments: readonly ("knowledge" | "rul
5
5
  export type RootUninstallableType = InstallableExtensionType;
6
6
  export interface RootUninstallIntent {
7
7
  readonly source: string;
8
+ readonly owner: string;
8
9
  readonly type: RootUninstallableType;
9
10
  readonly name: string;
10
11
  }
@@ -94,6 +94,7 @@ export const resolveRootUninstallIntent = Effect.fn("UninstallExtensions.resolve
94
94
  }
95
95
  return {
96
96
  source,
97
+ owner: parsed.owner,
97
98
  type: toInstallableExtensionType(parsed.type),
98
99
  name: parsed.name,
99
100
  };
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Refusing to uninstall an installed package the workspace does not configure.
3
+ *
4
+ * Uninstall withdraws desired intent. An installed package that no desired
5
+ * route reaches is not intent to withdraw; sync reconciles it, so uninstall
6
+ * refuses before planning rather than reporting a removal it cannot own.
7
+ */
8
+ import * as Effect from "effect/Effect";
9
+ import type { InstallableExtensionType } from "@agentxm/extension-model/unstable/extensions/installable-types";
10
+ import { LockfileReader, WorkspaceMutations } from "@agentxm/workspace-state";
11
+ import { ExtensionLifecycleFailed } from "../errors.js";
12
+ /** The single extension a literal uninstall selector names. */
13
+ export interface UninstallSubject {
14
+ readonly type: InstallableExtensionType;
15
+ readonly name: string;
16
+ /** The owner handle, when the selector spelled a fully qualified name. */
17
+ readonly owner: string | undefined;
18
+ }
19
+ /** The subject a typed selector names, or none for a glob. */
20
+ export declare const typedUninstallSubject: (type: InstallableExtensionType, selector: string) => UninstallSubject | undefined;
21
+ /**
22
+ * Fail when no desired route reaches the subject while an installed package
23
+ * for it sits in the install root.
24
+ */
25
+ export declare const refuseUndesiredInstalledTarget: (subject: UninstallSubject) => Effect.Effect<undefined, ExtensionLifecycleFailed, import("effect/FileSystem").FileSystem | import("effect/Path").Path | WorkspaceMutations | LockfileReader>;
26
+ //# sourceMappingURL=undesired-target.d.ts.map
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Refusing to uninstall an installed package the workspace does not configure.
3
+ *
4
+ * Uninstall withdraws desired intent. An installed package that no desired
5
+ * route reaches is not intent to withdraw; sync reconciles it, so uninstall
6
+ * refuses before planning rather than reporting a removal it cannot own.
7
+ */
8
+ import * as Effect from "effect/Effect";
9
+ import { parseExtensionFqnParts, toExtensionTypePlural, } from "@agentxm/extension-model/unstable/extensions";
10
+ import { LockfileReader, WorkspaceMutations, observeInstallRoot } from "@agentxm/workspace-state";
11
+ import { ExtensionLifecycleFailed } from "../errors.js";
12
+ /** The subject a typed selector names, or none for a glob. */
13
+ export const typedUninstallSubject = (type, selector) => {
14
+ const trimmed = selector.trim();
15
+ if (trimmed.includes("*"))
16
+ return undefined;
17
+ const fqn = parseExtensionFqnParts(trimmed);
18
+ return fqn !== undefined && fqn.type === type
19
+ ? { type, name: fqn.name, owner: fqn.owner }
20
+ : { type, name: trimmed, owner: undefined };
21
+ };
22
+ const readFailed = (cause) => new ExtensionLifecycleFailed({
23
+ category: "internal",
24
+ detail: "Installed packages could not be observed",
25
+ cause,
26
+ });
27
+ /**
28
+ * Fail when no desired route reaches the subject while an installed package
29
+ * for it sits in the install root.
30
+ */
31
+ export const refuseUndesiredInstalledTarget = Effect.fn("UninstallExtensions.refuseUndesiredInstalledTarget")(function* (subject) {
32
+ const ws = yield* WorkspaceMutations;
33
+ const graph = yield* ws.getDesiredStateGraph().pipe(Effect.mapError(readFailed));
34
+ if (graph.nodes.some((node) => node.type === subject.type && node.name === subject.name))
35
+ return;
36
+ const inventory = yield* observeInstallRoot({
37
+ layout: ws.layout,
38
+ graph,
39
+ locks: yield* LockfileReader,
40
+ }).pipe(Effect.mapError(readFailed));
41
+ const installed = inventory.leftovers.some((entry) => entry.type === subject.type &&
42
+ entry.name === subject.name &&
43
+ (subject.owner === undefined || entry.owner === subject.owner));
44
+ if (!installed)
45
+ return;
46
+ const identity = subject.owner === undefined
47
+ ? subject.name
48
+ : `${subject.owner}/${toExtensionTypePlural(subject.type)}/${subject.name}`;
49
+ return yield* new ExtensionLifecycleFailed({
50
+ category: "conflict",
51
+ title: "Extension is not configured",
52
+ detail: `${subject.type} ${identity} is installed but not configured; axm sync removes installed packages that are not configured.`,
53
+ cmd: "axm sync",
54
+ });
55
+ });
56
+ //# sourceMappingURL=undesired-target.js.map
@@ -23,6 +23,7 @@ import { parseRuleUninstallRequest, planRuleUninstall } from "../rules/uninstall
23
23
  import { parseSkillUninstallRequest, planSkillUninstall } from "../skills/uninstall/plan.js";
24
24
  import { parseSubagentUninstallRequest, planSubagentUninstall, } from "../subagents/uninstall/plan.js";
25
25
  import { resolveRootUninstallIntent } from "./root-intent.js";
26
+ import { refuseUndesiredInstalledTarget, typedUninstallSubject } from "./undesired-target.js";
26
27
  const planForType = (type, selector) => {
27
28
  switch (type) {
28
29
  case "skill":
@@ -90,9 +91,21 @@ const planForType = (type, selector) => {
90
91
  /** Settle a removal without writing anything. */
91
92
  export const prepareUninstallExtensions = Effect.fn("UninstallExtensions.prepare")(function* (request) {
92
93
  const resolved = yield* Option.match(request.type, {
93
- onSome: (type) => Effect.succeed({ type, selector: request.selector }),
94
- onNone: () => resolveRootUninstallIntent(request.selector).pipe(Effect.map((intent) => ({ type: intent.type, selector: intent.name }))),
94
+ onSome: (type) => Effect.succeed({
95
+ type,
96
+ selector: request.selector,
97
+ subject: typedUninstallSubject(type, request.selector),
98
+ }),
99
+ onNone: () => resolveRootUninstallIntent(request.selector).pipe(Effect.map((intent) => ({
100
+ type: intent.type,
101
+ selector: intent.name,
102
+ subject: { type: intent.type, name: intent.name, owner: intent.owner },
103
+ }))),
95
104
  });
105
+ // An installed package no desired route reaches is sync's to reconcile, not
106
+ // intent this removal can withdraw.
107
+ if (resolved.type !== "pack" && resolved.subject !== undefined)
108
+ yield* refuseUndesiredInstalledTarget(resolved.subject);
96
109
  const planned = yield* planForType(resolved.type, resolved.selector);
97
110
  const proposal = yield* proposeDesiredState(planned.names.map((name) => ({ kind: "remove", type: resolved.type, name }))).pipe(Effect.mapError((cause) => new ExtensionLifecycleFailed({
98
111
  category: "conflict",
@@ -126,11 +139,22 @@ export const prepareUninstallExtensions = Effect.fn("UninstallExtensions.prepare
126
139
  const jobs = yield* Effect.forEach(planned.plan.jobs, (job) => Effect.gen(function* () {
127
140
  const steps = yield* Effect.forEach(job.steps, (step) => Effect.gen(function* () {
128
141
  const artifact = artifactByName.get(step.label ?? "");
129
- if (artifact === undefined ||
130
- step.readiness === "error" ||
131
- leafType === undefined ||
132
- ((artifact.targets?.length ?? 0) === 0 && (artifact.references?.length ?? 0) === 0))
142
+ if (artifact === undefined || step.readiness === "error" || leafType === undefined)
133
143
  return step;
144
+ if ((artifact.targets?.length ?? 0) === 0 && (artifact.references?.length ?? 0) === 0) {
145
+ // Nothing exists to withdraw: the planner's descriptive artifact
146
+ // would name paths that are neither present nor changed, so the
147
+ // step declares none, and a result that changed nothing reports
148
+ // the empty evidence instead.
149
+ const { artifact: _descriptive, ...withoutArtifact } = step;
150
+ return {
151
+ ...withoutArtifact,
152
+ run: step.run.pipe(Effect.map((result) => result.result === "success" &&
153
+ (result.disposition === "unchanged" || result.artifact?.change === "unchanged")
154
+ ? { ...result, artifact }
155
+ : result)),
156
+ };
157
+ }
134
158
  const cleanup = yield* collectCleanupStep({
135
159
  expectedSkillNames: activeNames("skill"),
136
160
  expectedSubagentNames: activeNames("subagent"),
@@ -10,9 +10,8 @@
10
10
  *
11
11
  * @experimental This API is unstable and may change without notice.
12
12
  */
13
- import * as Effect from "effect/Effect";
14
13
  import type * as Option from "effect/Option";
15
- import type { JobStepResult, Plan, StepFailure } from "@agentxm/workspace-operations";
14
+ import type { Plan, PlannedJobStep } from "@agentxm/workspace-operations";
16
15
  import type { SkillExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/skill";
17
16
  import type { SubagentExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/subagent";
18
17
  import type { SkillLockEntry, SubagentLockEntry } from "@agentxm/workspace-state";
@@ -40,6 +39,6 @@ export interface SelectiveUpdateUnit<TOperation> {
40
39
  * steps are the selected entries, each either unchanged or dispatched to the
41
40
  * install operation that advances it.
42
41
  */
43
- export declare const buildSelectiveUpdatePlan: <TOperation, R>(units: ReadonlyArray<SelectiveUpdateUnit<TOperation>>, acceptedResolutions: Readonly<Record<string, AcceptedEntry | undefined>>, name: string, description: Option.Option<string>, makeRunClosure: (operation: TOperation) => Effect.Effect<JobStepResult, StepFailure, R>) => Plan<R>;
42
+ export declare const buildSelectiveUpdatePlan: <TOperation, R>(units: ReadonlyArray<SelectiveUpdateUnit<TOperation>>, acceptedResolutions: Readonly<Record<string, AcceptedEntry | undefined>>, name: string, description: Option.Option<string>, makeStep: (operation: TOperation) => PlannedJobStep<R>) => Plan<R>;
44
43
  export {};
45
44
  //# sourceMappingURL=plan.d.ts.map
@@ -41,7 +41,7 @@ const unchangedStep = (label) => ({
41
41
  * steps are the selected entries, each either unchanged or dispatched to the
42
42
  * install operation that advances it.
43
43
  */
44
- export const buildSelectiveUpdatePlan = (units, acceptedResolutions, name, description, makeRunClosure) => ({
44
+ export const buildSelectiveUpdatePlan = (units, acceptedResolutions, name, description, makeStep) => ({
45
45
  _tag: "Plan",
46
46
  name,
47
47
  description,
@@ -52,7 +52,7 @@ export const buildSelectiveUpdatePlan = (units, acceptedResolutions, name, descr
52
52
  const accepted = acceptedResolutions[unit.name];
53
53
  const needsUpdate = accepted === undefined || unit.force || hasChanged(unit.ref, accepted);
54
54
  return needsUpdate
55
- ? { readiness: "ready", label: unit.name, run: makeRunClosure(unit.operation) }
55
+ ? { ...makeStep(unit.operation), label: unit.name }
56
56
  : unchangedStep(unit.name);
57
57
  }),
58
58
  },
@@ -2,16 +2,12 @@
2
2
  * What a selective update's plan steps declare at execution time.
3
3
  *
4
4
  * A selective advance runs the same install operation the install routes run,
5
- * so its steps carry the install step requirements plus the two services that
6
- * operation reaches for directly: the source providers it re-fetches through,
7
- * and the boundary's failure conversion. Those requirements travel with the
5
+ * so its steps carry the install step requirements. Those requirements travel with the
8
6
  * step and are composed once at the application's runtime boundary — nothing
9
7
  * is captured into a step's closure on the way.
10
8
  *
11
9
  * @experimental This API is unstable and may change without notice.
12
10
  */
13
- import type { SourceHostProviders } from "@agentxm/extension-sources";
14
11
  import type { InstallStepRequirements } from "../../install/vocabulary.js";
15
- import type { StepFailureConversion } from "../../step-failure-conversion.js";
16
- export type SelectiveUpdateStepRequirements = InstallStepRequirements | SourceHostProviders | StepFailureConversion;
12
+ export type SelectiveUpdateStepRequirements = InstallStepRequirements;
17
13
  //# sourceMappingURL=requirements.d.ts.map
@@ -2,9 +2,7 @@
2
2
  * What a selective update's plan steps declare at execution time.
3
3
  *
4
4
  * A selective advance runs the same install operation the install routes run,
5
- * so its steps carry the install step requirements plus the two services that
6
- * operation reaches for directly: the source providers it re-fetches through,
7
- * and the boundary's failure conversion. Those requirements travel with the
5
+ * so its steps carry the install step requirements. Those requirements travel with the
8
6
  * step and are composed once at the application's runtime boundary — nothing
9
7
  * is captured into a step's closure on the way.
10
8
  *
@@ -14,7 +14,7 @@ import * as Effect from "effect/Effect";
14
14
  import * as Option from "effect/Option";
15
15
  import { parseSourceQualifiedRegistrySourcePatternParts } from "@agentxm/extension-model/unstable/extensions";
16
16
  import { resolveInstalledIdentifierNameOrInput, resolveSource, SourceHostProviders, } from "@agentxm/extension-sources";
17
- import { expandGlobs } from "../../glob.js";
17
+ import { expandGlobs } from "@agentxm/extension-model/unstable/extensions/name-patterns";
18
18
  /**
19
19
  * Whether a positional selector names the same origin an entry declares.
20
20
  * Two Registry patterns compare by their parsed parts; anything else
@@ -16,9 +16,7 @@
16
16
  */
17
17
  import * as Effect from "effect/Effect";
18
18
  import { SourceHostProviders } from "@agentxm/extension-sources";
19
- import { WorkspaceMutations } from "@agentxm/workspace-state";
20
19
  import { ExtensionLifecycleFailed } from "../../errors.js";
21
- import type { SelectiveUpdateStepRequirements } from "./requirements.js";
22
20
  import { type SelectiveUpdateSelectors } from "./selection.js";
23
21
  /** Everything `skills update` needs beyond the workspace it runs in. */
24
22
  export interface SelectiveSkillUpdateRequest extends SelectiveUpdateSelectors {
@@ -47,9 +45,9 @@ export declare const prepareSelectiveSkillUpdate: (request: SelectiveSkillUpdate
47
45
  outcome: "planned";
48
46
  subjectType: "skill";
49
47
  planName: string;
50
- execution: import("@agentxm/workspace-operations").ExecutionCandidate<SelectiveUpdateStepRequirements, never>;
48
+ execution: import("@agentxm/workspace-operations").ExecutionCandidate<import("../../index.ts").InstallStepRequirements, never>;
51
49
  reason?: never;
52
50
  message?: never;
53
51
  planDescription?: never;
54
- }, ExtensionLifecycleFailed | import("@agentxm/extension-sources").SourceSyntaxInvalid | import("@agentxm/extension-sources").SourceHostNotConfigured | import("@agentxm/extension-sources").SourceNotResolvable | import("@agentxm/extension-sources").SourceNetworkFailure | import("@agentxm/extension-sources").GitOperationFailed | import("@agentxm/extension-sources").WorkspaceCatalogUnavailable | import("@agentxm/extension-sources").AxmSkillGateUnavailable | import("@agentxm/registry-client").RegistryProblem | import("@agentxm/registry-client").RegistryRequestFailed | import("@agentxm/registry-client").RegistryOperationFailed | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").LockfileIoError | import("@agentxm/workspace-state").LockfileParseError | import("@agentxm/workspace-state").LockfileDecodeError | import("@agentxm/workspace-state").LockfileVersionUnsupported | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").LockfileValidationError | import("@agentxm/extension-resolution").ExtensionResolutionFailed | import("@agentxm/workspace-operations").CandidateFingerprintFailed, import("effect/FileSystem").FileSystem | import("effect/Path").Path | WorkspaceMutations | import("effect/unstable/http/HttpClient").HttpClient | import("@agentxm/workspace-state").ConfiguredAgentOutcomesProvider | import("@agentxm/extension-resolution").ReleaseAgePosture | import("effect/Scope").Scope | SourceHostProviders | import("@agentxm/extension-sources").WorkspaceCatalog>;
52
+ }, ExtensionLifecycleFailed | import("@agentxm/extension-sources").SourceSyntaxInvalid | import("@agentxm/extension-sources").SourceHostNotConfigured | import("@agentxm/extension-sources").SourceNotResolvable | import("@agentxm/extension-sources").SourceNetworkFailure | import("@agentxm/extension-sources").GitOperationFailed | import("@agentxm/extension-sources").WorkspaceCatalogUnavailable | import("@agentxm/extension-sources").AxmSkillGateUnavailable | import("@agentxm/registry-client").RegistryProblem | import("@agentxm/registry-client").RegistryRequestFailed | import("@agentxm/registry-client").RegistryOperationFailed | import("@agentxm/workspace-state").SettingsIoError | import("@agentxm/workspace-state").SettingsParseError | import("@agentxm/workspace-state").SettingsDecodeError | import("@agentxm/workspace-state").LockfileIoError | import("@agentxm/workspace-state").LockfileParseError | import("@agentxm/workspace-state").LockfileDecodeError | import("@agentxm/workspace-state").LockfileVersionUnsupported | import("@agentxm/workspace-state").WorkspaceRootEscape | import("@agentxm/workspace-state").LockfileValidationError | import("@agentxm/extension-resolution").ExtensionResolutionFailed | import("@agentxm/workspace-operations").CandidateFingerprintFailed, import("@agentxm/workspace-state").ConfiguredAgentOutcomesProvider | import("@agentxm/extension-materialization").SkillManager | import("@agentxm/extension-resolution").ReleaseAgePosture | import("effect/Scope").Scope | SourceHostProviders | import("@agentxm/extension-sources").WorkspaceCatalog | import("../../index.ts").InstallStepRequirements>;
55
53
  //# sourceMappingURL=skills.d.ts.map