@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,223 @@
1
+ // @effect-diagnostics globalDate:off — ZIP's driver API requires one fixed Date value and never reads the ambient clock
2
+ /**
3
+ * A file-backed Registry a specification can publish into.
4
+ *
5
+ * Writes the real Registry directory layout — a per-extension index plus
6
+ * version archives with genuine integrity hashes — that the production
7
+ * source-resolution layer reads over `file://`. Publication instants predate
8
+ * the deterministic test clock by more than the default minimum release age,
9
+ * so every written version is immediately eligible; rewriting an extension
10
+ * with a longer version list models a later publication.
11
+ *
12
+ * @experimental This API is unstable and may change without notice.
13
+ */
14
+ import { createHash } from "node:crypto";
15
+ import * as fs from "node:fs";
16
+ import * as os from "node:os";
17
+ import * as nodePath from "node:path";
18
+ import { strToU8, zipSync } from "fflate";
19
+ const PUBLISHED_AT = "1960-01-01T00:00:00Z";
20
+ const OWNER = "@acme";
21
+ // ZIP stores a local-time DOS timestamp and admits only 1980-2099. Building the
22
+ // fixed instant from local components keeps the encoded fields identical in
23
+ // every timezone; an instant fixed in UTC falls into 1979 west of Greenwich.
24
+ // eslint-disable-next-line no-restricted-syntax -- ZIP's driver API requires Date; this fixed value never reads the ambient clock.
25
+ const ARCHIVE_MTIME = new Date(1980, 0, 2, 0, 0, 0, 0);
26
+ const versionParts = (version) => (version.split("-")[0] ?? version).split(".").map((part) => Number.parseInt(part, 10));
27
+ /**
28
+ * The Registry index lists versions newest-first and the resolvers rely on
29
+ * that order; callers may pass versions in any order.
30
+ */
31
+ const newestFirst = (entries) => [...entries].sort((left, right) => {
32
+ const a = versionParts(left.version);
33
+ const b = versionParts(right.version);
34
+ for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
35
+ const difference = (b[index] ?? 0) - (a[index] ?? 0);
36
+ if (difference !== 0)
37
+ return difference;
38
+ }
39
+ return 0;
40
+ });
41
+ /** Publish into a throwaway `file://` Registry. */
42
+ export const makeLifecycleRegistry = () => {
43
+ const root = fs.realpathSync(fs.mkdtempSync(nodePath.join(os.tmpdir(), "axm-lifecycle-registry-")));
44
+ const writeArchive = (directory, version, entries) => {
45
+ fs.mkdirSync(directory, { recursive: true });
46
+ const archive = zipSync(Object.fromEntries(Object.entries(entries).map(([relative, content]) => [relative, strToU8(content)])), { mtime: ARCHIVE_MTIME });
47
+ fs.writeFileSync(nodePath.join(directory, `${version}.zip`), archive);
48
+ return archive;
49
+ };
50
+ const writeIndex = (directory, index) => {
51
+ fs.mkdirSync(directory, { recursive: true });
52
+ fs.writeFileSync(nodePath.join(directory, "index.json"), `${JSON.stringify(index, null, 2)}\n`);
53
+ };
54
+ const integrity = (archive) => `sha512-${createHash("sha512").update(archive).digest("base64")}`;
55
+ const extensionDirectory = (plural, name) => nodePath.join(root, "extensions", OWNER, plural, name);
56
+ const publish = (args) => {
57
+ const directory = extensionDirectory(args.plural, args.name);
58
+ const entries = args.versions.map((entry) => ({
59
+ version: entry.version,
60
+ published: entry.published ?? PUBLISHED_AT,
61
+ integrity: integrity(writeArchive(directory, entry.version, args.archive(entry))),
62
+ ...(args.extra === undefined ? {} : args.extra(entry)),
63
+ }));
64
+ writeIndex(directory, {
65
+ owner: OWNER,
66
+ type: args.type,
67
+ name: args.name,
68
+ publisherBindingId: "hbnd_test",
69
+ deprecation: null,
70
+ versions: newestFirst(entries),
71
+ });
72
+ };
73
+ const manifest = (contents) => `${JSON.stringify(contents, null, 2)}\n`;
74
+ return {
75
+ root,
76
+ source: { name: "agentxm", type: "registry", location: `file://${root}` },
77
+ writeSkill: (name, versions) => publish({
78
+ plural: "skills",
79
+ type: "skill",
80
+ name,
81
+ versions,
82
+ archive: ({ version, body }) => ({
83
+ "skill.json": manifest({
84
+ owner: OWNER,
85
+ type: "skill",
86
+ name,
87
+ version,
88
+ description: `The ${name} skill.`,
89
+ }),
90
+ "src/SKILL.md": `---\nname: "${name}"\ndescription: "The ${name} skill."\n---\n\n# ${name}\n\n${body}\n`,
91
+ }),
92
+ }),
93
+ writeSubagent: (name, versions) => publish({
94
+ plural: "subagents",
95
+ type: "subagent",
96
+ name,
97
+ versions,
98
+ archive: ({ version, body }) => ({
99
+ "subagent.json": manifest({
100
+ owner: OWNER,
101
+ type: "subagent",
102
+ name,
103
+ version,
104
+ description: `The ${name} subagent.`,
105
+ }),
106
+ [`src/${name}.md`]: `---\nname: ${name}\ndescription: The ${name} subagent.\n---\n\n# ${name}\n\n${body}\n`,
107
+ }),
108
+ }),
109
+ writeRule: (name, versions) => publish({
110
+ plural: "rules",
111
+ type: "rule",
112
+ name,
113
+ versions,
114
+ archive: ({ version, body }) => ({
115
+ "rule.json": manifest({
116
+ owner: OWNER,
117
+ type: "rule",
118
+ name,
119
+ version,
120
+ description: `The ${name} rule.`,
121
+ }),
122
+ "src/RULE.md": `Guidance for ${name}: ${body}\n`,
123
+ }),
124
+ }),
125
+ writeHook: (name, versions) => publish({
126
+ plural: "hooks",
127
+ type: "hook",
128
+ name,
129
+ versions,
130
+ archive: ({ version }) => ({
131
+ "hook.json": manifest({
132
+ owner: OWNER,
133
+ type: "hook",
134
+ name,
135
+ version,
136
+ description: `The ${name} hook.`,
137
+ runtime: "bash",
138
+ entrypoint: "src/hook.sh",
139
+ bindings: [{ on: "tool.pre", match: { tools: ["file.write"] } }],
140
+ }),
141
+ "src/hook.sh": `#!/usr/bin/env bash\necho "${name}"\n`,
142
+ }),
143
+ }),
144
+ writeMcp: (name, versions) => publish({
145
+ plural: "mcps",
146
+ type: "mcp-server",
147
+ name,
148
+ versions,
149
+ archive: ({ version, secretInput, files }) => ({
150
+ "mcp.json": manifest({
151
+ owner: OWNER,
152
+ type: "mcp-server",
153
+ name,
154
+ version,
155
+ server: {
156
+ name: `ai.agentxm.spec/${name}`,
157
+ description: `The ${name} MCP server.`,
158
+ version,
159
+ packages: [
160
+ {
161
+ registryType: "npm",
162
+ identifier: `@acme/${name}`,
163
+ version,
164
+ transport: { type: "stdio" },
165
+ ...(secretInput === undefined
166
+ ? {}
167
+ : {
168
+ environmentVariables: [
169
+ { name: secretInput, isRequired: true, isSecret: true },
170
+ ],
171
+ }),
172
+ },
173
+ ],
174
+ },
175
+ }),
176
+ ...files,
177
+ }),
178
+ }),
179
+ writePack: (name, versions) => publish({
180
+ plural: "packs",
181
+ type: "pack",
182
+ name,
183
+ versions,
184
+ archive: ({ version, dependencies, files }) => ({
185
+ "pack.json": manifest({
186
+ owner: OWNER,
187
+ type: "pack",
188
+ name,
189
+ version,
190
+ description: `The ${name} pack.`,
191
+ dependencies,
192
+ }),
193
+ ...files,
194
+ }),
195
+ // The index carries the member constraints the resolver reads; the
196
+ // archive manifest must agree with it for the accepted identity to
197
+ // match the realized package.
198
+ extra: ({ dependencies }) => ({ dependencies }),
199
+ }),
200
+ writeKnowledge: (name, versions) => publish({
201
+ plural: "knowledge",
202
+ type: "knowledge",
203
+ name,
204
+ versions,
205
+ archive: ({ version, body }) => ({
206
+ "knowledge.json": manifest({
207
+ owner: OWNER,
208
+ type: "knowledge",
209
+ name,
210
+ version,
211
+ description: `The ${name} knowledge bundle.`,
212
+ format: { name: "okf", version: "0.2" },
213
+ bundleRoot: "src",
214
+ }),
215
+ "src/index.md": `---\nokf_version: "0.2"\ndescription: "The ${name} knowledge bundle."\n---\n\n# ${name}\n\n${body}\n`,
216
+ }),
217
+ }),
218
+ cleanup: () => {
219
+ fs.rmSync(root, { recursive: true, force: true });
220
+ },
221
+ };
222
+ };
223
+ //# sourceMappingURL=test-registry.js.map
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @agentxm/extension-lifecycle deterministic fixtures and ports.
3
+ *
4
+ * A throwaway workspace with settings, lockfile, and authored extension
5
+ * content written as the product writes them, plus every service a lifecycle
6
+ * use case reads or writes through: the workspace state and its transaction
7
+ * scope, the projection capability, all seven materialization managers, and
8
+ * the plan pipeline's per-invocation services with a recording interaction
9
+ * port. Production source never imports this module.
10
+ *
11
+ * @experimental This API is unstable and may change without notice.
12
+ */
13
+ import * as Effect from "effect/Effect";
14
+ import * as Layer from "effect/Layer";
15
+ import * as HttpClient from "effect/unstable/http/HttpClient";
16
+ import { AgentExecutableResolver } from "@agentxm/agent-integration";
17
+ import type { WorkspaceScope } from "@agentxm/extension-model/unstable/workspace-scope";
18
+ import { ReleaseAgePosture } from "@agentxm/extension-resolution";
19
+ import { SourceHostProviders } from "@agentxm/extension-sources";
20
+ import { type ResolvePlanInteractionTestState } from "@agentxm/workspace-operations/testing";
21
+ import { ExtensionSelectionInteraction } from "./install/selection-interaction.js";
22
+ import { BundledAxmSkillAsset } from "./skills/install/bundled.js";
23
+ import { StepFailureConversion } from "./step-failure-conversion.js";
24
+ /**
25
+ * The official skill as a running executable carries it. The generator that
26
+ * produces the real asset belongs to the application; a specification only
27
+ * needs a compatible one to observe what the lifecycle does with it.
28
+ */
29
+ export declare const bundledAxmSkillAsset: (options?: {
30
+ readonly version?: string;
31
+ readonly runningCliVersion?: string;
32
+ readonly body?: string;
33
+ /**
34
+ * The compatibility range the asset declares. The default is the bounded,
35
+ * wildcard-free form the install validates against; an unbounded range is
36
+ * what an incompatible-asset example supplies deliberately.
37
+ */
38
+ readonly cliVersionRange?: string;
39
+ }) => Layer.Layer<BundledAxmSkillAsset>;
40
+ export interface LifecycleFixtureOptions {
41
+ readonly scope?: WorkspaceScope;
42
+ /** Settings document for the selected scope; written as authored. */
43
+ readonly settings?: Readonly<Record<string, unknown>>;
44
+ /** Lockfile document for the selected scope; `lockfileVersion` is supplied. */
45
+ readonly lockfile?: Readonly<Record<string, unknown>>;
46
+ /** Extra files written into the project root, keyed by relative path. */
47
+ readonly files?: Readonly<Record<string, string>>;
48
+ /** Agent executables the machine reports as present. */
49
+ readonly installedExecutables?: ReadonlyArray<string>;
50
+ /**
51
+ * How the fixture resolves sources. `none` (the default) refuses every
52
+ * lookup, which is what an activation fixture wants: turning something on
53
+ * never fetches. `live` composes the production source-resolution layer, so
54
+ * an install fixture acquires from a real local package or a `file://`
55
+ * Registry the specification published into.
56
+ */
57
+ readonly sources?: "none" | "live";
58
+ /** The CLI version the official-skill compatibility policy evaluates against. */
59
+ readonly cliVersion?: string;
60
+ /**
61
+ * How the fixture answers "which of these did you want?". The default takes
62
+ * every candidate and records the question, so a specification can show
63
+ * that a choice was — or was not — asked for.
64
+ */
65
+ readonly select?: "all" | "none";
66
+ /**
67
+ * Whether a confirmable risk condition can be approved at a prompt, and
68
+ * what happens when one opens. The default has no terminal, so a
69
+ * confirmable condition blocks naming interactive approval instead of
70
+ * prompting. `onConfirm` runs while the answer is still pending, which is
71
+ * where a specification observes that nothing was written before it.
72
+ */
73
+ readonly confirmation?: {
74
+ readonly available?: boolean;
75
+ readonly answer?: "approved" | "declined" | "cancelled";
76
+ readonly onConfirm?: () => void;
77
+ };
78
+ }
79
+ /**
80
+ * A throwaway workspace with every service a lifecycle use case needs over
81
+ * it, and a pinned user home so a project-scope run can be shown to leave the
82
+ * user scope alone.
83
+ */
84
+ export declare const makeLifecycleFixture: (options?: LifecycleFixtureOptions) => {
85
+ root: string;
86
+ home: string;
87
+ workspaceRoot: string;
88
+ writeFile: (relativePath: string, contents: string) => void;
89
+ readFile: (relativePath: string) => string;
90
+ exists: (relativePath: string) => boolean;
91
+ /** Every file under the project root. */
92
+ snapshot: () => readonly (readonly [string, string])[];
93
+ /** Every file under the pinned user home. */
94
+ homeSnapshot: () => readonly (readonly [string, string])[];
95
+ /** What the plan interaction port was asked to present and confirm. */
96
+ interactionState: () => ResolvePlanInteractionTestState;
97
+ /** Every time the fixture was asked which extensions to install. */
98
+ selectionCalls: () => ReadonlyArray<{
99
+ readonly type: "skill" | "subagent";
100
+ readonly offered: ReadonlyArray<string>;
101
+ }>;
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 | import("@agentxm/workspace-state").ConfiguredAgentOutcomesProvider | import("@agentxm/extension-materialization").ExtensionManagers | import("@agentxm/workspace-transactions").FootprintRecorder | HttpClient.HttpClient | 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/extension-materialization").HookManager | import("@agentxm/extension-materialization").KnowledgeManager | import("@agentxm/extension-materialization").RuleManager | import("@agentxm/extension-materialization").SkillManager | import("@agentxm/extension-materialization").McpServerManager | import("@agentxm/extension-materialization").PackManager | ExtensionSelectionInteraction | BundledAxmSkillAsset | AgentExecutableResolver | import("@agentxm/workspace-state/live").WorkspaceStateServices | import("@agentxm/workspace-projection").ProjectionParticipants | import("@agentxm/workspace-projection").WorkspaceInvariantFacts>>;
103
+ cleanup: () => void;
104
+ };
105
+ export type LifecycleFixture = ReturnType<typeof makeLifecycleFixture>;
106
+ export { writeAgentSkillDirectory, writeLocalHookPackage, writeLocalKnowledgePackage, writeLocalRulePackage, writeLocalSkillPackage, writeLocalSubagentPackage, type LocalPackageFixture, } from "./test-packages.js";
107
+ export { makeLifecycleRegistry, type LifecycleRegistry, type RegistryHookVersion, type RegistryKnowledgeVersion, type RegistryMcpVersion, type RegistryPackVersion, type RegistryRuleVersion, type RegistrySkillVersion, type RegistrySubagentVersion, } from "./test-registry.js";
108
+ //# sourceMappingURL=testing.d.ts.map
@@ -0,0 +1,241 @@
1
+ /**
2
+ * @agentxm/extension-lifecycle deterministic fixtures and ports.
3
+ *
4
+ * A throwaway workspace with settings, lockfile, and authored extension
5
+ * content written as the product writes them, plus every service a lifecycle
6
+ * use case reads or writes through: the workspace state and its transaction
7
+ * scope, the projection capability, all seven materialization managers, and
8
+ * the plan pipeline's per-invocation services with a recording interaction
9
+ * port. Production source never imports this module.
10
+ *
11
+ * @experimental This API is unstable and may change without notice.
12
+ */
13
+ import * as fs from "node:fs";
14
+ import * as os from "node:os";
15
+ import * as nodePath from "node:path";
16
+ import * as ConfigProvider from "effect/ConfigProvider";
17
+ import * as Effect from "effect/Effect";
18
+ import * as Layer from "effect/Layer";
19
+ import * as Option from "effect/Option";
20
+ import * as HttpClient from "effect/unstable/http/HttpClient";
21
+ import { AgentExecutableResolver } from "@agentxm/agent-integration";
22
+ import { decodeAbsolutePathSync } from "@agentxm/extension-model/unstable/path-types";
23
+ import { ExtensionManagersLive, HookManagerLive, KnowledgeManagerLive, McpSecretStoreLive, McpServerManagerLive, PackManagerLive, ProjectionParticipantsLive, RuleManagerLive, SkillManagerLive, SubagentManagerLive, } from "@agentxm/extension-materialization/live";
24
+ import { ReleaseAgePosture, decideNamedRegistryVersion, makeAxmSkillCompatibilityPolicyLayer, namedRegistryCandidates, resolveVersionEntryWithReleaseAge, } from "@agentxm/extension-resolution";
25
+ import { AxmSkillCandidateGateLive } from "@agentxm/extension-resolution/live";
26
+ import { RegistryResolutionPolicy, SourceHostProviders } from "@agentxm/extension-sources";
27
+ import { SourceHostProvidersLive } from "@agentxm/extension-sources/live";
28
+ import { StepFailure } from "@agentxm/workspace-operations";
29
+ import { PlanInvocationTest, ResolvePlanInteractionTest, } from "@agentxm/workspace-operations/testing";
30
+ import { CodingAgentRepositoryLive, NativeWriteAuthorityLive, WorkspaceCatalogLive, WorkspaceInvariantFactsLive, } from "@agentxm/workspace-projection/live";
31
+ import { layer as WorkspaceLayerLive } from "@agentxm/workspace-state/live";
32
+ import { ExtensionLifecycleFailed } from "./errors.js";
33
+ import { ExtensionSelectionInteraction } from "./install/selection-interaction.js";
34
+ import { BundledAxmSkillAsset } from "./skills/install/bundled.js";
35
+ import { StepFailureConversion } from "./step-failure-conversion.js";
36
+ /**
37
+ * The official skill as a running executable carries it. The generator that
38
+ * produces the real asset belongs to the application; a specification only
39
+ * needs a compatible one to observe what the lifecycle does with it.
40
+ */
41
+ export const bundledAxmSkillAsset = (options = {}) => {
42
+ const version = options.version ?? "1.0.0";
43
+ const nextMajor = `${Number(version.split(".")[0] ?? "0") + 1}.0.0`;
44
+ const body = options.body ?? "---\nname: axm\ndescription: The official AXM skill.\n---\n\n# axm\n";
45
+ return Layer.succeed(BundledAxmSkillAsset, {
46
+ manifestJson: `${JSON.stringify({
47
+ owner: "@agentxm",
48
+ type: "skill",
49
+ name: "axm",
50
+ version,
51
+ description: "The official AXM skill.",
52
+ }, null, 2)}\n`,
53
+ version,
54
+ cliVersion: version,
55
+ cliVersionRange: options.cliVersionRange ?? `>=${version} <${nextMajor}`,
56
+ sourceFiles: [{ path: "SKILL.md", base64: Buffer.from(body).toString("base64") }],
57
+ runningCliVersion: options.runningCliVersion ?? version,
58
+ });
59
+ };
60
+ /**
61
+ * Render a failure as the sentence a structural fixture reports. Assertions in
62
+ * this package bind to this mapping, not to the application boundary's
63
+ * wording: a fixture that borrowed the application's renderer would be
64
+ * asserting the application's words from inside the feature.
65
+ */
66
+ const describeFailure = (failure) => {
67
+ if (failure instanceof ExtensionLifecycleFailed)
68
+ return failure.detail ?? failure.category;
69
+ if (typeof failure === "object" && failure !== null) {
70
+ for (const key of ["detail", "subject", "message"]) {
71
+ if (key in failure) {
72
+ const candidate = Reflect.get(failure, key);
73
+ if (typeof candidate === "string" && candidate.length > 0)
74
+ return candidate;
75
+ }
76
+ }
77
+ if ("cause" in failure && failure.cause !== undefined && failure.cause !== failure) {
78
+ return describeFailure(failure.cause);
79
+ }
80
+ }
81
+ return String(failure);
82
+ };
83
+ /**
84
+ * Structural stand-in for the application's failure adapter: the feature's own
85
+ * failure maps 1:1; anything else keeps its detail sentence under an
86
+ * `internal` category.
87
+ */
88
+ const TestStepFailureConversion = Layer.succeed(StepFailureConversion, {
89
+ toStepFailure: (failure) => failure instanceof ExtensionLifecycleFailed
90
+ ? new StepFailure({
91
+ category: failure.category,
92
+ detail: failure.detail ?? failure.category,
93
+ ...(failure.suggestions === undefined ? {} : { suggestions: failure.suggestions }),
94
+ ...(failure.cause === undefined ? {} : { cause: failure.cause }),
95
+ })
96
+ : new StepFailure({ category: "internal", detail: describeFailure(failure), cause: failure }),
97
+ describeFailure,
98
+ describeFailureMessage: describeFailure,
99
+ });
100
+ /**
101
+ * A throwaway workspace with every service a lifecycle use case needs over
102
+ * it, and a pinned user home so a project-scope run can be shown to leave the
103
+ * user scope alone.
104
+ */
105
+ export const makeLifecycleFixture = (options = {}) => {
106
+ const scope = options.scope ?? "project";
107
+ const root = fs.realpathSync(fs.mkdtempSync(nodePath.join(os.tmpdir(), "axm-lifecycle-")));
108
+ const home = fs.realpathSync(fs.mkdtempSync(nodePath.join(os.tmpdir(), "axm-lifecycle-home-")));
109
+ const workspaceRoot = scope === "user" ? nodePath.join(home, ".axm", "workspace") : root;
110
+ fs.mkdirSync(nodePath.join(root, ".axm"), { recursive: true });
111
+ fs.mkdirSync(nodePath.join(home, ".axm", "workspace"), { recursive: true });
112
+ const writeFile = (relativePath, contents) => {
113
+ const file = nodePath.join(root, relativePath);
114
+ fs.mkdirSync(nodePath.dirname(file), { recursive: true });
115
+ fs.writeFileSync(file, contents);
116
+ };
117
+ const readFile = (relativePath) => fs.readFileSync(nodePath.join(root, relativePath), "utf8");
118
+ const exists = (relativePath) => fs.existsSync(nodePath.join(root, relativePath));
119
+ /** Every file, symlink, and directory under a root, so purity can be proven. */
120
+ const snapshotUnder = (base) => {
121
+ const entries = [];
122
+ const walk = (directory) => {
123
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
124
+ const absolute = nodePath.join(directory, entry.name);
125
+ const relative = nodePath.relative(base, absolute);
126
+ if (entry.isSymbolicLink()) {
127
+ entries.push([relative, `symlink:${fs.readlinkSync(absolute)}`]);
128
+ continue;
129
+ }
130
+ if (entry.isDirectory()) {
131
+ entries.push([relative, "directory"]);
132
+ walk(absolute);
133
+ continue;
134
+ }
135
+ entries.push([relative, fs.readFileSync(absolute, "utf8")]);
136
+ }
137
+ };
138
+ walk(base);
139
+ return entries.sort((left, right) => left[0].localeCompare(right[0]));
140
+ };
141
+ if (options.settings !== undefined) {
142
+ fs.writeFileSync(nodePath.join(workspaceRoot, "axm.json"), JSON.stringify({ agents: [], ...options.settings }, null, 2));
143
+ // JSON is valid YAML, so the lockfile fixture needs no emitter.
144
+ fs.writeFileSync(nodePath.join(workspaceRoot, "axm-lock.yaml"), JSON.stringify({ lockfileVersion: 7, skills: {}, ...options.lockfile }));
145
+ }
146
+ for (const [relativePath, contents] of Object.entries(options.files ?? {})) {
147
+ writeFile(relativePath, contents);
148
+ }
149
+ const confirmation = options.confirmation;
150
+ const interaction = ResolvePlanInteractionTest({
151
+ isConfirmationAvailable: confirmation?.available ?? false,
152
+ confirmApplyChanges: () => Effect.sync(() => {
153
+ confirmation?.onConfirm?.();
154
+ return confirmation?.answer ?? "approved";
155
+ }),
156
+ });
157
+ const selectionCalls = [];
158
+ const selection = Layer.succeed(ExtensionSelectionInteraction, {
159
+ selectSkills: (candidates) => {
160
+ selectionCalls.push({ type: "skill", offered: candidates.map((ref) => ref.skill.name) });
161
+ return Effect.succeed(options.select === "none" ? [] : candidates);
162
+ },
163
+ selectSubagents: (candidates) => {
164
+ selectionCalls.push({
165
+ type: "subagent",
166
+ offered: candidates.map((ref) => ref.subagent.name),
167
+ });
168
+ return Effect.succeed(options.select === "none" ? [] : candidates);
169
+ },
170
+ });
171
+ const environment = ConfigProvider.layer(ConfigProvider.fromEnv({ env: { AXM_USER_HOME: home } }));
172
+ // Activation never fetches a source: it turns on what the workspace already
173
+ // acquired. A fixture that could fetch would be describing installation.
174
+ const refusingSourceProviders = Layer.succeed(SourceHostProviders, {
175
+ find: () => Effect.succeed([]),
176
+ resolveNamedRegistry: () => Effect.die("no named registry in this fixture"),
177
+ fetch: () => Effect.die("no source fetch in this fixture"),
178
+ cloneUrl: () => Option.none(),
179
+ origin: () => "fixture",
180
+ });
181
+ const transport = Layer.succeed(HttpClient.HttpClient, HttpClient.make(() => Effect.die("no HTTP request in this fixture")));
182
+ const installedExecutables = new Set(options.installedExecutables ?? []);
183
+ const executables = Layer.succeed(AgentExecutableResolver, {
184
+ exists: (name) => Effect.succeed(installedExecutables.has(name)),
185
+ });
186
+ // One environment, built outward: the workspace state and the ports over it,
187
+ // then the projection services that read them, then the managers that read
188
+ // both, then the registry that indexes the managers. `provideMerge` keeps
189
+ // every layer's output, so a service a manager keeps in `R` is still there.
190
+ const base = Layer.provideMerge(Layer.mergeAll(WorkspaceLayerLive({
191
+ scope,
192
+ projectRoot: decodeAbsolutePathSync(root),
193
+ allowUninitialized: options.settings === undefined,
194
+ }), CodingAgentRepositoryLive, NativeWriteAuthorityLive, transport, interaction.layer, selection, executables, TestStepFailureConversion, Layer.succeed(ReleaseAgePosture, "enforce"), PlanInvocationTest,
195
+ // Every AXM executable carries the official skill, so the world a
196
+ // lifecycle specification runs in carries one too. Only a request whose
197
+ // subject is `bundled` ever reads it.
198
+ bundledAxmSkillAsset()), environment);
199
+ const withProjection = Layer.provideMerge(WorkspaceCatalogLive, base);
200
+ // Installing resolves real sources, so the install fixtures compose the
201
+ // production resolution layer over the same workspace catalog.
202
+ const liveSourceProviders = Layer.provide(SourceHostProvidersLive, Layer.mergeAll(withProjection, Layer.provide(AxmSkillCandidateGateLive, makeAxmSkillCompatibilityPolicyLayer(options.cliVersion ?? "0.0.0-fixture")), Layer.succeed(RegistryResolutionPolicy, {
203
+ selectVersion: resolveVersionEntryWithReleaseAge,
204
+ decideNamedVersion: decideNamedRegistryVersion,
205
+ namedCandidates: namedRegistryCandidates,
206
+ })));
207
+ const resolvedSources = (options.sources ?? "none") === "live" ? liveSourceProviders : refusingSourceProviders;
208
+ const withSources = Layer.provideMerge(resolvedSources, withProjection);
209
+ const leafManagers = Layer.provideMerge(Layer.mergeAll(RuleManagerLive, HookManagerLive, KnowledgeManagerLive, SkillManagerLive, SubagentManagerLive, McpSecretStoreLive), withSources);
210
+ const withMcp = Layer.provideMerge(McpServerManagerLive, leafManagers);
211
+ const withPack = Layer.provideMerge(PackManagerLive, withMcp);
212
+ // The participant registry indexes the managers, and the invariant facts
213
+ // read the registry, so both come after every manager is available.
214
+ const withParticipants = Layer.provideMerge(ProjectionParticipantsLive, withPack);
215
+ const services = Layer.provideMerge(Layer.mergeAll(ExtensionManagersLive, WorkspaceInvariantFactsLive), withParticipants);
216
+ return {
217
+ root,
218
+ home,
219
+ workspaceRoot,
220
+ writeFile,
221
+ readFile,
222
+ exists,
223
+ /** Every file under the project root. */
224
+ snapshot: () => snapshotUnder(root),
225
+ /** Every file under the pinned user home. */
226
+ homeSnapshot: () => snapshotUnder(home),
227
+ /** What the plan interaction port was asked to present and confirm. */
228
+ interactionState: () => interaction.state,
229
+ /** Every time the fixture was asked which extensions to install. */
230
+ selectionCalls: () => selectionCalls,
231
+ provide: (effect) => effect.pipe(Effect.provide(services)),
232
+ cleanup: () => {
233
+ fs.rmSync(root, { recursive: true, force: true });
234
+ fs.rmSync(home, { recursive: true, force: true });
235
+ },
236
+ };
237
+ };
238
+ // Local package and Registry fixtures an install specification publishes into.
239
+ export { writeAgentSkillDirectory, writeLocalHookPackage, writeLocalKnowledgePackage, writeLocalRulePackage, writeLocalSkillPackage, writeLocalSubagentPackage, } from "./test-packages.js";
240
+ export { makeLifecycleRegistry, } from "./test-registry.js";
241
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Whether a removal may delete the package it withdraws.
3
+ *
4
+ * An extension declared directly and also required by an installed pack keeps
5
+ * its package when the direct declaration goes: the pack still depends on it.
6
+ * The membership fact is workspace state's; deciding that it means "retain"
7
+ * is this feature's.
8
+ *
9
+ * @experimental This API is unstable and may change without notice.
10
+ */
11
+ import type { UninstallRetentionPolicy } from "@agentxm/extension-materialization";
12
+ import type { WorkspaceMutationsService } from "@agentxm/workspace-state";
13
+ import type { ExtensionLifecycleFailed } from "../errors.js";
14
+ /** Retain a package an installed pack still requires. */
15
+ export declare const makeWorkspaceRetentionPolicy: (ws: WorkspaceMutationsService) => UninstallRetentionPolicy<ExtensionLifecycleFailed>;
16
+ /**
17
+ * A pack's own exclusive members carry no separate retention question: the
18
+ * pack that required them is being removed in the same transition.
19
+ */
20
+ export declare const exclusiveMemberRetentionPolicy: UninstallRetentionPolicy<ExtensionLifecycleFailed>;
21
+ //# sourceMappingURL=retention-policy.d.ts.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Whether a removal may delete the package it withdraws.
3
+ *
4
+ * An extension declared directly and also required by an installed pack keeps
5
+ * its package when the direct declaration goes: the pack still depends on it.
6
+ * The membership fact is workspace state's; deciding that it means "retain"
7
+ * is this feature's.
8
+ *
9
+ * @experimental This API is unstable and may change without notice.
10
+ */
11
+ import * as Effect from "effect/Effect";
12
+ import { installRefused } from "../install/vocabulary.js";
13
+ /** Retain a package an installed pack still requires. */
14
+ export const makeWorkspaceRetentionPolicy = (ws) => ({
15
+ isRequiredByInstalledPack: (args) => ws.isExtensionRequiredByInstalledPack(args.target).pipe(Effect.mapError((cause) => installRefused({
16
+ category: "internal",
17
+ detail: `Pack membership for ${args.target.type} "${args.target.name}" could not be read`,
18
+ cause,
19
+ }))),
20
+ });
21
+ /**
22
+ * A pack's own exclusive members carry no separate retention question: the
23
+ * pack that required them is being removed in the same transition.
24
+ */
25
+ export const exclusiveMemberRetentionPolicy = {
26
+ isRequiredByInstalledPack: () => Effect.succeed(false),
27
+ };
28
+ //# sourceMappingURL=retention-policy.js.map
@@ -0,0 +1,17 @@
1
+ import * as Effect from "effect/Effect";
2
+ import type { ExtensionLifecycleFailed } from "../errors.js";
3
+ import { type InstallableExtensionType } from "@agentxm/extension-model/unstable/extensions/installable-types";
4
+ export declare const rootUninstallableTypeSegments: readonly ("knowledge" | "rules" | "hooks" | "mcps" | "skills" | "subagents" | "packs")[];
5
+ export type RootUninstallableType = InstallableExtensionType;
6
+ export interface RootUninstallIntent {
7
+ readonly source: string;
8
+ readonly type: RootUninstallableType;
9
+ readonly name: string;
10
+ }
11
+ /**
12
+ * Root uninstall accepts only a registry FQN: it is the one form that names
13
+ * the extension type without guessing, and every other spelling has a
14
+ * per-type command that already knows the type.
15
+ */
16
+ export declare const resolveRootUninstallIntent: (input: string) => Effect.Effect<RootUninstallIntent, ExtensionLifecycleFailed>;
17
+ //# sourceMappingURL=root-intent.d.ts.map