@agentxm/registry-protocol 0.28.4-bootstrap.0

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 (137) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +13 -0
  3. package/dist/src/unstable/content/agent-skills-standard.d.ts +17 -0
  4. package/dist/src/unstable/content/agent-skills-standard.js +24 -0
  5. package/dist/src/unstable/content/frontmatter.d.ts +52 -0
  6. package/dist/src/unstable/content/frontmatter.js +115 -0
  7. package/dist/src/unstable/content/index.d.ts +11 -0
  8. package/dist/src/unstable/content/index.js +11 -0
  9. package/dist/src/unstable/content/skill-content.d.ts +43 -0
  10. package/dist/src/unstable/content/skill-content.js +184 -0
  11. package/dist/src/unstable/content/skill-types.d.ts +22 -0
  12. package/dist/src/unstable/content/skill-types.js +9 -0
  13. package/dist/src/unstable/content/subagent-content.d.ts +58 -0
  14. package/dist/src/unstable/content/subagent-content.js +101 -0
  15. package/dist/src/unstable/knowledge/index.d.ts +8 -0
  16. package/dist/src/unstable/knowledge/index.js +8 -0
  17. package/dist/src/unstable/knowledge/knowledge-search.d.ts +32 -0
  18. package/dist/src/unstable/knowledge/knowledge-search.js +147 -0
  19. package/dist/src/unstable/knowledge/okf.d.ts +86 -0
  20. package/dist/src/unstable/knowledge/okf.js +1090 -0
  21. package/dist/src/unstable/lint/catalog/hook/decision-portability.d.ts +4 -0
  22. package/dist/src/unstable/lint/catalog/hook/decision-portability.js +40 -0
  23. package/dist/src/unstable/lint/catalog/hook/entrypoint-exists.d.ts +4 -0
  24. package/dist/src/unstable/lint/catalog/hook/entrypoint-exists.js +39 -0
  25. package/dist/src/unstable/lint/catalog/hook/envelope.d.ts +6 -0
  26. package/dist/src/unstable/lint/catalog/hook/envelope.js +15 -0
  27. package/dist/src/unstable/lint/catalog/hook/matcher-raw-portability.d.ts +4 -0
  28. package/dist/src/unstable/lint/catalog/hook/matcher-raw-portability.js +51 -0
  29. package/dist/src/unstable/lint/catalog/hook.d.ts +4 -0
  30. package/dist/src/unstable/lint/catalog/hook.js +17 -0
  31. package/dist/src/unstable/lint/catalog/knowledge/diagnostics.d.ts +128 -0
  32. package/dist/src/unstable/lint/catalog/knowledge/diagnostics.js +78 -0
  33. package/dist/src/unstable/lint/catalog/knowledge/envelope.d.ts +6 -0
  34. package/dist/src/unstable/lint/catalog/knowledge/envelope.js +15 -0
  35. package/dist/src/unstable/lint/catalog/knowledge.d.ts +5 -0
  36. package/dist/src/unstable/lint/catalog/knowledge.js +11 -0
  37. package/dist/src/unstable/lint/catalog/mcp-server/envelope.d.ts +6 -0
  38. package/dist/src/unstable/lint/catalog/mcp-server/envelope.js +15 -0
  39. package/dist/src/unstable/lint/catalog/mcp-server.d.ts +4 -0
  40. package/dist/src/unstable/lint/catalog/mcp-server.js +6 -0
  41. package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.d.ts +28 -0
  42. package/dist/src/unstable/lint/catalog/pack/manifest-keys-recognized.js +38 -0
  43. package/dist/src/unstable/lint/catalog/pack/manifest-present.d.ts +17 -0
  44. package/dist/src/unstable/lint/catalog/pack/manifest-present.js +24 -0
  45. package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.d.ts +25 -0
  46. package/dist/src/unstable/lint/catalog/pack/manifest-schema-valid.js +33 -0
  47. package/dist/src/unstable/lint/catalog/pack-accessor/contexts.d.ts +51 -0
  48. package/dist/src/unstable/lint/catalog/pack-accessor/contexts.js +45 -0
  49. package/dist/src/unstable/lint/catalog/pack-accessor/vft.d.ts +60 -0
  50. package/dist/src/unstable/lint/catalog/pack-accessor/vft.js +85 -0
  51. package/dist/src/unstable/lint/catalog/pack.d.ts +37 -0
  52. package/dist/src/unstable/lint/catalog/pack.js +47 -0
  53. package/dist/src/unstable/lint/catalog/rule/envelope.d.ts +6 -0
  54. package/dist/src/unstable/lint/catalog/rule/envelope.js +15 -0
  55. package/dist/src/unstable/lint/catalog/rule.d.ts +4 -0
  56. package/dist/src/unstable/lint/catalog/rule.js +6 -0
  57. package/dist/src/unstable/lint/catalog/shared/envelope-rules.d.ts +93 -0
  58. package/dist/src/unstable/lint/catalog/shared/envelope-rules.js +89 -0
  59. package/dist/src/unstable/lint/catalog/shared/manifest-json.d.ts +18 -0
  60. package/dist/src/unstable/lint/catalog/shared/manifest-json.js +28 -0
  61. package/dist/src/unstable/lint/catalog/shared/manifest-present.d.ts +25 -0
  62. package/dist/src/unstable/lint/catalog/shared/manifest-present.js +37 -0
  63. package/dist/src/unstable/lint/catalog/shared/recommended-packs-rules.d.ts +70 -0
  64. package/dist/src/unstable/lint/catalog/shared/recommended-packs-rules.js +139 -0
  65. package/dist/src/unstable/lint/catalog/shared/schema-rule.d.ts +58 -0
  66. package/dist/src/unstable/lint/catalog/shared/schema-rule.js +103 -0
  67. package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.d.ts +31 -0
  68. package/dist/src/unstable/lint/catalog/skill/frontmatter-parseable.js +148 -0
  69. package/dist/src/unstable/lint/catalog/skill/frontmatter-standard-valid.d.ts +13 -0
  70. package/dist/src/unstable/lint/catalog/skill/frontmatter-standard-valid.js +61 -0
  71. package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.d.ts +26 -0
  72. package/dist/src/unstable/lint/catalog/skill/manifest-keys-recognized.js +41 -0
  73. package/dist/src/unstable/lint/catalog/skill/manifest-present.d.ts +23 -0
  74. package/dist/src/unstable/lint/catalog/skill/manifest-present.js +32 -0
  75. package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.d.ts +21 -0
  76. package/dist/src/unstable/lint/catalog/skill/manifest-schema-valid.js +36 -0
  77. package/dist/src/unstable/lint/catalog/skill/recommended-packs-valid.d.ts +8 -0
  78. package/dist/src/unstable/lint/catalog/skill/recommended-packs-valid.js +12 -0
  79. package/dist/src/unstable/lint/catalog/skill/skill-md-present.d.ts +17 -0
  80. package/dist/src/unstable/lint/catalog/skill/skill-md-present.js +37 -0
  81. package/dist/src/unstable/lint/catalog/skill/standalone-declaration-valid.d.ts +8 -0
  82. package/dist/src/unstable/lint/catalog/skill/standalone-declaration-valid.js +12 -0
  83. package/dist/src/unstable/lint/catalog/skill-accessor/contexts.d.ts +61 -0
  84. package/dist/src/unstable/lint/catalog/skill-accessor/contexts.js +55 -0
  85. package/dist/src/unstable/lint/catalog/skill-accessor/vft.d.ts +77 -0
  86. package/dist/src/unstable/lint/catalog/skill-accessor/vft.js +135 -0
  87. package/dist/src/unstable/lint/catalog/skill.d.ts +44 -0
  88. package/dist/src/unstable/lint/catalog/skill.js +64 -0
  89. package/dist/src/unstable/lint/catalog/subagent/envelope.d.ts +6 -0
  90. package/dist/src/unstable/lint/catalog/subagent/envelope.js +15 -0
  91. package/dist/src/unstable/lint/catalog/subagent.d.ts +4 -0
  92. package/dist/src/unstable/lint/catalog/subagent.js +6 -0
  93. package/dist/src/unstable/lint/compose-path.d.ts +28 -0
  94. package/dist/src/unstable/lint/compose-path.js +64 -0
  95. package/dist/src/unstable/lint/config.d.ts +103 -0
  96. package/dist/src/unstable/lint/config.js +140 -0
  97. package/dist/src/unstable/lint/context.d.ts +246 -0
  98. package/dist/src/unstable/lint/context.js +21 -0
  99. package/dist/src/unstable/lint/describe-document.d.ts +31 -0
  100. package/dist/src/unstable/lint/describe-document.js +50 -0
  101. package/dist/src/unstable/lint/evaluate.d.ts +47 -0
  102. package/dist/src/unstable/lint/evaluate.js +82 -0
  103. package/dist/src/unstable/lint/issues-to-findings.d.ts +35 -0
  104. package/dist/src/unstable/lint/issues-to-findings.js +149 -0
  105. package/dist/src/unstable/lint/publish.d.ts +49 -0
  106. package/dist/src/unstable/lint/publish.js +86 -0
  107. package/dist/src/unstable/lint/rule.d.ts +98 -0
  108. package/dist/src/unstable/lint/rule.js +18 -0
  109. package/dist/src/unstable/publish/archive-guardrails.d.ts +39 -0
  110. package/dist/src/unstable/publish/archive-guardrails.js +250 -0
  111. package/dist/src/unstable/publish/filtered-package-validation.d.ts +24 -0
  112. package/dist/src/unstable/publish/filtered-package-validation.js +117 -0
  113. package/dist/src/unstable/publish/index.d.ts +12 -0
  114. package/dist/src/unstable/publish/index.js +12 -0
  115. package/dist/src/unstable/publish/ingest-limits.d.ts +25 -0
  116. package/dist/src/unstable/publish/ingest-limits.js +41 -0
  117. package/dist/src/unstable/publish/input-normalization.d.ts +40 -0
  118. package/dist/src/unstable/publish/input-normalization.js +125 -0
  119. package/dist/src/unstable/publish/manifest-policy.d.ts +611 -0
  120. package/dist/src/unstable/publish/manifest-policy.js +196 -0
  121. package/dist/src/unstable/publish/test-zip-helpers.d.ts +13 -0
  122. package/dist/src/unstable/publish/test-zip-helpers.js +117 -0
  123. package/dist/src/unstable/publish/visibility.d.ts +161 -0
  124. package/dist/src/unstable/publish/visibility.js +124 -0
  125. package/dist/src/unstable/registry/discover-schema.d.ts +68 -0
  126. package/dist/src/unstable/registry/discover-schema.js +35 -0
  127. package/dist/src/unstable/registry/index.d.ts +9 -0
  128. package/dist/src/unstable/registry/index.js +9 -0
  129. package/dist/src/unstable/registry/publication-set.d.ts +303 -0
  130. package/dist/src/unstable/registry/publication-set.js +507 -0
  131. package/dist/src/unstable/registry/release-age-policy.d.ts +57 -0
  132. package/dist/src/unstable/registry/release-age-policy.js +117 -0
  133. package/dist/src/unstable/registry/schema.d.ts +220 -0
  134. package/dist/src/unstable/registry/schema.js +114 -0
  135. package/dist/src/unstable/suggested-action.d.ts +19 -0
  136. package/dist/src/unstable/suggested-action.js +42 -0
  137. package/package.json +214 -0
@@ -0,0 +1,507 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as Schema from "effect/Schema";
3
+ import * as semver from "semver";
4
+ import { SuggestedActionSchema } from "../suggested-action.js";
5
+ import { ExtensionNameSchema, ExtensionTypeSchema, ExtensionVisibilitySchema, } from "@agentxm/extension-model/unstable/extensions/common";
6
+ import { formatFqn } from "@agentxm/extension-model/unstable/extensions/fqn";
7
+ import { HandleSchema } from "@agentxm/extension-model/unstable/extensions/handle";
8
+ import { VisibilityEvaluationSchema, VisibilityEvaluationUnavailableSchema, VisibilityIntentSchema, } from "../publish/visibility.js";
9
+ import { VersionRangeSchema, VersionSchema, } from "@agentxm/extension-model/unstable/version-constraints";
10
+ import { DeprecationViewSchema, } from "@agentxm/extension-model/unstable/extensions/deprecation";
11
+ export const PUBLICATION_SET_CONTRACT = "publication-set-v2";
12
+ export const MAX_PUBLICATION_SET_CANDIDATES = 100;
13
+ export const Sha256HexSchema = Schema.String.check(Schema.isPattern(/^[a-f0-9]{64}$/)).annotate({
14
+ identifier: "Sha256Hex",
15
+ });
16
+ const PublicationTargetSchema = Schema.Struct({
17
+ owner: HandleSchema,
18
+ type: ExtensionTypeSchema,
19
+ name: ExtensionNameSchema,
20
+ version: VersionSchema,
21
+ }).annotate({ identifier: "PublicationTarget" });
22
+ const PackDependencyDescriptorSchema = Schema.Struct({
23
+ owner: HandleSchema,
24
+ type: Schema.Literals(["hook", "knowledge", "mcp-server", "rule", "skill", "subagent"]),
25
+ name: ExtensionNameSchema,
26
+ range: VersionRangeSchema,
27
+ }).annotate({ identifier: "PackDependencyDescriptor" });
28
+ export const PublicationVisibilityInputSchema = Schema.Struct({
29
+ intent: Schema.NullOr(VisibilityIntentSchema),
30
+ request: Schema.NullOr(ExtensionVisibilitySchema),
31
+ }).annotate({ identifier: "PublicationVisibilityInput" });
32
+ const PublicationDescriptorSchema = Schema.Struct({
33
+ target: PublicationTargetSchema,
34
+ participation: Schema.Literals(["publish", "verified-existing"]),
35
+ archiveSha256Hex: Schema.optional(Sha256HexSchema),
36
+ visibility: PublicationVisibilityInputSchema,
37
+ pack: Schema.optional(Schema.Struct({
38
+ dependencies: Schema.Array(PackDependencyDescriptorSchema),
39
+ })),
40
+ }).annotate({ identifier: "PublicationDescriptor" });
41
+ export const PreviewPublicationSetRequestSchema = Schema.Struct({
42
+ contract: Schema.Literal(PUBLICATION_SET_CONTRACT),
43
+ candidates: Schema.Array(PublicationDescriptorSchema),
44
+ }).annotate({ identifier: "PreviewPublicationSetRequest" });
45
+ export const PackDependencyFindingSchema = Schema.Struct({
46
+ kind: Schema.Literal("advisory"),
47
+ ruleId: Schema.Literals([
48
+ "pack/dependency-version-resolvable",
49
+ "pack/dependency-deprecated",
50
+ ]),
51
+ severity: Schema.Literals(["error", "warning"]),
52
+ reason: Schema.Literals([
53
+ "selected-new-private",
54
+ "selected-existing-private",
55
+ "target-unavailable",
56
+ "lifecycle-unavailable",
57
+ "no-installable-version",
58
+ "range-unsatisfied",
59
+ "deprecated",
60
+ ]),
61
+ dependency: PackDependencyDescriptorSchema,
62
+ effectiveVisibility: Schema.optional(Schema.Literals(["public", "private"])),
63
+ lifecycle: Schema.optional(Schema.Literals(["active", "unavailable"])),
64
+ deprecation: Schema.optional(DeprecationViewSchema),
65
+ location: Schema.Struct({ file: Schema.Literal("pack.json") }),
66
+ path: Schema.Literal("./pack.json"),
67
+ message: Schema.String,
68
+ suggestions: Schema.Array(SuggestedActionSchema),
69
+ }).annotate({ identifier: "PackDependencyFinding" });
70
+ const ResolvedPublicationCandidateSchema = Schema.Struct({
71
+ kind: Schema.Literal("resolved"),
72
+ target: PublicationTargetSchema,
73
+ participation: Schema.Literals(["publish", "verified-existing"]),
74
+ descriptorDigest: Sha256HexSchema,
75
+ visibility: VisibilityEvaluationSchema,
76
+ condition: Schema.optional(Schema.String),
77
+ });
78
+ const UnavailablePublicationCandidateSchema = Schema.Struct({
79
+ kind: Schema.Literal("unavailable"),
80
+ target: PublicationTargetSchema,
81
+ participation: Schema.Literals(["publish", "verified-existing"]),
82
+ descriptorDigest: Sha256HexSchema,
83
+ code: Schema.Literal("publish/target-unavailable"),
84
+ visibility: VisibilityEvaluationUnavailableSchema,
85
+ });
86
+ const PublicationPackResultSchema = Schema.Struct({
87
+ target: PublicationTargetSchema,
88
+ status: Schema.Literals(["admitted", "blocked"]),
89
+ findings: Schema.Array(PackDependencyFindingSchema),
90
+ resolutions: Schema.Array(Schema.Struct({
91
+ dependency: PackDependencyDescriptorSchema,
92
+ effectiveVersion: VersionSchema,
93
+ }).annotate({ identifier: "PackDependencyResolution" })),
94
+ });
95
+ export const PreviewPublicationSetResponseSchema = Schema.Struct({
96
+ contract: Schema.Literal(PUBLICATION_SET_CONTRACT),
97
+ publicationSetDigest: Sha256HexSchema,
98
+ status: Schema.Literals(["admitted", "blocked"]),
99
+ candidates: Schema.Array(Schema.Union([ResolvedPublicationCandidateSchema, UnavailablePublicationCandidateSchema])),
100
+ packs: Schema.Array(PublicationPackResultSchema),
101
+ }).annotate({ identifier: "PreviewPublicationSetResponse" });
102
+ const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
103
+ export const comparePublicationTargets = (left, right) => compareText(left.owner, right.owner) ||
104
+ compareText(left.type, right.type) ||
105
+ compareText(left.name, right.name) ||
106
+ compareText(left.version, right.version);
107
+ const compareDependencies = (left, right) => compareText(left.owner, right.owner) ||
108
+ compareText(left.type, right.type) ||
109
+ compareText(left.name, right.name) ||
110
+ compareText(left.range, right.range);
111
+ export const normalizePublicationDescriptor = (descriptor) => ({
112
+ target: descriptor.target,
113
+ participation: descriptor.participation,
114
+ visibility: descriptor.visibility,
115
+ ...(descriptor.archiveSha256Hex === undefined
116
+ ? {}
117
+ : { archiveSha256Hex: descriptor.archiveSha256Hex }),
118
+ ...(descriptor.pack === undefined
119
+ ? {}
120
+ : {
121
+ pack: {
122
+ dependencies: [...descriptor.pack.dependencies].sort(compareDependencies),
123
+ },
124
+ }),
125
+ });
126
+ export const normalizePublicationSet = (descriptors) => descriptors
127
+ .map(normalizePublicationDescriptor)
128
+ .sort((left, right) => comparePublicationTargets(left.target, right.target));
129
+ const canonicalBytes = (value) => new TextEncoder().encode(JSON.stringify(value));
130
+ const sha256Hex = (bytes) => Schema.decodeUnknownSync(Sha256HexSchema)(createHash("sha256").update(bytes).digest("hex"));
131
+ export const publicationDescriptorDigest = (descriptor) => sha256Hex(canonicalBytes({
132
+ contract: PUBLICATION_SET_CONTRACT,
133
+ descriptor: normalizePublicationDescriptor(descriptor),
134
+ }));
135
+ export const publicationSetDigest = (descriptors) => sha256Hex(canonicalBytes({
136
+ contract: PUBLICATION_SET_CONTRACT,
137
+ candidates: normalizePublicationSet(descriptors),
138
+ }));
139
+ export const archiveSha256Hex = (archive) => sha256Hex(archive);
140
+ export const publicationTargetKey = (target) => JSON.stringify([target.owner, target.type, target.name, target.version]);
141
+ const publicationIdentityKey = (target) => `${target.owner}\u0000${target.type}\u0000${target.name}`;
142
+ export const validatePublicationDescriptors = (descriptors) => {
143
+ if (descriptors.length > MAX_PUBLICATION_SET_CANDIDATES) {
144
+ throw new RangeError(`Publication sets accept at most ${MAX_PUBLICATION_SET_CANDIDATES} candidates.`);
145
+ }
146
+ Schema.decodeUnknownSync(PreviewPublicationSetRequestSchema)({
147
+ contract: PUBLICATION_SET_CONTRACT,
148
+ candidates: descriptors,
149
+ });
150
+ const identities = new Set();
151
+ for (const descriptor of descriptors) {
152
+ const key = publicationIdentityKey(descriptor.target);
153
+ if (identities.has(key)) {
154
+ throw new TypeError(`Duplicate publication target ${key}.`);
155
+ }
156
+ identities.add(key);
157
+ if ((descriptor.participation === "publish") !== (descriptor.archiveSha256Hex !== undefined)) {
158
+ throw new TypeError("archiveSha256Hex is required exactly for publish candidates.");
159
+ }
160
+ if ((descriptor.target.type === "pack") !== (descriptor.pack !== undefined)) {
161
+ throw new TypeError("Pack declarations are required exactly for pack candidates.");
162
+ }
163
+ }
164
+ return normalizePublicationSet(descriptors);
165
+ };
166
+ const findingBase = (finding) => ({
167
+ kind: "advisory",
168
+ location: { file: "pack.json" },
169
+ path: "./pack.json",
170
+ ...finding,
171
+ });
172
+ const dependencyError = (input) => {
173
+ const fqn = formatFqn(input.snapshot.dependency);
174
+ return findingBase({
175
+ ruleId: "pack/dependency-version-resolvable",
176
+ severity: "error",
177
+ reason: input.reason,
178
+ dependency: input.snapshot.dependency,
179
+ ...(input.discloseState !== true
180
+ ? {}
181
+ : {
182
+ ...(input.snapshot.visibility === "public" || input.snapshot.visibility === "private"
183
+ ? { effectiveVisibility: input.snapshot.visibility }
184
+ : {}),
185
+ ...(input.snapshot.lifecycleState === null
186
+ ? {}
187
+ : { lifecycle: input.snapshot.lifecycleState === "active" ? "active" : "unavailable" }),
188
+ }),
189
+ message: `Dependency ${fqn} requests range "${input.snapshot.dependency.range}", but ${input.explanation}.`,
190
+ suggestions: input.suggestions,
191
+ });
192
+ };
193
+ const evaluateDependencySnapshot = (snapshot, packVisibility) => {
194
+ const fqn = formatFqn(snapshot.dependency);
195
+ if (!snapshot.exists) {
196
+ return [
197
+ dependencyError({
198
+ snapshot,
199
+ reason: "target-unavailable",
200
+ explanation: "that extension does not exist in the registry",
201
+ suggestions: [
202
+ {
203
+ description: `Publish ${fqn} or correct the dependency identity`,
204
+ cmd: `axm publish ${fqn}`,
205
+ },
206
+ ],
207
+ }),
208
+ ];
209
+ }
210
+ if (packVisibility === "public" && snapshot.visibility !== "public") {
211
+ return [
212
+ dependencyError({
213
+ snapshot,
214
+ reason: "target-unavailable",
215
+ explanation: "that extension is not public",
216
+ suggestions: [{ description: `Make ${fqn} public or depend on a public extension` }],
217
+ }),
218
+ ];
219
+ }
220
+ if (snapshot.lifecycleState !== "active") {
221
+ return [
222
+ dependencyError({
223
+ snapshot,
224
+ reason: "lifecycle-unavailable",
225
+ explanation: "that extension is not active",
226
+ discloseState: true,
227
+ suggestions: [{ description: `Restore ${fqn} to active state or remove it from the pack` }],
228
+ }),
229
+ ];
230
+ }
231
+ const installable = snapshot.versions.filter((version) => version.status === "available" && !version.yanked && !version.purged);
232
+ if (installable.length === 0) {
233
+ return [
234
+ dependencyError({
235
+ snapshot,
236
+ reason: "no-installable-version",
237
+ explanation: "it has no installable versions",
238
+ discloseState: true,
239
+ suggestions: [
240
+ {
241
+ description: `Publish an installable version of ${fqn} or remove it from the pack`,
242
+ cmd: `axm publish ${fqn}`,
243
+ },
244
+ ],
245
+ }),
246
+ ];
247
+ }
248
+ if (!installable.some((candidate) => semver.satisfies(candidate.version, snapshot.dependency.range))) {
249
+ return [
250
+ dependencyError({
251
+ snapshot,
252
+ reason: "range-unsatisfied",
253
+ explanation: "no installable version satisfies the requested range",
254
+ discloseState: true,
255
+ suggestions: [
256
+ {
257
+ description: `Publish a version of ${fqn} satisfying "${snapshot.dependency.range}" or correct the requested range`,
258
+ cmd: `axm publish ${fqn}`,
259
+ },
260
+ ],
261
+ }),
262
+ ];
263
+ }
264
+ return snapshot.deprecation !== null
265
+ ? [
266
+ findingBase({
267
+ ruleId: "pack/dependency-deprecated",
268
+ severity: "warning",
269
+ reason: "deprecated",
270
+ dependency: snapshot.dependency,
271
+ effectiveVisibility: snapshot.visibility === "private" ? "private" : "public",
272
+ lifecycle: "active",
273
+ deprecation: snapshot.deprecation,
274
+ message: `Dependency ${fqn} requests range "${snapshot.dependency.range}" and resolves to a deprecated extension.`,
275
+ suggestions: [
276
+ { description: `Prefer a supported replacement for ${fqn} when one is available` },
277
+ ],
278
+ }),
279
+ ]
280
+ : [];
281
+ };
282
+ const dependencyIdentityKey = (dependency) => `${dependency.owner}\u0000${dependency.type}\u0000${dependency.name}`;
283
+ export const evaluateProspectivePackDependencies = (input) => {
284
+ const snapshots = new Map(input.snapshots.map((snapshot) => [dependencyIdentityKey(snapshot.dependency), snapshot]));
285
+ const candidates = new Map(input.candidates.map((candidate) => [
286
+ publicationIdentityKey(candidate.descriptor.target),
287
+ candidate,
288
+ ]));
289
+ return input.dependencies
290
+ .flatMap((dependency) => {
291
+ const current = snapshots.get(dependencyIdentityKey(dependency)) ??
292
+ {
293
+ dependency,
294
+ exists: false,
295
+ visibility: null,
296
+ lifecycleState: null,
297
+ deprecation: null,
298
+ versions: [],
299
+ };
300
+ const selected = candidates.get(dependencyIdentityKey(dependency));
301
+ if (selected === undefined)
302
+ return evaluateDependencySnapshot(current, input.packVisibility);
303
+ if (selected.kind === "unavailable") {
304
+ return [
305
+ dependencyError({
306
+ snapshot: current,
307
+ reason: "target-unavailable",
308
+ explanation: "that selected target is unavailable",
309
+ suggestions: [
310
+ {
311
+ description: `Verify authority and availability for ${formatFqn(dependency)}, then preview the complete set again`,
312
+ },
313
+ ],
314
+ }),
315
+ ];
316
+ }
317
+ if (input.packVisibility === "public" && selected.visibility.resolved?.value === "private") {
318
+ return [
319
+ dependencyError({
320
+ snapshot: { ...current, exists: true, visibility: "private" },
321
+ reason: current.exists ? "selected-existing-private" : "selected-new-private",
322
+ explanation: "the selected dependency will remain private",
323
+ discloseState: true,
324
+ suggestions: current.exists
325
+ ? [
326
+ {
327
+ description: `Make ${formatFqn(dependency)} public explicitly, then preview the complete set again`,
328
+ },
329
+ ]
330
+ : [
331
+ {
332
+ description: `Publish ${formatFqn(dependency)} as public, then preview the complete set again`,
333
+ cmd: `axm publish ${formatFqn(dependency)} --visibility public`,
334
+ },
335
+ ],
336
+ }),
337
+ ];
338
+ }
339
+ return evaluateDependencySnapshot({
340
+ ...current,
341
+ exists: true,
342
+ visibility: selected.visibility.resolved?.value ?? current.visibility,
343
+ lifecycleState: current.exists ? current.lifecycleState : "active",
344
+ versions: selected.descriptor.participation === "publish"
345
+ ? [
346
+ ...current.versions,
347
+ {
348
+ version: selected.descriptor.target.version,
349
+ status: "available",
350
+ yanked: false,
351
+ purged: false,
352
+ },
353
+ ]
354
+ : current.versions,
355
+ }, input.packVisibility);
356
+ })
357
+ .sort((left, right) => compareText(formatFqn(left.dependency), formatFqn(right.dependency)) ||
358
+ compareText(left.dependency.range, right.dependency.range));
359
+ };
360
+ /**
361
+ * Evaluate dependency admission and expose the exact version an ordinary
362
+ * Registry consumer would resolve from the same prospective snapshot.
363
+ */
364
+ export const evaluateProspectivePackDependencyState = (input) => {
365
+ const findings = evaluateProspectivePackDependencies(input);
366
+ const snapshots = new Map(input.snapshots.map((snapshot) => [dependencyIdentityKey(snapshot.dependency), snapshot]));
367
+ const candidates = new Map(input.candidates.map((candidate) => [
368
+ publicationIdentityKey(candidate.descriptor.target),
369
+ candidate,
370
+ ]));
371
+ const resolutions = input.dependencies.flatMap((dependency) => {
372
+ const current = snapshots.get(dependencyIdentityKey(dependency)) ??
373
+ {
374
+ dependency,
375
+ exists: false,
376
+ visibility: null,
377
+ lifecycleState: null,
378
+ deprecation: null,
379
+ versions: [],
380
+ };
381
+ const selected = candidates.get(dependencyIdentityKey(dependency));
382
+ if (selected?.kind === "unavailable" ||
383
+ (input.packVisibility === "public" && selected?.visibility.resolved?.value === "private")) {
384
+ return [];
385
+ }
386
+ const prospective = selected === undefined
387
+ ? current
388
+ : {
389
+ ...current,
390
+ exists: true,
391
+ visibility: selected.visibility.resolved?.value ?? current.visibility,
392
+ lifecycleState: current.exists ? current.lifecycleState : "active",
393
+ versions: selected.descriptor.participation === "publish"
394
+ ? [
395
+ ...current.versions,
396
+ {
397
+ version: selected.descriptor.target.version,
398
+ status: "available",
399
+ yanked: false,
400
+ purged: false,
401
+ },
402
+ ]
403
+ : current.versions,
404
+ };
405
+ if (!prospective.exists ||
406
+ (input.packVisibility === "public" && prospective.visibility !== "public") ||
407
+ prospective.lifecycleState !== "active") {
408
+ return [];
409
+ }
410
+ const effectiveVersion = semver.maxSatisfying(prospective.versions
411
+ .filter((version) => version.status === "available" && !version.yanked && !version.purged)
412
+ .map((version) => version.version), dependency.range);
413
+ return effectiveVersion === null
414
+ ? []
415
+ : [
416
+ {
417
+ dependency,
418
+ effectiveVersion: Schema.decodeUnknownSync(VersionSchema)(effectiveVersion),
419
+ },
420
+ ];
421
+ });
422
+ return {
423
+ findings,
424
+ resolutions: [...resolutions].sort((left, right) => compareDependencies(left.dependency, right.dependency)),
425
+ };
426
+ };
427
+ export const validatePublicationSetResponse = (descriptors, response) => {
428
+ const normalized = validatePublicationDescriptors(descriptors);
429
+ const expectedDigest = publicationSetDigest(normalized);
430
+ if (response.publicationSetDigest !== expectedDigest) {
431
+ throw new TypeError("The publication-set digest does not match the submitted descriptors.");
432
+ }
433
+ const expectedCandidates = new Map(normalized.map((descriptor) => [publicationTargetKey(descriptor.target), descriptor]));
434
+ if (response.candidates.length !== expectedCandidates.size) {
435
+ throw new TypeError("The publication-set response does not account for every candidate.");
436
+ }
437
+ for (const candidate of response.candidates) {
438
+ const key = publicationTargetKey(candidate.target);
439
+ const descriptor = expectedCandidates.get(key);
440
+ if (descriptor === undefined ||
441
+ candidate.participation !== descriptor.participation ||
442
+ candidate.descriptorDigest !== publicationDescriptorDigest(descriptor)) {
443
+ throw new TypeError("The publication-set response contains an incompatible candidate.");
444
+ }
445
+ const expectedFqn = formatFqn(candidate.target);
446
+ if (candidate.visibility.target !== expectedFqn) {
447
+ throw new TypeError("The publication-set response evaluates the wrong visibility target.");
448
+ }
449
+ if (candidate.kind === "resolved") {
450
+ if (candidate.visibility.resolved === null ||
451
+ JSON.stringify(candidate.visibility.intent) !==
452
+ JSON.stringify(descriptor.visibility.intent) ||
453
+ candidate.visibility.request !== descriptor.visibility.request) {
454
+ throw new TypeError("The publication-set response changed submitted visibility authority.");
455
+ }
456
+ }
457
+ if ((response.status === "admitted" &&
458
+ candidate.kind === "resolved" &&
459
+ candidate.participation === "publish") !==
460
+ (candidate.kind === "resolved" && candidate.condition !== undefined)) {
461
+ throw new TypeError("Publication conditions are valid only for admitted upload candidates.");
462
+ }
463
+ }
464
+ const visibilityBlocked = response.candidates.some((candidate) => candidate.kind === "unavailable" ||
465
+ candidate.visibility.findings.some((finding) => finding.severity === "error"));
466
+ const packBlocked = response.packs.some((pack) => pack.status === "blocked");
467
+ if ((response.status === "blocked") !== (visibilityBlocked || packBlocked)) {
468
+ throw new TypeError("The publication-set status does not match its candidate findings.");
469
+ }
470
+ const expectedPacks = normalized.filter((descriptor) => descriptor.target.type === "pack" && descriptor.participation === "publish");
471
+ if (response.packs.length !== expectedPacks.length) {
472
+ throw new TypeError("The publication-set response does not account for every publishable pack.");
473
+ }
474
+ const packKeys = new Set();
475
+ for (const pack of response.packs) {
476
+ const key = publicationTargetKey(pack.target);
477
+ const descriptor = expectedPacks.find((candidate) => publicationTargetKey(candidate.target) === key);
478
+ if (packKeys.has(key) || descriptor === undefined) {
479
+ throw new TypeError("The publication-set response contains an incompatible pack result.");
480
+ }
481
+ packKeys.add(key);
482
+ if (pack.status === "blocked") {
483
+ if (pack.resolutions.length > 0) {
484
+ throw new TypeError("Blocked pack results cannot claim effective dependency resolutions.");
485
+ }
486
+ continue;
487
+ }
488
+ const expectedDependencies = descriptor.pack?.dependencies ?? [];
489
+ if (pack.resolutions.length !== expectedDependencies.length) {
490
+ throw new TypeError("An admitted pack result does not account for every effective dependency resolution.");
491
+ }
492
+ const resolutionKeys = new Set();
493
+ for (const resolution of pack.resolutions) {
494
+ const dependencyKey = dependencyIdentityKey(resolution.dependency);
495
+ const expected = expectedDependencies.find((dependency) => dependencyIdentityKey(dependency) === dependencyKey);
496
+ if (resolutionKeys.has(dependencyKey) ||
497
+ expected === undefined ||
498
+ expected.range !== resolution.dependency.range ||
499
+ !semver.satisfies(resolution.effectiveVersion, resolution.dependency.range)) {
500
+ throw new TypeError("The publication-set response contains an incompatible effective dependency resolution.");
501
+ }
502
+ resolutionKeys.add(dependencyKey);
503
+ }
504
+ }
505
+ return response;
506
+ };
507
+ //# sourceMappingURL=publication-set.js.map
@@ -0,0 +1,57 @@
1
+ import * as Duration from "effect/Duration";
2
+ import * as Effect from "effect/Effect";
3
+ import * as Option from "effect/Option";
4
+ import type { ExtensionFqnParts } from "@agentxm/extension-model/unstable/extensions/common";
5
+ import type { ReleaseAgeEvaluation, ReleaseAgeEvidence, ReleaseAgeExemption } from "@agentxm/extension-model/unstable/extensions/release-age";
6
+ import type { VersionEntry } from "./schema.js";
7
+ export declare const releaseAgeExemptionForIdentity: (evaluation: ReleaseAgeEvaluation, identity: ExtensionFqnParts) => ReleaseAgeExemption | undefined;
8
+ export interface ReleaseAgeRecordBase {
9
+ readonly reason: "minimum-release-age";
10
+ readonly target: string;
11
+ readonly dependencyPath: ReadonlyArray<string>;
12
+ readonly requestedRange?: string;
13
+ readonly currentVersion?: string;
14
+ readonly selectedVersion?: string;
15
+ readonly candidateVersion: string;
16
+ readonly publishedAt: string;
17
+ readonly eligibleAt: string;
18
+ readonly minimumReleaseAgeSeconds: number;
19
+ }
20
+ export type ReleaseAgeHoldbackRecord = ReleaseAgeRecordBase;
21
+ export type ReleaseAgeBypassRecord = ReleaseAgeRecordBase & ({
22
+ readonly bypassCause: "exclude";
23
+ readonly exemptionScope: "project" | "user";
24
+ } | {
25
+ readonly bypassCause: "ignore-flag";
26
+ });
27
+ export type ReleaseAgeRecord = ReleaseAgeHoldbackRecord | ReleaseAgeBypassRecord;
28
+ export interface ReleaseAgeOperationEvidence {
29
+ readonly evaluatedAt: string;
30
+ readonly holdbacks: ReadonlyArray<ReleaseAgeHoldbackRecord>;
31
+ readonly bypasses: ReadonlyArray<ReleaseAgeBypassRecord>;
32
+ }
33
+ /** Deduplicate and order operation evidence independently from concurrent resolution order. */
34
+ export declare const normalizeReleaseAgeRecords: <Record extends ReleaseAgeRecord>(records: ReadonlyArray<Record>) => ReadonlyArray<Record>;
35
+ export declare const DEFAULT_MINIMUM_RELEASE_AGE = "24h";
36
+ export declare const DEFAULT_MINIMUM_RELEASE_AGE_DURATION: Duration.Duration;
37
+ export declare const parseMinimumReleaseAge: (value: string) => Option.Option<Duration.Duration>;
38
+ /**
39
+ * Render a release-age window in the compact grammar the setting accepts, so
40
+ * output names the policy in the same units the reader would configure.
41
+ *
42
+ * Days are used only from two days up: the default window is written `24h`,
43
+ * and reporting it as `1d` would not match the setting the reader would edit.
44
+ * Falls back to whole seconds when the window is not a clean larger unit.
45
+ */
46
+ export declare const formatMinimumReleaseAgeSeconds: (seconds: number) => string;
47
+ export declare const isVersionEntryMature: (entry: VersionEntry, minimumAge: Duration.Duration) => Effect.Effect<boolean>;
48
+ export declare const isVersionEntryEligibleAt: (entry: VersionEntry, evaluation: ReleaseAgeEvaluation) => boolean;
49
+ export declare const releaseAgeEvidence: (entry: VersionEntry, evaluation: ReleaseAgeEvaluation) => ReleaseAgeEvidence;
50
+ export declare const filterMatureVersions: (versions: ReadonlyArray<VersionEntry>, minimumAge: Duration.Duration) => Effect.Effect<ReadonlyArray<VersionEntry>>;
51
+ export declare const releaseAgeHoldbackWarning: (args: {
52
+ readonly fqn: string;
53
+ readonly selectedVersion: string;
54
+ readonly heldVersion: string;
55
+ readonly minimumReleaseAge: string;
56
+ }) => string;
57
+ //# sourceMappingURL=release-age-policy.d.ts.map
@@ -0,0 +1,117 @@
1
+ import * as DateTime from "effect/DateTime";
2
+ import * as Duration from "effect/Duration";
3
+ import * as Effect from "effect/Effect";
4
+ import * as Option from "effect/Option";
5
+ import { matchesReleaseAgeExcludePattern } from "@agentxm/extension-model/unstable/extensions/fqn-pattern";
6
+ export const releaseAgeExemptionForIdentity = (evaluation, identity) => {
7
+ if (evaluation.grantedExemption !== undefined)
8
+ return evaluation.grantedExemption;
9
+ const excluded = evaluation.exclude?.find(({ pattern }) => matchesReleaseAgeExcludePattern(pattern, identity));
10
+ if (excluded !== undefined) {
11
+ return { bypassCause: "exclude", exemptionScope: excluded.scope };
12
+ }
13
+ return evaluation.mode === "ignore" ? { bypassCause: "ignore-flag" } : undefined;
14
+ };
15
+ const releaseAgeRecordKey = (record) => [
16
+ record.target,
17
+ ...record.dependencyPath,
18
+ record.candidateVersion,
19
+ record.selectedVersion ?? "",
20
+ record.currentVersion ?? "",
21
+ record.requestedRange ?? "",
22
+ "bypassCause" in record ? record.bypassCause : "",
23
+ "exemptionScope" in record ? record.exemptionScope : "",
24
+ ].join("\u0000");
25
+ /** Deduplicate and order operation evidence independently from concurrent resolution order. */
26
+ export const normalizeReleaseAgeRecords = (records) => {
27
+ const byKey = new Map();
28
+ for (const record of records) {
29
+ byKey.set(releaseAgeRecordKey(record), record);
30
+ }
31
+ return [...byKey.entries()]
32
+ .sort(([left], [right]) => left.localeCompare(right))
33
+ .map(([, record]) => record);
34
+ };
35
+ export const DEFAULT_MINIMUM_RELEASE_AGE = "24h";
36
+ export const DEFAULT_MINIMUM_RELEASE_AGE_DURATION = Duration.hours(24);
37
+ /**
38
+ * Documented exception: hand-rolled parser for the compact duration grammar
39
+ * (`24h`, `30d`, `500ms`).
40
+ *
41
+ * Effect v4 has no `Duration.decode`; the closest constructors are
42
+ * `Duration.fromInput` / `Duration.fromInputUnsafe`, and the `Duration.Input`
43
+ * string form only accepts space-separated long-unit strings ("30 days"), so
44
+ * the compact grammar is not expressible upstream. `durationPattern` and
45
+ * `parseMinimumReleaseAge` therefore stay as a thin syntax adapter over the
46
+ * `Duration` constructors (`Duration.millis` / `seconds` / `minutes` /
47
+ * `hours` / `days`).
48
+ *
49
+ * Removal condition: upstream `Duration` accepting the compact unit grammar.
50
+ */
51
+ const durationPattern = /^(\d+)(ms|s|m|h|d)$/;
52
+ export const parseMinimumReleaseAge = (value) => {
53
+ const trimmed = value.trim();
54
+ const match = durationPattern.exec(trimmed);
55
+ if (match === null)
56
+ return Option.none();
57
+ const amountText = match[1];
58
+ const unit = match[2];
59
+ if (amountText === undefined || unit === undefined)
60
+ return Option.none();
61
+ const amount = Number(amountText);
62
+ if (!Number.isSafeInteger(amount))
63
+ return Option.none();
64
+ switch (unit) {
65
+ case "ms":
66
+ return Option.some(Duration.millis(amount));
67
+ case "s":
68
+ return Option.some(Duration.seconds(amount));
69
+ case "m":
70
+ return Option.some(Duration.minutes(amount));
71
+ case "h":
72
+ return Option.some(Duration.hours(amount));
73
+ case "d":
74
+ return Option.some(Duration.days(amount));
75
+ }
76
+ // Unreachable at runtime, but `unit` is typed `string` (a regex capture),
77
+ // so the switch is not statically exhaustive and the compiler (TS2366)
78
+ // requires this ending return.
79
+ return Option.none();
80
+ };
81
+ /**
82
+ * Render a release-age window in the compact grammar the setting accepts, so
83
+ * output names the policy in the same units the reader would configure.
84
+ *
85
+ * Days are used only from two days up: the default window is written `24h`,
86
+ * and reporting it as `1d` would not match the setting the reader would edit.
87
+ * Falls back to whole seconds when the window is not a clean larger unit.
88
+ */
89
+ export const formatMinimumReleaseAgeSeconds = (seconds) => {
90
+ const whole = Math.max(0, Math.round(seconds));
91
+ if (whole === 0)
92
+ return "0s";
93
+ if (whole % 86_400 === 0 && whole >= 172_800)
94
+ return `${whole / 86_400}d`;
95
+ if (whole % 3_600 === 0)
96
+ return `${whole / 3_600}h`;
97
+ if (whole % 60 === 0)
98
+ return `${whole / 60}m`;
99
+ return `${whole}s`;
100
+ };
101
+ export const isVersionEntryMature = (entry, minimumAge) => Duration.isLessThanOrEqualTo(minimumAge, Duration.zero)
102
+ ? Effect.succeed(true)
103
+ : DateTime.now.pipe(Effect.map((now) =>
104
+ // Inclusive at the boundary: a release published exactly `minimumAge`
105
+ // ago is mature, matching the original `now - published >= minimumAge`.
106
+ DateTime.isLessThanOrEqualTo(DateTime.addDuration(entry.published, minimumAge), now)));
107
+ export const isVersionEntryEligibleAt = (entry, evaluation) => Duration.isLessThanOrEqualTo(evaluation.minimumReleaseAge, Duration.zero) ||
108
+ DateTime.isLessThanOrEqualTo(DateTime.addDuration(entry.published, evaluation.minimumReleaseAge), evaluation.evaluatedAt);
109
+ export const releaseAgeEvidence = (entry, evaluation) => ({
110
+ version: entry.version,
111
+ publishedAt: DateTime.formatIso(entry.published),
112
+ eligibleAt: DateTime.formatIso(DateTime.addDuration(entry.published, evaluation.minimumReleaseAge)),
113
+ minimumReleaseAgeSeconds: Math.max(0, Duration.toMillis(evaluation.minimumReleaseAge) / 1_000),
114
+ });
115
+ export const filterMatureVersions = (versions, minimumAge) => Effect.filter(versions, (entry) => isVersionEntryMature(entry, minimumAge));
116
+ export const releaseAgeHoldbackWarning = (args) => `${args.fqn} held at ${args.selectedVersion} — ${args.heldVersion} has not reached the ${args.minimumReleaseAge} minimum release age`;
117
+ //# sourceMappingURL=release-age-policy.js.map