@agentxm/extension-lifecycle 0.28.12 → 0.28.13

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 (207) hide show
  1. package/dist/src/activation/display-paths.d.ts +25 -0
  2. package/dist/src/activation/display-paths.js +25 -0
  3. package/dist/src/activation/errors.d.ts +16 -0
  4. package/dist/src/activation/errors.js +7 -0
  5. package/dist/src/activation/set-activation.d.ts +134 -0
  6. package/dist/src/activation/set-activation.js +772 -0
  7. package/dist/src/demote/demote-to-external-source.d.ts +82 -0
  8. package/dist/src/demote/demote-to-external-source.js +233 -0
  9. package/dist/src/errors.d.ts +0 -8
  10. package/dist/src/errors.js +0 -8
  11. package/dist/src/glob.d.ts +35 -0
  12. package/dist/src/glob.js +75 -0
  13. package/dist/src/hooks/configured-agent-outcomes-provider.d.ts +7 -3
  14. package/dist/src/hooks/configured-agent-outcomes-provider.js +11 -4
  15. package/dist/src/hooks/install/plan.d.ts +45 -0
  16. package/dist/src/hooks/install/plan.js +224 -0
  17. package/dist/src/hooks/uninstall/plan.d.ts +20 -0
  18. package/dist/src/hooks/uninstall/plan.js +77 -0
  19. package/dist/src/index.d.ts +48 -16
  20. package/dist/src/index.js +60 -17
  21. package/dist/src/install/aggregate-projection-step.d.ts +16 -0
  22. package/dist/src/install/aggregate-projection-step.js +49 -0
  23. package/dist/src/install/configured.d.ts +65 -0
  24. package/dist/src/install/configured.js +406 -0
  25. package/dist/src/install/inline-mcp-operation.d.ts +4 -0
  26. package/dist/src/install/inline-mcp-operation.js +25 -0
  27. package/dist/src/install/install-extensions.d.ts +110 -0
  28. package/dist/src/install/install-extensions.js +282 -0
  29. package/dist/src/install/per-type-install.d.ts +12 -0
  30. package/dist/src/install/per-type-install.js +12 -0
  31. package/dist/src/install/registry-install-target.d.ts +35 -0
  32. package/dist/src/install/registry-install-target.js +66 -0
  33. package/dist/src/install/registry-login-suggestion.d.ts +21 -0
  34. package/dist/src/install/registry-login-suggestion.js +34 -0
  35. package/dist/src/install/registry-source-resolution.d.ts +54 -0
  36. package/dist/src/install/registry-source-resolution.js +216 -0
  37. package/dist/src/install/root-intent.d.ts +19 -0
  38. package/dist/src/install/root-intent.js +122 -0
  39. package/dist/src/install/selection-interaction.d.ts +46 -0
  40. package/dist/src/install/selection-interaction.js +22 -0
  41. package/dist/src/install/vocabulary.d.ts +143 -0
  42. package/dist/src/install/vocabulary.js +29 -0
  43. package/dist/src/knowledge/install/plan.d.ts +35 -0
  44. package/dist/src/knowledge/install/plan.js +128 -0
  45. package/dist/src/knowledge/uninstall/plan.d.ts +23 -0
  46. package/dist/src/knowledge/uninstall/plan.js +140 -0
  47. package/dist/src/live.d.ts +4 -11
  48. package/dist/src/live.js +4 -11
  49. package/dist/src/mcps/install/plan.d.ts +65 -0
  50. package/dist/src/mcps/install/plan.js +301 -0
  51. package/dist/src/mcps/operations/disable.d.ts +5 -3
  52. package/dist/src/mcps/operations/disable.js +6 -4
  53. package/dist/src/mcps/operations/enable.d.ts +5 -3
  54. package/dist/src/mcps/operations/enable.js +8 -6
  55. package/dist/src/mcps/operations/sync-outcome.d.ts +1 -1
  56. package/dist/src/mcps/operations/uninstall.d.ts +5 -3
  57. package/dist/src/mcps/operations/uninstall.js +4 -4
  58. package/dist/src/mcps/uninstall/plan.d.ts +24 -0
  59. package/dist/src/mcps/uninstall/plan.js +109 -0
  60. package/dist/src/packs/artifact.d.ts +21 -0
  61. package/dist/src/packs/artifact.js +32 -0
  62. package/dist/src/packs/constraint-gate.d.ts +30 -0
  63. package/dist/src/packs/constraint-gate.js +76 -0
  64. package/dist/src/packs/expansion.d.ts +13 -14
  65. package/dist/src/packs/expansion.js +1 -2
  66. package/dist/src/packs/graph-transition.d.ts +64 -0
  67. package/dist/src/packs/graph-transition.js +219 -0
  68. package/dist/src/packs/install/plan.d.ts +95 -0
  69. package/dist/src/packs/install/plan.js +845 -0
  70. package/dist/src/packs/member-install-step.d.ts +29 -0
  71. package/dist/src/packs/member-install-step.js +156 -0
  72. package/dist/src/packs/operations/install.d.ts +3 -3
  73. package/dist/src/packs/operations/install.js +5 -5
  74. package/dist/src/packs/promote-authored-pack.d.ts +86 -0
  75. package/dist/src/packs/promote-authored-pack.js +239 -0
  76. package/dist/src/packs/resolved-dependency.d.ts +9 -26
  77. package/dist/src/packs/resolved-dependency.js +8 -20
  78. package/dist/src/packs/uninstall/plan.d.ts +66 -0
  79. package/dist/src/packs/uninstall/plan.js +344 -0
  80. package/dist/src/packs/uninstall/readiness.d.ts +50 -0
  81. package/dist/src/packs/uninstall/readiness.js +130 -0
  82. package/dist/src/publisher-binding.d.ts +8 -40
  83. package/dist/src/publisher-binding.js +8 -46
  84. package/dist/src/rules/install/plan.d.ts +35 -0
  85. package/dist/src/rules/install/plan.js +133 -0
  86. package/dist/src/rules/uninstall/plan.d.ts +19 -0
  87. package/dist/src/rules/uninstall/plan.js +54 -0
  88. package/dist/src/skills/install/bundled.d.ts +73 -0
  89. package/dist/src/skills/install/bundled.js +244 -0
  90. package/dist/src/skills/install/plan.d.ts +72 -0
  91. package/dist/src/skills/install/plan.js +429 -0
  92. package/dist/src/skills/install/selection.d.ts +26 -0
  93. package/dist/src/skills/install/selection.js +41 -0
  94. package/dist/src/skills/install/source.d.ts +21 -0
  95. package/dist/src/skills/install/source.js +76 -0
  96. package/dist/src/skills/operations/disable.d.ts +3 -2
  97. package/dist/src/skills/operations/disable.js +7 -11
  98. package/dist/src/skills/operations/enable.d.ts +3 -2
  99. package/dist/src/skills/operations/enable.js +6 -8
  100. package/dist/src/skills/operations/install.d.ts +4 -4
  101. package/dist/src/skills/operations/install.js +13 -12
  102. package/dist/src/skills/uninstall/plan.d.ts +23 -0
  103. package/dist/src/skills/uninstall/plan.js +165 -0
  104. package/dist/src/skills/utils.js +1 -1
  105. package/dist/src/step-failure-conversion.d.ts +44 -0
  106. package/dist/src/step-failure-conversion.js +26 -0
  107. package/dist/src/step-failure.d.ts +29 -0
  108. package/dist/src/step-failure.js +130 -0
  109. package/dist/src/subagents/install/plan.d.ts +49 -0
  110. package/dist/src/subagents/install/plan.js +270 -0
  111. package/dist/src/subagents/install/selection.d.ts +26 -0
  112. package/dist/src/subagents/install/selection.js +37 -0
  113. package/dist/src/subagents/install/source.d.ts +18 -0
  114. package/dist/src/subagents/install/source.js +72 -0
  115. package/dist/src/subagents/operations/disable.d.ts +5 -3
  116. package/dist/src/subagents/operations/disable.js +5 -3
  117. package/dist/src/subagents/operations/enable.d.ts +5 -3
  118. package/dist/src/subagents/operations/enable.js +17 -12
  119. package/dist/src/subagents/uninstall/plan.d.ts +20 -0
  120. package/dist/src/subagents/uninstall/plan.js +142 -0
  121. package/dist/src/test-packages.d.ts +34 -0
  122. package/dist/src/test-packages.js +111 -0
  123. package/dist/src/test-registry.d.ts +78 -0
  124. package/dist/src/test-registry.js +223 -0
  125. package/dist/src/testing.d.ts +108 -0
  126. package/dist/src/testing.js +241 -0
  127. package/dist/src/uninstall/retention-policy.d.ts +21 -0
  128. package/dist/src/uninstall/retention-policy.js +28 -0
  129. package/dist/src/uninstall/root-intent.d.ts +17 -0
  130. package/dist/src/uninstall/root-intent.js +101 -0
  131. package/dist/src/uninstall/uninstall-extensions.d.ts +61 -0
  132. package/dist/src/uninstall/uninstall-extensions.js +127 -0
  133. package/dist/src/uninstall/vocabulary.d.ts +40 -0
  134. package/dist/src/uninstall/vocabulary.js +14 -0
  135. package/dist/src/update/atomicity.d.ts +22 -0
  136. package/dist/src/update/atomicity.js +23 -0
  137. package/dist/src/update/blockers.d.ts +31 -0
  138. package/dist/src/update/blockers.js +78 -0
  139. package/dist/src/update/configured.d.ts +78 -0
  140. package/dist/src/update/configured.js +546 -0
  141. package/dist/src/update/root-request.d.ts +39 -0
  142. package/dist/src/update/root-request.js +117 -0
  143. package/dist/src/update/selective/plan.d.ts +45 -0
  144. package/dist/src/update/selective/plan.js +61 -0
  145. package/dist/src/update/selective/requirements.d.ts +17 -0
  146. package/dist/src/update/selective/requirements.js +14 -0
  147. package/dist/src/update/selective/selection.d.ts +63 -0
  148. package/dist/src/update/selective/selection.js +85 -0
  149. package/dist/src/update/selective/skills.d.ts +55 -0
  150. package/dist/src/update/selective/skills.js +596 -0
  151. package/dist/src/update/selective/subagents.d.ts +52 -0
  152. package/dist/src/update/selective/subagents.js +331 -0
  153. package/dist/src/update/selective/use-case.d.ts +121 -0
  154. package/dist/src/update/selective/use-case.js +28 -0
  155. package/dist/src/update/selective/vocabulary.d.ts +43 -0
  156. package/dist/src/update/selective/vocabulary.js +28 -0
  157. package/dist/src/update/selector.d.ts +72 -0
  158. package/dist/src/update/selector.js +120 -0
  159. package/dist/src/update/targeted-plan.d.ts +30 -0
  160. package/dist/src/update/targeted-plan.js +113 -0
  161. package/dist/src/update/update-extensions.d.ts +164 -0
  162. package/dist/src/update/update-extensions.js +579 -0
  163. package/dist/src/workspace-paths.d.ts +31 -0
  164. package/dist/src/workspace-paths.js +46 -0
  165. package/package.json +22 -12
  166. package/dist/src/configured-entry-resolution.d.ts +0 -111
  167. package/dist/src/configured-entry-resolution.js +0 -731
  168. package/dist/src/failure-adapter.d.ts +0 -35
  169. package/dist/src/failure-adapter.js +0 -23
  170. package/dist/src/hooks/manager.d.ts +0 -15
  171. package/dist/src/hooks/manager.js +0 -769
  172. package/dist/src/internal/fs-helpers.d.ts +0 -12
  173. package/dist/src/internal/fs-helpers.js +0 -13
  174. package/dist/src/internal/integrity.d.ts +0 -13
  175. package/dist/src/internal/integrity.js +0 -17
  176. package/dist/src/knowledge/manager.d.ts +0 -11
  177. package/dist/src/knowledge/manager.js +0 -711
  178. package/dist/src/mcps/manager.d.ts +0 -16
  179. package/dist/src/mcps/manager.js +0 -247
  180. package/dist/src/mcps/operations/artifact.d.ts +0 -24
  181. package/dist/src/mcps/operations/artifact.js +0 -74
  182. package/dist/src/mcps/operations/install.d.ts +0 -78
  183. package/dist/src/mcps/operations/install.js +0 -596
  184. package/dist/src/packs/dependency-resolution.d.ts +0 -65
  185. package/dist/src/packs/dependency-resolution.js +0 -414
  186. package/dist/src/packs/manager.d.ts +0 -16
  187. package/dist/src/packs/manager.js +0 -181
  188. package/dist/src/registry-materialization.d.ts +0 -52
  189. package/dist/src/registry-materialization.js +0 -72
  190. package/dist/src/release-age-posture.d.ts +0 -23
  191. package/dist/src/release-age-posture.js +0 -15
  192. package/dist/src/resolution-timeout.d.ts +0 -5
  193. package/dist/src/resolution-timeout.js +0 -11
  194. package/dist/src/rules/manager.d.ts +0 -20
  195. package/dist/src/rules/manager.js +0 -480
  196. package/dist/src/skills/manager.d.ts +0 -18
  197. package/dist/src/skills/manager.js +0 -346
  198. package/dist/src/skills/materialization.d.ts +0 -54
  199. package/dist/src/skills/materialization.js +0 -158
  200. package/dist/src/skills/operations/source-hash.d.ts +0 -2
  201. package/dist/src/skills/operations/source-hash.js +0 -6
  202. package/dist/src/subagents/manager.d.ts +0 -18
  203. package/dist/src/subagents/manager.js +0 -722
  204. package/dist/src/workflows/install-command/workflow.d.ts +0 -56
  205. package/dist/src/workflows/install-command/workflow.js +0 -46
  206. package/dist/src/workflows/uninstall-command/workflow.d.ts +0 -42
  207. package/dist/src/workflows/uninstall-command/workflow.js +0 -29
@@ -0,0 +1,772 @@
1
+ /**
2
+ * Activation: turning one installed extension on or off without changing
3
+ * what the workspace acquired.
4
+ *
5
+ * All seven extension types share one request, one settled candidate, and one
6
+ * resolution, because activation means the same thing for every type: desired
7
+ * state records the preference, and the projections that preference implies
8
+ * are created or removed. What differs is only how a type is projected — a
9
+ * skill through agent artifacts, an MCP server through each agent's native
10
+ * configuration, a rule or Knowledge bundle through a shared managed region,
11
+ * a Pack through the members it contributes.
12
+ *
13
+ * Two invariants hold across every type. Disabling never removes canonical
14
+ * content or retires an accepted resolution, so re-enabling never re-resolves
15
+ * or re-acquires. And a type whose projection shares a file with instruction
16
+ * management reconciles that file inside the same transaction, so the
17
+ * workspace is never left with a settings change the files do not reflect.
18
+ *
19
+ * @experimental This API is unstable and may change without notice.
20
+ */
21
+ import * as Effect from "effect/Effect";
22
+ import * as FileSystem from "effect/FileSystem";
23
+ import * as Option from "effect/Option";
24
+ import * as Path from "effect/Path";
25
+ import * as Schema from "effect/Schema";
26
+ import { resolveInstalledIdentifierNameOrInput } from "@agentxm/extension-sources";
27
+ import { SourceHostProviders, WorkspaceCatalog, } from "@agentxm/extension-sources";
28
+ import { buildInstallOperation, collectRetainedMaterializeSteps, ExtensionManagers, installMcpServer, RetainedContentUnusable, SubagentManager, } from "@agentxm/extension-materialization";
29
+ import { makeConfiguredReleaseAgeEvaluation, ReleaseAgePosture, resolveConfiguredHook, resolveConfiguredKnowledge, resolveConfiguredRule, } from "@agentxm/extension-resolution";
30
+ import { activeInstructionsConfig, applyProjectionPlans, CodingAgentRepository, findManagedSubagentFiles, instructionReconciliationReadiness, observeInstructions, projectionPlanExclusionWarnings, reconcileInstructionTransition, } from "@agentxm/workspace-projection";
31
+ import { OperationJournal, operationPresentation, prepareExecutionCandidate, resolveExecutionCandidate, ResolvePlanInteraction, StepFailure, } from "@agentxm/workspace-operations";
32
+ import { acquiredExtensionDisplayPathFromLockEntry, ConfiguredAgentOutcomesProvider, installedRowsByName, isDesiredExtensionActive, RenderedFilePathSchema, sanitizeName, WorkspaceMutations, } from "@agentxm/workspace-state";
33
+ import { FootprintRecorder, runWorkspaceTransaction, WorkspaceTransactionScope, } from "@agentxm/workspace-transactions";
34
+ import { ExtensionLifecycleFailed } from "../errors.js";
35
+ import { StepFailureConversion, withAdaptedStepFailures, } from "../step-failure-conversion.js";
36
+ import { enableMcpServer } from "../mcps/operations/enable.js";
37
+ import { disableMcpServer } from "../mcps/operations/disable.js";
38
+ import { enableSkill } from "../skills/operations/enable.js";
39
+ import { disableSkill } from "../skills/operations/disable.js";
40
+ import { enableSubagent } from "../subagents/operations/enable.js";
41
+ import { disableSubagent } from "../subagents/operations/disable.js";
42
+ import { canonicalNodeDisplayPath, canonicalRootDisplayPath, lockfileDisplayPath, settingsDisplayPath, } from "./display-paths.js";
43
+ // -----------------------------------------------------------------------------
44
+ // Shared vocabulary
45
+ // -----------------------------------------------------------------------------
46
+ const decodeRenderedFilePath = Schema.decodeUnknownSync(RenderedFilePathSchema);
47
+ const TYPE_LABEL = {
48
+ skill: "Skill",
49
+ subagent: "Subagent",
50
+ "mcp-server": "MCP server",
51
+ rule: "rule",
52
+ hook: "hooks package",
53
+ knowledge: "Knowledge bundle",
54
+ pack: "Pack",
55
+ };
56
+ /** How the plan names its subject, matching the command that raised it. */
57
+ const PLAN_SUBJECT = {
58
+ skill: "skill",
59
+ subagent: "subagent",
60
+ "mcp-server": "MCP server",
61
+ rule: "rules",
62
+ hook: "hooks",
63
+ knowledge: "knowledge bundle",
64
+ pack: "pack",
65
+ };
66
+ const AGGREGATE_UNIT_TYPES = new Set([
67
+ "rule",
68
+ "hook",
69
+ "knowledge",
70
+ ]);
71
+ const unchanged = (request, name, message) => ({
72
+ _tag: "Unchanged",
73
+ type: request.type,
74
+ name,
75
+ enabled: request.enabled,
76
+ message,
77
+ });
78
+ const notInstalled = (type, name, listCommand) => new ExtensionLifecycleFailed({
79
+ category: "not_found",
80
+ detail: `${TYPE_LABEL[type]} '${name}' is not installed`,
81
+ suggestions: [{ description: `Inspect installed ${type}s`, cmd: listCommand }],
82
+ });
83
+ const settingsArtifact = (scope) => ({
84
+ path: settingsDisplayPath(scope),
85
+ scope,
86
+ change: "updated",
87
+ targets: [{ path: settingsDisplayPath(scope), change: "updated" }],
88
+ });
89
+ const normalizedPackIdentity = (identity) => identity.startsWith("workspace:") ? identity.slice("workspace:".length) : identity;
90
+ const packContributesTo = (node, packIdentity) => node.origins.some((origin) => origin.type === "pack" && normalizedPackIdentity(origin.pack) === packIdentity);
91
+ const remainsActiveWithoutPack = (node, packIdentity) => isDesiredExtensionActive(node.origins.filter((origin) => origin.type !== "pack" || normalizedPackIdentity(origin.pack) !== packIdentity));
92
+ // -----------------------------------------------------------------------------
93
+ // prepare
94
+ // -----------------------------------------------------------------------------
95
+ /** The gate a rule transition passes before it may reconcile instruction files. */
96
+ const instructionGate = (ws) => Effect.gen(function* () {
97
+ const config = yield* activeInstructionsConfig(ws);
98
+ if (Option.isNone(config)) {
99
+ return { config, blocked: Option.none() };
100
+ }
101
+ const snapshot = yield* observeInstructions({ ws, config: config.value });
102
+ const readiness = yield* instructionReconciliationReadiness({ ws, snapshot });
103
+ return {
104
+ config,
105
+ blocked: Option.map(readiness, (failure) => new ExtensionLifecycleFailed({
106
+ category: "conflict",
107
+ detail: failure._tag === "InstructionMaintenanceFailed"
108
+ ? failure.detail
109
+ : "Instruction reconciliation cannot proceed against the current workspace",
110
+ cause: failure,
111
+ })),
112
+ };
113
+ });
114
+ const hookLockArtifact = (args) => {
115
+ if (Option.isNone(args.lockEntry))
116
+ return settingsArtifact(args.scope);
117
+ const entry = args.lockEntry.value;
118
+ const packagePath = acquiredExtensionDisplayPathFromLockEntry(canonicalRootDisplayPath(args.scope), entry, "hooks", args.name);
119
+ const targets = (args.enabled
120
+ ? [
121
+ { path: settingsDisplayPath(args.scope), change: "updated" },
122
+ { path: lockfileDisplayPath(args.scope), change: "updated" },
123
+ { path: packagePath, change: "created" },
124
+ ]
125
+ : [
126
+ { path: settingsDisplayPath(args.scope), change: "updated" },
127
+ { path: packagePath, change: "removed" },
128
+ ]).sort((left, right) => left.path.localeCompare(right.path));
129
+ const version = entry.type === "registry" ? entry.resolvedVersion : undefined;
130
+ return {
131
+ path: settingsDisplayPath(args.scope),
132
+ scope: args.scope,
133
+ ...(args.enabled && version !== undefined ? { version } : {}),
134
+ change: "updated",
135
+ targets,
136
+ };
137
+ };
138
+ /**
139
+ * Settle an activation request into the change it makes.
140
+ *
141
+ * A request the workspace already satisfies settles as `Unchanged` rather
142
+ * than as an empty plan, so repeating a command reports what is true instead
143
+ * of offering to do nothing. A request naming a subject the workspace does
144
+ * not hold fails with the type's own recovery route.
145
+ */
146
+ export const prepareSetActivation = (request) => Effect.gen(function* () {
147
+ const ws = yield* WorkspaceMutations;
148
+ const path = yield* Path.Path;
149
+ const scope = ws.scope;
150
+ const verb = request.enabled ? "enabled" : "disabled";
151
+ switch (request.type) {
152
+ case "skill":
153
+ case "subagent": {
154
+ const name = yield* resolveInstalledIdentifierNameOrInput({
155
+ input: request.name,
156
+ resourceType: request.type,
157
+ });
158
+ const rows = yield* ws.records.rows(request.type).pipe(Effect.map(installedRowsByName));
159
+ const entry = rows[name];
160
+ if (entry === undefined) {
161
+ return yield* notInstalled(request.type, request.name, `axm ${request.type}s list`);
162
+ }
163
+ if (request.enabled && entry.enabled) {
164
+ return unchanged(request, name, `${TYPE_LABEL[request.type]} '${name}' is already enabled`);
165
+ }
166
+ // An implicit member is always active, so only a configured row can
167
+ // already be disabled; disabling an implicit one promotes it.
168
+ if (!request.enabled && entry.lifecycle === "configured" && !entry.enabled) {
169
+ return unchanged(request, name, `${TYPE_LABEL[request.type]} '${name}' is already disabled`);
170
+ }
171
+ return {
172
+ _tag: "SetActivation",
173
+ type: request.type,
174
+ name,
175
+ enabled: request.enabled,
176
+ scope,
177
+ artifact: settingsArtifact(scope),
178
+ transition: { kind: request.type },
179
+ instructions: Option.none(),
180
+ blocked: Option.none(),
181
+ warning: Option.none(),
182
+ agentOutcomes: [],
183
+ };
184
+ }
185
+ case "mcp-server": {
186
+ const configured = yield* ws.getConfiguredMcpServerEntries();
187
+ const entry = configured[request.name];
188
+ if (entry === undefined) {
189
+ return unchanged(request, request.name, `MCP server "${request.name}" is not configured`);
190
+ }
191
+ if (entry.enabled === request.enabled) {
192
+ return unchanged(request, request.name, `MCP server "${request.name}" is already ${verb}`);
193
+ }
194
+ return {
195
+ _tag: "SetActivation",
196
+ type: "mcp-server",
197
+ name: request.name,
198
+ enabled: request.enabled,
199
+ scope,
200
+ artifact: settingsArtifact(scope),
201
+ transition: { kind: "mcp-server" },
202
+ instructions: Option.none(),
203
+ blocked: Option.none(),
204
+ warning: Option.none(),
205
+ agentOutcomes: [],
206
+ };
207
+ }
208
+ case "rule": {
209
+ const configured = yield* ws.getConfiguredRuleEntries();
210
+ const entry = configured[request.name];
211
+ if (entry === undefined) {
212
+ return unchanged(request, request.name, `rule "${request.name}" is not configured`);
213
+ }
214
+ if (entry.enabled === request.enabled) {
215
+ return unchanged(request, request.name, `rule "${request.name}" is already ${verb}`);
216
+ }
217
+ const gate = yield* instructionGate(ws);
218
+ const resolved = request.enabled
219
+ ? Option.some(yield* resolveConfiguredRule(request.name, entry.source, yield* makeConfiguredReleaseAgeEvaluation()))
220
+ : Option.none();
221
+ return {
222
+ _tag: "SetActivation",
223
+ type: "rule",
224
+ name: request.name,
225
+ enabled: request.enabled,
226
+ scope,
227
+ artifact: settingsArtifact(scope),
228
+ transition: Option.match(resolved, {
229
+ onNone: () => ({ kind: "deactivate-rule" }),
230
+ onSome: ({ ref, versionRange }) => ({
231
+ kind: "reinstall",
232
+ ref,
233
+ versionRange,
234
+ }),
235
+ }),
236
+ instructions: gate.config,
237
+ blocked: gate.blocked,
238
+ warning: Option.none(),
239
+ agentOutcomes: [],
240
+ };
241
+ }
242
+ case "hook": {
243
+ const configured = yield* ws.getConfiguredHookEntries();
244
+ const entry = configured[request.name];
245
+ if (entry === undefined) {
246
+ return unchanged(request, request.name, `hooks package "${request.name}" is not configured`);
247
+ }
248
+ if (entry.enabled === request.enabled) {
249
+ return unchanged(request, request.name, `hooks package "${request.name}" is already ${verb}`);
250
+ }
251
+ const lockEntry = yield* ws
252
+ .getLockedHookEntry(request.name)
253
+ .pipe(Effect.catch(() => Effect.succeed(Option.none())));
254
+ const artifact = hookLockArtifact({
255
+ lockEntry,
256
+ name: request.name,
257
+ scope,
258
+ enabled: request.enabled,
259
+ });
260
+ if (!request.enabled) {
261
+ return {
262
+ _tag: "SetActivation",
263
+ type: "hook",
264
+ name: request.name,
265
+ enabled: false,
266
+ scope,
267
+ artifact,
268
+ transition: { kind: "deactivate-hook" },
269
+ instructions: Option.none(),
270
+ blocked: Option.none(),
271
+ warning: Option.none(),
272
+ agentOutcomes: [],
273
+ };
274
+ }
275
+ const { ref, versionRange } = yield* resolveConfiguredHook(request.name, entry.source, yield* makeConfiguredReleaseAgeEvaluation());
276
+ const managers = yield* ExtensionManagers;
277
+ // Hook packages report per-agent outcomes because a hooks package can
278
+ // be projected for one agent and not another.
279
+ const agentOutcomes = managers.hook.configuredAgentOutcomesForRef === undefined
280
+ ? []
281
+ : yield* managers.hook.configuredAgentOutcomesForRef(ref, "projected");
282
+ return {
283
+ _tag: "SetActivation",
284
+ type: "hook",
285
+ name: request.name,
286
+ enabled: true,
287
+ scope,
288
+ artifact,
289
+ transition: { kind: "reinstall", ref, versionRange },
290
+ instructions: Option.none(),
291
+ blocked: Option.none(),
292
+ warning: Option.none(),
293
+ agentOutcomes,
294
+ };
295
+ }
296
+ case "knowledge": {
297
+ const configured = yield* ws.getConfiguredKnowledgeEntries();
298
+ const entry = configured[request.name];
299
+ if (entry === undefined) {
300
+ return yield* new ExtensionLifecycleFailed({
301
+ category: "not_found",
302
+ detail: `Knowledge bundle "${request.name}" is not configured`,
303
+ });
304
+ }
305
+ if (entry.enabled === request.enabled) {
306
+ return unchanged(request, request.name, `Knowledge bundle "${request.name}" is already ${verb}`);
307
+ }
308
+ if (!request.enabled) {
309
+ return {
310
+ _tag: "SetActivation",
311
+ type: "knowledge",
312
+ name: request.name,
313
+ enabled: false,
314
+ scope,
315
+ artifact: settingsArtifact(scope),
316
+ transition: { kind: "deactivate-knowledge" },
317
+ instructions: Option.none(),
318
+ blocked: Option.none(),
319
+ warning: Option.none(),
320
+ agentOutcomes: [],
321
+ };
322
+ }
323
+ const { ref, versionRange } = yield* resolveConfiguredKnowledge(request.name, entry.source, yield* makeConfiguredReleaseAgeEvaluation());
324
+ return {
325
+ _tag: "SetActivation",
326
+ type: "knowledge",
327
+ name: request.name,
328
+ enabled: true,
329
+ scope,
330
+ artifact: settingsArtifact(scope),
331
+ transition: { kind: "reinstall", ref, versionRange },
332
+ instructions: Option.none(),
333
+ blocked: Option.none(),
334
+ warning: Option.none(),
335
+ agentOutcomes: [],
336
+ };
337
+ }
338
+ case "pack": {
339
+ const configured = yield* ws.getConfiguredPackEntries();
340
+ const entry = configured[request.name];
341
+ if (entry === undefined) {
342
+ return yield* new ExtensionLifecycleFailed({
343
+ category: "not_found",
344
+ detail: `Pack "${request.name}" is not configured`,
345
+ suggestions: [{ description: "Inspect installed packs", cmd: "axm packs list" }],
346
+ });
347
+ }
348
+ if (entry.enabled === request.enabled) {
349
+ return unchanged(request, request.name, `Pack "${request.name}" is already ${verb}`);
350
+ }
351
+ const graph = yield* ws.getDesiredStateGraph();
352
+ if (!graph.complete) {
353
+ return yield* new ExtensionLifecycleFailed({
354
+ category: "conflict",
355
+ detail: `Cannot ${request.enabled ? "enable" : "disable"} the pack while desired state is unresolved`,
356
+ suggestions: [{ description: "Inspect workspace facts", cmd: "axm lint" }],
357
+ });
358
+ }
359
+ const packNode = graph.nodes.find((node) => node.type === "pack" && node.name === request.name);
360
+ if (packNode === undefined) {
361
+ return yield* new ExtensionLifecycleFailed({
362
+ category: "not_found",
363
+ detail: `Pack "${request.name}" was not found`,
364
+ });
365
+ }
366
+ const identity = normalizedPackIdentity(packNode.identity);
367
+ // Disabling moves only the members that lose their last active origin;
368
+ // enabling moves every member the Pack contributes.
369
+ const affected = graph.nodes.filter((node) => node.type !== "pack" &&
370
+ node.enabled &&
371
+ packContributesTo(node, identity) &&
372
+ !remainsActiveWithoutPack(node, identity));
373
+ const contributed = graph.nodes.filter((node) => node.type !== "pack" && packContributesTo(node, identity));
374
+ const members = request.enabled ? contributed : affected;
375
+ const memberTargets = members.map((node) => ({
376
+ path: canonicalNodeDisplayPath(path, ws, node),
377
+ change: "unchanged",
378
+ }));
379
+ return {
380
+ _tag: "SetActivation",
381
+ type: "pack",
382
+ name: request.name,
383
+ enabled: request.enabled,
384
+ scope,
385
+ artifact: {
386
+ path: settingsDisplayPath(scope),
387
+ scope,
388
+ change: "updated",
389
+ fileCount: memberTargets.length + 1,
390
+ targets: [{ path: settingsDisplayPath(scope), change: "updated" }, ...memberTargets],
391
+ },
392
+ transition: {
393
+ kind: "pack",
394
+ identity,
395
+ members,
396
+ aggregateTypes: new Set(members.map((node) => node.type)),
397
+ },
398
+ instructions: Option.none(),
399
+ blocked: Option.none(),
400
+ warning: Option.none(),
401
+ agentOutcomes: [],
402
+ };
403
+ }
404
+ }
405
+ });
406
+ // -----------------------------------------------------------------------------
407
+ // previewOrApply
408
+ // -----------------------------------------------------------------------------
409
+ /** Remove one Pack member's own projection, by what the member projects into. */
410
+ const dematerializeMember = (node) => Effect.gen(function* () {
411
+ const ws = yield* WorkspaceMutations;
412
+ const path = yield* Path.Path;
413
+ const agentRepo = yield* CodingAgentRepository;
414
+ const managers = yield* ExtensionManagers;
415
+ switch (node.type) {
416
+ case "skill":
417
+ return yield* managers.skill.materializeDeactivate({
418
+ target: { type: "skill", name: node.name },
419
+ });
420
+ case "mcp-server": {
421
+ const agents = yield* agentRepo.getConfiguredAgents();
422
+ return yield* Effect.forEach(agents, (agent) => agent
423
+ .removeMcpServer({
424
+ workspaceRoot: ws.baseDir,
425
+ scope: ws.scope,
426
+ serverName: node.name,
427
+ disableOnly: true,
428
+ })
429
+ .pipe(Effect.flatMap((outcome) => outcome._tag === "disabled" ||
430
+ outcome._tag === "nothing-runnable" ||
431
+ outcome._tag === "needs-input" ||
432
+ outcome._tag === "misconfigured" ||
433
+ outcome._tag === "failed"
434
+ ? Effect.fail(new ExtensionLifecycleFailed({
435
+ category: "conflict",
436
+ detail: `MCP server removal failed for ${agent.id}: ${outcome.reason}`,
437
+ }))
438
+ : Effect.void)), { concurrency: "unbounded", discard: true });
439
+ }
440
+ case "subagent": {
441
+ const agents = yield* agentRepo.getConfiguredAgents();
442
+ return yield* Effect.forEach(agents, (agent) => agent.resolveEffectiveSubagentsDir({ workspaceRoot: ws.baseDir, scope: ws.scope }).pipe(Effect.flatMap((resolved) => resolved._tag !== "supported"
443
+ ? Effect.void
444
+ : findManagedSubagentFiles(resolved.dir, sanitizeName(node.name)).pipe(Effect.flatMap((managedPaths) => agent
445
+ .removeSubagent({
446
+ workspaceRoot: ws.baseDir,
447
+ scope: ws.scope,
448
+ subagentName: node.name,
449
+ renderedFilePaths: managedPaths.map((filePath) => decodeRenderedFilePath(path.relative(ws.baseDir, filePath))),
450
+ })
451
+ .pipe(Effect.flatMap((outcome) => outcome._tag === "conflict"
452
+ ? new ExtensionLifecycleFailed({
453
+ category: "conflict",
454
+ detail: `Subagent removal failed for ${agent.id}: ${outcome.reason}`,
455
+ })
456
+ : Effect.void)))))), { concurrency: "unbounded", discard: true });
457
+ }
458
+ case "rule":
459
+ case "hook":
460
+ case "knowledge":
461
+ case "pack":
462
+ // These types own only shared aggregate units. One trailing render
463
+ // reflects the post-transition graph for the whole closure.
464
+ return;
465
+ }
466
+ });
467
+ /**
468
+ * Render the shared aggregate units the moved member types contribute to,
469
+ * once for the whole closure rather than once per contributor.
470
+ */
471
+ const reconcileAggregateProjections = (types) => Effect.gen(function* () {
472
+ const managers = yield* ExtensionManagers;
473
+ const plans = [];
474
+ if (types.has("rule"))
475
+ plans.push(...(yield* managers.rule.projectionPlans()));
476
+ if (types.has("hook"))
477
+ plans.push(...(yield* managers.hook.projectionPlans()));
478
+ if (types.has("knowledge"))
479
+ plans.push(...(yield* managers.knowledge.projectionPlans()));
480
+ yield* applyProjectionPlans(plans);
481
+ return projectionPlanExclusionWarnings(plans);
482
+ });
483
+ /**
484
+ * The Pack graph after the change must say what the change claimed: the Pack
485
+ * carries its new activation, and every member the change deactivated is
486
+ * inactive. Validated inside the transaction, so a graph that disagrees is
487
+ * restored rather than committed.
488
+ */
489
+ const validatePackActivation = (candidate) => Effect.gen(function* () {
490
+ const ws = yield* WorkspaceMutations;
491
+ const graph = yield* ws.getDesiredStateGraph();
492
+ const packNode = graph.nodes.find((node) => node.type === "pack" && node.name === candidate.name);
493
+ if (packNode === undefined || packNode.enabled !== candidate.enabled) {
494
+ return yield* new ExtensionLifecycleFailed({
495
+ category: "conflict",
496
+ detail: `Pack "${candidate.name}" did not reach the ${candidate.enabled ? "enabled" : "disabled"} state`,
497
+ });
498
+ }
499
+ if (candidate.enabled)
500
+ return;
501
+ const stillActive = candidate.members.filter((member) => graph.nodes.some((node) => node.type === member.type && node.name === member.name && node.enabled));
502
+ if (stillActive.length > 0) {
503
+ return yield* new ExtensionLifecycleFailed({
504
+ category: "conflict",
505
+ detail: `Disabling "${candidate.name}" left members active: ${stillActive
506
+ .map((member) => `${member.type} ${member.name}`)
507
+ .join(", ")}`,
508
+ });
509
+ }
510
+ });
511
+ const NO_WARNINGS = [];
512
+ /** Disabling a rule: record the preference, then remove its projection. */
513
+ const deactivateRuleTransition = (candidate) => Effect.gen(function* () {
514
+ const ws = yield* WorkspaceMutations;
515
+ const managers = yield* ExtensionManagers;
516
+ yield* ws.updateRuleEntry(candidate.name, (current) => ({ ...current, enabled: false }));
517
+ yield* managers.rule.materializeDeactivate({
518
+ target: { type: "rule", name: candidate.name },
519
+ });
520
+ return NO_WARNINGS;
521
+ });
522
+ /** Disabling a hooks package: record the preference, then stop projecting it. */
523
+ const deactivateHookTransition = (candidate) => Effect.gen(function* () {
524
+ const ws = yield* WorkspaceMutations;
525
+ const managers = yield* ExtensionManagers;
526
+ yield* ws.updateHookEntry(candidate.name, (current) => ({ ...current, enabled: false }));
527
+ yield* managers.hook.materializeDeactivate({
528
+ target: { type: "hook", name: candidate.name },
529
+ });
530
+ return NO_WARNINGS;
531
+ });
532
+ /**
533
+ * Disabling a Knowledge bundle: record the preference, then re-render the
534
+ * discovery region. Re-rendering is what deactivation means for Knowledge, so
535
+ * this step carries the region's own exclusion report.
536
+ */
537
+ const deactivateKnowledgeTransition = (candidate) => Effect.gen(function* () {
538
+ const ws = yield* WorkspaceMutations;
539
+ const managers = yield* ExtensionManagers;
540
+ yield* ws.updateKnowledgeEntry(candidate.name, (current) => ({ ...current, enabled: false }));
541
+ const plans = yield* managers.knowledge.projectionPlans();
542
+ yield* applyProjectionPlans(plans);
543
+ return projectionPlanExclusionWarnings(plans);
544
+ });
545
+ /** Enabling a Pack, or disabling it and the members it alone kept active. */
546
+ const packTransition = (candidate, transition) => Effect.gen(function* () {
547
+ const ws = yield* WorkspaceMutations;
548
+ const conversion = yield* StepFailureConversion;
549
+ const configured = yield* ws.getConfiguredPackEntries();
550
+ const entry = configured[candidate.name];
551
+ if (entry === undefined) {
552
+ return yield* new ExtensionLifecycleFailed({
553
+ category: "not_found",
554
+ detail: `Pack "${candidate.name}" is no longer configured`,
555
+ });
556
+ }
557
+ yield* ws.setPackEntry(candidate.name, { ...entry, enabled: candidate.enabled });
558
+ if (candidate.enabled) {
559
+ // A disabled Pack contributes nothing, so the members the planner saw
560
+ // still carried the old inactive state. Re-read the closure from the
561
+ // graph this transition just produced: retained re-materialization
562
+ // covers active members only, and they became active one line ago.
563
+ const graph = yield* ws.getDesiredStateGraph();
564
+ const activeMembers = graph.nodes.filter((node) => node.type !== "pack" && packContributesTo(node, transition.identity));
565
+ // The capability refuses when accepted content is not usable; that is
566
+ // this feature's own conflict, reported with the recovery route the
567
+ // operator can actually take.
568
+ const { steps } = yield* collectRetainedMaterializeSteps({
569
+ nodes: activeMembers,
570
+ adapter: { toStepFailure: conversion.toStepFailure },
571
+ runMcpServerInstall: ({ ref }) => installMcpServer({
572
+ name: "install-mcp-server",
573
+ args: {
574
+ ref,
575
+ nonInteractive: true,
576
+ force: false,
577
+ allowWorkspaceSourceTransition: false,
578
+ versionRange: Option.none(),
579
+ skipSettings: Option.some(true),
580
+ },
581
+ }).pipe(withAdaptedStepFailures),
582
+ }).pipe(Effect.mapError((failure) => failure instanceof RetainedContentUnusable
583
+ ? new ExtensionLifecycleFailed({
584
+ category: "conflict",
585
+ detail: failure.detail,
586
+ suggestions: [
587
+ {
588
+ description: "Refresh the pack and its retained members",
589
+ cmd: "axm packs update --yes",
590
+ },
591
+ ],
592
+ cause: failure,
593
+ })
594
+ : failure));
595
+ yield* Effect.forEach(steps, (step) => step.readiness === "error"
596
+ ? Effect.fail(new ExtensionLifecycleFailed({ category: "conflict", detail: step.errorMessage }))
597
+ : step.run.pipe(Effect.asVoid), { concurrency: 1, discard: true });
598
+ }
599
+ else {
600
+ yield* Effect.forEach(transition.members.filter((node) => !AGGREGATE_UNIT_TYPES.has(node.type)), dematerializeMember, { concurrency: 1, discard: true });
601
+ }
602
+ return yield* reconcileAggregateProjections(transition.aggregateTypes);
603
+ });
604
+ /**
605
+ * The step for a type whose executor owns its whole transition, including the
606
+ * transaction it opens and the artifact it observes.
607
+ */
608
+ const executorStep = (candidate, kind) => {
609
+ const run = kind === "skill"
610
+ ? candidate.enabled
611
+ ? enableSkill({ name: "enable-skill", args: { skillName: candidate.name } })
612
+ : disableSkill({ name: "disable-skill", args: { skillName: candidate.name } })
613
+ : kind === "subagent"
614
+ ? candidate.enabled
615
+ ? enableSubagent({ name: "enable-subagent", args: { subagentName: candidate.name } })
616
+ : disableSubagent({ name: "disable-subagent", args: { subagentName: candidate.name } })
617
+ : candidate.enabled
618
+ ? enableMcpServer({ name: "enable-mcp-server", args: { serverName: candidate.name } })
619
+ : disableMcpServer({ name: "disable-mcp-server", args: { serverName: candidate.name } });
620
+ return { label: candidate.name, readiness: "ready", run };
621
+ };
622
+ /**
623
+ * The step for a type whose transition this use case composes itself: the
624
+ * preference, its projections, and any instruction file they share, inside
625
+ * one transaction.
626
+ */
627
+ const transactionStep = (candidate, transition, validate) => {
628
+ const { artifact } = candidate;
629
+ return {
630
+ label: candidate.name,
631
+ readiness: "ready",
632
+ artifact,
633
+ ...(candidate.agentOutcomes.length === 0 ? {} : { agentOutcomes: candidate.agentOutcomes }),
634
+ run: Effect.gen(function* () {
635
+ const ws = yield* WorkspaceMutations;
636
+ const warnings = yield* runWorkspaceTransaction({
637
+ transition: Option.match(candidate.instructions, {
638
+ onNone: () => transition,
639
+ onSome: (config) => reconcileInstructionTransition({ ws, config, transition }),
640
+ }),
641
+ validate,
642
+ });
643
+ return {
644
+ result: "success",
645
+ message: `${candidate.enabled ? "Enabled" : "Disabled"} ${candidate.name}`,
646
+ artifact,
647
+ ...(warnings.length === 0 ? {} : { warnings }),
648
+ };
649
+ }).pipe(withAdaptedStepFailures),
650
+ };
651
+ };
652
+ const activationStep = (candidate) => {
653
+ if (Option.isSome(candidate.blocked)) {
654
+ return {
655
+ label: candidate.name,
656
+ readiness: "error",
657
+ errorMessage: candidate.blocked.value.detail ?? "Activation cannot proceed",
658
+ };
659
+ }
660
+ const transition = candidate.transition;
661
+ switch (transition.kind) {
662
+ case "skill":
663
+ case "subagent":
664
+ case "mcp-server":
665
+ return executorStep(candidate, transition.kind);
666
+ case "reinstall":
667
+ return reinstallStep(candidate, transition);
668
+ case "deactivate-rule":
669
+ return transactionStep(candidate, deactivateRuleTransition(candidate), () => Effect.void);
670
+ case "deactivate-hook":
671
+ return transactionStep(candidate, deactivateHookTransition(candidate), () => Effect.void);
672
+ case "deactivate-knowledge":
673
+ return transactionStep(candidate, deactivateKnowledgeTransition(candidate), () => Effect.void);
674
+ case "pack":
675
+ return transactionStep(candidate, packTransition(candidate, transition), () => validatePackActivation({
676
+ name: candidate.name,
677
+ enabled: candidate.enabled,
678
+ members: transition.members,
679
+ }));
680
+ }
681
+ };
682
+ /**
683
+ * Enabling a rule, hooks package, or Knowledge bundle re-runs the configured
684
+ * install closure for the entry the workspace already accepted, so the same
685
+ * publisher-trust and release-age evidence a first install carries travels
686
+ * with the re-activation.
687
+ */
688
+ const reinstallStep = (candidate, transition) => {
689
+ const { artifact } = candidate;
690
+ const install = (managers, toStepFailure) => {
691
+ const common = {
692
+ toStepFailure,
693
+ versionRange: transition.versionRange,
694
+ message: `Enabled ${candidate.name}`,
695
+ buildArtifact: () => Effect.succeed(artifact),
696
+ };
697
+ switch (transition.ref.type) {
698
+ case "rule":
699
+ return buildInstallOperation(managers.rule, { ...common, ref: transition.ref });
700
+ case "hook":
701
+ return buildInstallOperation(managers.hook, { ...common, ref: transition.ref });
702
+ case "knowledge":
703
+ return buildInstallOperation(managers.knowledge, { ...common, ref: transition.ref });
704
+ case "skill":
705
+ return buildInstallOperation(managers.skill, { ...common, ref: transition.ref });
706
+ case "subagent":
707
+ return buildInstallOperation(managers.subagent, { ...common, ref: transition.ref });
708
+ case "mcp-server":
709
+ return buildInstallOperation(managers["mcp-server"], { ...common, ref: transition.ref });
710
+ case "pack":
711
+ return buildInstallOperation(managers.pack, { ...common, ref: transition.ref });
712
+ }
713
+ };
714
+ const run = Effect.gen(function* () {
715
+ const ws = yield* WorkspaceMutations;
716
+ const managers = yield* ExtensionManagers;
717
+ const conversion = yield* StepFailureConversion;
718
+ const step = install(managers, conversion.toStepFailure);
719
+ if (step.readiness === "error") {
720
+ return yield* new StepFailure({ category: "conflict", detail: step.errorMessage });
721
+ }
722
+ return yield* Option.match(candidate.instructions, {
723
+ onNone: () => step.run,
724
+ onSome: (config) => runWorkspaceTransaction({
725
+ transition: reconcileInstructionTransition({ ws, config, transition: step.run }),
726
+ validate: () => Effect.void,
727
+ }).pipe(withAdaptedStepFailures),
728
+ });
729
+ });
730
+ return {
731
+ label: candidate.name,
732
+ readiness: "ready",
733
+ artifact,
734
+ ...(candidate.agentOutcomes.length === 0 ? {} : { agentOutcomes: candidate.agentOutcomes }),
735
+ run,
736
+ };
737
+ };
738
+ /**
739
+ * Preview or apply a settled activation. The whole change — the preference,
740
+ * the projections it implies, and any instruction file it shares — runs in
741
+ * one workspace transaction, so a projection that cannot be written never
742
+ * leaves the preference recorded.
743
+ */
744
+ export const previewOrApplySetActivation = (candidate, execution) => Effect.gen(function* () {
745
+ const plan = {
746
+ _tag: "Plan",
747
+ name: `${candidate.enabled ? "Enable" : "Disable"} ${PLAN_SUBJECT[candidate.type]}`,
748
+ description: Option.some(`${candidate.enabled ? "Enable" : "Disable"} ${candidate.name}${candidate.type === "pack" ? " without changing locked versions" : ""}`),
749
+ presentation: operationPresentation(candidate.enabled
750
+ ? { imperative: "enable", past: "Enabled", gerund: "Enabling" }
751
+ : { imperative: "disable", past: "Disabled", gerund: "Disabling" }, candidate.type),
752
+ jobs: [{ concurrency: 1, steps: [activationStep(candidate)] }],
753
+ };
754
+ // Every activation moves exactly one extension to one desired state, so
755
+ // the resolution projects that operation's configured-agent outcomes
756
+ // before the change and verifies them after it.
757
+ const configuredAgentOperations = [
758
+ {
759
+ extensionType: candidate.type,
760
+ name: candidate.name,
761
+ plannedState: candidate.enabled ? "enabled" : "disabled",
762
+ },
763
+ ];
764
+ const prepared = yield* prepareExecutionCandidate(plan, { configuredAgentOperations });
765
+ return yield* resolveExecutionCandidate(prepared, execution);
766
+ });
767
+ /** The activation use case. */
768
+ export const SetActivation = {
769
+ prepare: prepareSetActivation,
770
+ previewOrApply: previewOrApplySetActivation,
771
+ };
772
+ //# sourceMappingURL=set-activation.js.map