@agentxm/extension-resolution 0.28.14-preview.1789139351.5cd4595aa

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 (47) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +56 -0
  3. package/dist/src/accepted-pack-dependency-resolver.d.ts +23 -0
  4. package/dist/src/accepted-pack-dependency-resolver.js +31 -0
  5. package/dist/src/accepted-pack-hydration.d.ts +33 -0
  6. package/dist/src/accepted-pack-hydration.js +93 -0
  7. package/dist/src/axm-skill-candidate.d.ts +16 -0
  8. package/dist/src/axm-skill-candidate.js +80 -0
  9. package/dist/src/axm-skill-compatibility.d.ts +87 -0
  10. package/dist/src/axm-skill-compatibility.js +248 -0
  11. package/dist/src/axm-skill-workspace-compatibility.d.ts +23 -0
  12. package/dist/src/axm-skill-workspace-compatibility.js +110 -0
  13. package/dist/src/configured-entry-resolution.d.ts +118 -0
  14. package/dist/src/configured-entry-resolution.js +735 -0
  15. package/dist/src/configured-entry.d.ts +38 -0
  16. package/dist/src/configured-entry.js +8 -0
  17. package/dist/src/errors.d.ts +113 -0
  18. package/dist/src/errors.js +71 -0
  19. package/dist/src/index.d.ts +34 -0
  20. package/dist/src/index.js +45 -0
  21. package/dist/src/live.d.ts +25 -0
  22. package/dist/src/live.js +47 -0
  23. package/dist/src/named-registry-resolution.d.ts +26 -0
  24. package/dist/src/named-registry-resolution.js +110 -0
  25. package/dist/src/pack-dependency-resolution.d.ts +73 -0
  26. package/dist/src/pack-dependency-resolution.js +422 -0
  27. package/dist/src/publisher-binding.d.ts +66 -0
  28. package/dist/src/publisher-binding.js +75 -0
  29. package/dist/src/release-age-policy.d.ts +55 -0
  30. package/dist/src/release-age-policy.js +115 -0
  31. package/dist/src/release-age-posture.d.ts +23 -0
  32. package/dist/src/release-age-posture.js +15 -0
  33. package/dist/src/resolution-timeout.d.ts +11 -0
  34. package/dist/src/resolution-timeout.js +17 -0
  35. package/dist/src/resolved-pack-dependency.d.ts +32 -0
  36. package/dist/src/resolved-pack-dependency.js +28 -0
  37. package/dist/src/source-authority.d.ts +60 -0
  38. package/dist/src/source-authority.js +70 -0
  39. package/dist/src/testing.d.ts +40 -0
  40. package/dist/src/testing.js +40 -0
  41. package/dist/src/update/constraint-precedence.d.ts +75 -0
  42. package/dist/src/update/constraint-precedence.js +99 -0
  43. package/dist/src/update/targeted-update-context.d.ts +101 -0
  44. package/dist/src/update/targeted-update-context.js +217 -0
  45. package/dist/src/version-resolution.d.ts +39 -0
  46. package/dist/src/version-resolution.js +76 -0
  47. package/package.json +66 -0
@@ -0,0 +1,422 @@
1
+ /**
2
+ * Pack dependency resolution: every declared member resolved to one exact
3
+ * identity under the pack's constraints, workspace authority, and the
4
+ * minimum-release-age policy.
5
+ *
6
+ * @experimental This API is unstable and may change without notice.
7
+ * @packageDocumentation
8
+ */
9
+ import * as Effect from "effect/Effect";
10
+ import * as Option from "effect/Option";
11
+ import * as semver from "semver";
12
+ import { formatFqn, parseFqnOrThrow, toExtensionTypePlural, } from "@agentxm/extension-model/unstable/extensions";
13
+ import { PackConstraintShadowed, PackDependencyConflict, PackDependencyInvalid, PackDependencyMissing, PackDependencyUnsatisfied, SourceAuthorityBlocked, } from "./errors.js";
14
+ import { releaseAgeExemptionForIdentity } from "./release-age-policy.js";
15
+ const validateSelectedDependency = (candidate, expectedType, parsed, fqn, constraint) => {
16
+ if (candidate.type !== expectedType ||
17
+ (candidate.refType !== "registry" && candidate.refType !== "workspace") ||
18
+ candidate.owner !== parsed.owner ||
19
+ candidate.name !== parsed.name) {
20
+ return new PackDependencyConflict({
21
+ detail: `Authorized dependency resolution does not match pack dependency ${fqn}`,
22
+ });
23
+ }
24
+ if (!semver.satisfies(candidate.version, constraint)) {
25
+ return new PackDependencyConflict({
26
+ detail: `Authorized dependency ${fqn}@${candidate.version} does not satisfy ${constraint}`,
27
+ });
28
+ }
29
+ return Effect.succeed({
30
+ owner: parsed.owner,
31
+ type: expectedType,
32
+ name: parsed.name,
33
+ ref: candidate,
34
+ });
35
+ };
36
+ const registrySourceForDependency = (pack, owner, sourceOverride) => {
37
+ const source = sourceOverride ?? (pack.source.type === "registry" ? pack.source : undefined);
38
+ if (source === undefined) {
39
+ return Effect.fail(new PackDependencyInvalid({
40
+ detail: `Cannot resolve pack dependencies from non-registry source`,
41
+ }));
42
+ }
43
+ return Effect.succeed({
44
+ ...source,
45
+ owner: Option.some(owner),
46
+ });
47
+ };
48
+ const workspaceConstraintConflict = (pack, memberFqn, workspaceVersion, constraint) => new PackConstraintShadowed({
49
+ packSource: pack.source.type === "workspace" ? "workspace" : "registry",
50
+ packFqn: formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name }),
51
+ memberFqn,
52
+ constraint,
53
+ workspaceVersion,
54
+ });
55
+ const resolveDependencyRef = (pack, expectedType, fqn, constraint, sources, minimumReleaseAge, sourceOverride, workspaceResolver, dependencyResolver) => Effect.gen(function* () {
56
+ const parsed = parseFqnOrThrow(fqn);
57
+ if (parsed.type !== expectedType) {
58
+ return yield* new PackDependencyInvalid({
59
+ detail: `Pack dependency type mismatch for expected ${expectedType}`,
60
+ });
61
+ }
62
+ if (workspaceResolver !== undefined) {
63
+ const workspace = yield* workspaceResolver({
64
+ owner: parsed.owner,
65
+ type: expectedType,
66
+ name: parsed.name,
67
+ constraint,
68
+ root: formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name }),
69
+ });
70
+ if (workspace.kind === "blocked") {
71
+ return yield* new SourceAuthorityBlocked({
72
+ detail: workspace.fact.detail,
73
+ recovery: workspace.fact.recovery,
74
+ });
75
+ }
76
+ if (workspace.kind === "selected") {
77
+ const candidate = workspace.ref;
78
+ if (candidate.type !== expectedType ||
79
+ candidate.refType !== "workspace" ||
80
+ candidate.owner !== parsed.owner ||
81
+ candidate.name !== parsed.name) {
82
+ return yield* new PackDependencyConflict({
83
+ detail: `Configured workspace authority does not match pack dependency ${fqn}`,
84
+ });
85
+ }
86
+ if (!semver.satisfies(candidate.version, constraint)) {
87
+ return yield* workspaceConstraintConflict(pack, fqn, candidate.version, constraint);
88
+ }
89
+ return {
90
+ owner: parsed.owner,
91
+ type: expectedType,
92
+ name: parsed.name,
93
+ ref: candidate,
94
+ };
95
+ }
96
+ }
97
+ if (dependencyResolver !== undefined) {
98
+ const candidate = yield* dependencyResolver({
99
+ owner: parsed.owner,
100
+ type: expectedType,
101
+ name: parsed.name,
102
+ constraint,
103
+ root: formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name }),
104
+ });
105
+ return yield* validateSelectedDependency(candidate, expectedType, parsed, fqn, constraint);
106
+ }
107
+ const source = yield* registrySourceForDependency(pack, parsed.owner, sourceOverride);
108
+ const matches = yield* Effect.scoped(sources.find(source, {
109
+ names: [parsed.name],
110
+ type: expectedType,
111
+ owner: Option.some(parsed.owner),
112
+ versionRange: Option.some(constraint),
113
+ ...(minimumReleaseAge === undefined ? {} : { minimumReleaseAge }),
114
+ }));
115
+ const matchingRef = matches.find((candidate) => candidate.type === expectedType &&
116
+ candidate.refType === "registry" &&
117
+ candidate.owner === parsed.owner &&
118
+ candidate.name === parsed.name);
119
+ if (matchingRef === undefined) {
120
+ return yield* new PackDependencyInvalid({
121
+ detail: `Unable to resolve pack dependency ${fqn}@${constraint}`,
122
+ });
123
+ }
124
+ return {
125
+ owner: parsed.owner,
126
+ type: expectedType,
127
+ name: parsed.name,
128
+ ref: matchingRef,
129
+ };
130
+ });
131
+ const dependencyReleaseAgeRecord = (args) => ({
132
+ reason: "minimum-release-age",
133
+ target: args.dependencyTarget,
134
+ dependencyPath: [args.packTarget, args.dependencyTarget],
135
+ requestedRange: args.constraint,
136
+ ...(args.selectedVersion === undefined ? {} : { selectedVersion: args.selectedVersion }),
137
+ candidateVersion: args.evidence.version,
138
+ publishedAt: args.evidence.publishedAt,
139
+ eligibleAt: args.evidence.eligibleAt,
140
+ minimumReleaseAgeSeconds: args.evidence.minimumReleaseAgeSeconds,
141
+ });
142
+ const resolveDependencyRefWithReleaseAge = (pack, expectedType, fqn, constraint, sources, evaluation, sourceOverride, workspaceResolver, dependencyResolver) => Effect.gen(function* () {
143
+ const parsed = parseFqnOrThrow(fqn);
144
+ if (parsed.type !== expectedType) {
145
+ return yield* new PackDependencyInvalid({
146
+ detail: `Pack dependency type mismatch for expected ${expectedType}`,
147
+ });
148
+ }
149
+ if (workspaceResolver !== undefined) {
150
+ const workspace = yield* workspaceResolver({
151
+ owner: parsed.owner,
152
+ type: expectedType,
153
+ name: parsed.name,
154
+ constraint,
155
+ root: formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name }),
156
+ });
157
+ if (workspace.kind === "blocked") {
158
+ return yield* new SourceAuthorityBlocked({
159
+ detail: workspace.fact.detail,
160
+ recovery: workspace.fact.recovery,
161
+ });
162
+ }
163
+ if (workspace.kind === "selected") {
164
+ const candidate = workspace.ref;
165
+ if (candidate.type !== expectedType ||
166
+ candidate.refType !== "workspace" ||
167
+ candidate.owner !== parsed.owner ||
168
+ candidate.name !== parsed.name) {
169
+ return yield* new PackDependencyConflict({
170
+ detail: `Configured workspace authority does not match pack dependency ${fqn}`,
171
+ });
172
+ }
173
+ if (!semver.satisfies(candidate.version, constraint)) {
174
+ return yield* workspaceConstraintConflict(pack, fqn, candidate.version, constraint);
175
+ }
176
+ return {
177
+ kind: "selected",
178
+ dependency: {
179
+ owner: parsed.owner,
180
+ type: expectedType,
181
+ name: parsed.name,
182
+ ref: candidate,
183
+ },
184
+ holdbacks: [],
185
+ bypasses: [],
186
+ };
187
+ }
188
+ }
189
+ if (dependencyResolver !== undefined) {
190
+ const candidate = yield* dependencyResolver({
191
+ owner: parsed.owner,
192
+ type: expectedType,
193
+ name: parsed.name,
194
+ constraint,
195
+ root: formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name }),
196
+ });
197
+ return {
198
+ kind: "selected",
199
+ dependency: yield* validateSelectedDependency(candidate, expectedType, parsed, fqn, constraint),
200
+ holdbacks: [],
201
+ bypasses: [],
202
+ };
203
+ }
204
+ const source = yield* registrySourceForDependency(pack, parsed.owner, sourceOverride);
205
+ const resolution = yield* Effect.scoped(sources.resolveNamedRegistry(source, {
206
+ name: parsed.name,
207
+ type: expectedType,
208
+ owner: parsed.owner,
209
+ versionRange: Option.some(constraint),
210
+ releaseAgeEvaluation: evaluation,
211
+ }));
212
+ const packTarget = formatFqn({ owner: pack.owner, type: "pack", name: pack.pack.name });
213
+ const dependencyTarget = formatFqn(parsed);
214
+ if (resolution.kind === "not_found") {
215
+ return yield* new PackDependencyMissing({ dependencyTarget });
216
+ }
217
+ if (resolution.kind === "version_unsatisfied") {
218
+ return yield* new PackDependencyUnsatisfied({ dependencyTarget, constraint });
219
+ }
220
+ if (resolution.kind === "policy_held") {
221
+ return {
222
+ kind: "policy_held",
223
+ holdback: dependencyReleaseAgeRecord({
224
+ packTarget,
225
+ dependencyTarget,
226
+ constraint,
227
+ evidence: resolution.candidate,
228
+ }),
229
+ };
230
+ }
231
+ const ref = resolution.ref;
232
+ return {
233
+ kind: "selected",
234
+ dependency: {
235
+ owner: parsed.owner,
236
+ type: expectedType,
237
+ name: parsed.name,
238
+ ref,
239
+ },
240
+ holdbacks: resolution.kind === "exempted" || resolution.newerHeld === undefined
241
+ ? []
242
+ : [
243
+ dependencyReleaseAgeRecord({
244
+ packTarget,
245
+ dependencyTarget,
246
+ constraint,
247
+ evidence: resolution.newerHeld,
248
+ selectedVersion: ref.version,
249
+ }),
250
+ ],
251
+ bypasses: resolution.kind === "selected"
252
+ ? []
253
+ : [
254
+ {
255
+ ...dependencyReleaseAgeRecord({
256
+ packTarget,
257
+ dependencyTarget,
258
+ constraint,
259
+ evidence: resolution.bypassed,
260
+ selectedVersion: ref.version,
261
+ }),
262
+ ...resolution.exemption,
263
+ },
264
+ ],
265
+ };
266
+ });
267
+ const toResolvedMap = (dependencies) => Object.fromEntries(dependencies.map((dependency) => {
268
+ const ref = dependency.ref;
269
+ return [
270
+ formatFqn(dependency),
271
+ ref.refType === "registry"
272
+ ? {
273
+ source: "registry",
274
+ version: ref.version,
275
+ publisherBindingId: ref.publisherBindingId,
276
+ integrity: Option.getOrElse(ref.integrity, () => ""),
277
+ }
278
+ : {
279
+ source: "workspace",
280
+ version: ref.version,
281
+ sourceIdentity: `workspace:${ref.owner}/${toExtensionTypePlural(ref.type)}/${ref.name}`,
282
+ contentIdentity: ref.sourceHash,
283
+ },
284
+ ];
285
+ }));
286
+ const resolveDependencyGroup = (pack, dependencies, expectedType, sources, minimumReleaseAge, sourceOverride, workspaceResolver, dependencyResolver) => Effect.forEach(dependencies, ([fqn, constraint]) => resolveDependencyRef(pack, expectedType, fqn, constraint, sources, minimumReleaseAge, sourceOverride, workspaceResolver, dependencyResolver), { concurrency: "unbounded" });
287
+ /**
288
+ * Group dependency FQNs by extension type.
289
+ *
290
+ * The `groups` record is keyed by every non-pack extension type, so a new type
291
+ * fails compile here rather than being silently dropped from pack membership.
292
+ */
293
+ const partitionDependencies = (dependencies) => {
294
+ const groups = {
295
+ skill: [],
296
+ "mcp-server": [],
297
+ subagent: [],
298
+ rule: [],
299
+ hook: [],
300
+ knowledge: [],
301
+ };
302
+ const unsupported = [];
303
+ for (const [fqn, constraint] of Object.entries(dependencies)) {
304
+ const parsed = parseFqnOrThrow(fqn);
305
+ if (parsed.type === "pack") {
306
+ unsupported.push(fqn);
307
+ continue;
308
+ }
309
+ groups[parsed.type].push([fqn, constraint]);
310
+ }
311
+ return { groups, unsupported };
312
+ };
313
+ export const resolvePackDependencies = (pack, sources, minimumReleaseAge, sourceOverride, workspaceResolver, dependencyResolver) => Effect.gen(function* () {
314
+ const dependencies = partitionDependencies(pack.pack.dependencies);
315
+ if (dependencies.unsupported.length > 0) {
316
+ return yield* new PackDependencyInvalid({
317
+ detail: `Pack declares ${dependencies.unsupported.length} unsupported dependency type${dependencies.unsupported.length === 1 ? "" : "s"}: ${dependencies.unsupported.join(", ")}`,
318
+ });
319
+ }
320
+ const resolveGroup = (type) => resolveDependencyGroup(pack, dependencies.groups[type], type, sources, minimumReleaseAge, sourceOverride, workspaceResolver, dependencyResolver);
321
+ const resolvedSkills = yield* resolveGroup("skill");
322
+ const resolvedMcpServers = yield* resolveGroup("mcp-server");
323
+ const resolvedSubagents = yield* resolveGroup("subagent");
324
+ const resolvedRules = yield* resolveGroup("rule");
325
+ const resolvedHooks = yield* resolveGroup("hook");
326
+ const resolvedKnowledge = yield* resolveGroup("knowledge");
327
+ return {
328
+ resolvedSkills: toResolvedMap(resolvedSkills),
329
+ resolvedMcpServers: toResolvedMap(resolvedMcpServers),
330
+ resolvedSubagents: toResolvedMap(resolvedSubagents),
331
+ resolvedRules: toResolvedMap(resolvedRules),
332
+ resolvedHooks: toResolvedMap(resolvedHooks),
333
+ resolvedKnowledge: toResolvedMap(resolvedKnowledge),
334
+ dependencyRefs: [
335
+ ...resolvedSkills,
336
+ ...resolvedMcpServers,
337
+ ...resolvedSubagents,
338
+ ...resolvedRules,
339
+ ...resolvedHooks,
340
+ ...resolvedKnowledge,
341
+ ].map((dependency) => dependency.ref),
342
+ };
343
+ });
344
+ export const resolvePackDependenciesWithReleaseAge = (pack, sources, evaluation, sourceOverride, workspaceResolver, dependencyResolver) => Effect.gen(function* () {
345
+ const packExemption = pack.refType === "registry"
346
+ ? releaseAgeExemptionForIdentity(evaluation, {
347
+ owner: pack.owner,
348
+ type: "pack",
349
+ name: pack.pack.name,
350
+ })
351
+ : undefined;
352
+ const dependencyEvaluation = packExemption?.bypassCause === "exclude"
353
+ ? { ...evaluation, grantedExemption: packExemption }
354
+ : evaluation;
355
+ const dependencies = partitionDependencies(pack.pack.dependencies);
356
+ if (dependencies.unsupported.length > 0) {
357
+ return yield* new PackDependencyInvalid({
358
+ detail: `Pack declares ${dependencies.unsupported.length} unsupported dependency type${dependencies.unsupported.length === 1 ? "" : "s"}: ${dependencies.unsupported.join(", ")}`,
359
+ });
360
+ }
361
+ const entries = [
362
+ ...dependencies.groups.skill.map(([fqn, constraint]) => ({
363
+ type: "skill",
364
+ fqn,
365
+ constraint,
366
+ })),
367
+ ...dependencies.groups["mcp-server"].map(([fqn, constraint]) => ({
368
+ type: "mcp-server",
369
+ fqn,
370
+ constraint,
371
+ })),
372
+ ...dependencies.groups.subagent.map(([fqn, constraint]) => ({
373
+ type: "subagent",
374
+ fqn,
375
+ constraint,
376
+ })),
377
+ ...dependencies.groups.rule.map(([fqn, constraint]) => ({
378
+ type: "rule",
379
+ fqn,
380
+ constraint,
381
+ })),
382
+ ...dependencies.groups.hook.map(([fqn, constraint]) => ({
383
+ type: "hook",
384
+ fqn,
385
+ constraint,
386
+ })),
387
+ ...dependencies.groups.knowledge.map(([fqn, constraint]) => ({
388
+ type: "knowledge",
389
+ fqn,
390
+ constraint,
391
+ })),
392
+ ];
393
+ const resolutions = yield* Effect.forEach(entries, ({ type, fqn, constraint }) => resolveDependencyRefWithReleaseAge(pack, type, fqn, constraint, sources, dependencyEvaluation, sourceOverride, workspaceResolver, dependencyResolver), { concurrency: "unbounded" });
394
+ const holdbacks = resolutions.flatMap((resolution) => resolution.kind === "policy_held" ? [resolution.holdback] : resolution.holdbacks);
395
+ const bypasses = resolutions.flatMap((resolution) => resolution.kind === "selected" ? resolution.bypasses : []);
396
+ if (resolutions.some((resolution) => resolution.kind === "policy_held")) {
397
+ return { kind: "policy_held", holdbacks, bypasses };
398
+ }
399
+ const selected = resolutions.flatMap((resolution) => resolution.kind === "selected" ? [resolution.dependency] : []);
400
+ const byType = (type) => selected.filter((dependency) => dependency.type === type);
401
+ const resolvedSkills = byType("skill");
402
+ const resolvedMcpServers = byType("mcp-server");
403
+ const resolvedSubagents = byType("subagent");
404
+ const resolvedRules = byType("rule");
405
+ const resolvedHooks = byType("hook");
406
+ const resolvedKnowledge = byType("knowledge");
407
+ return {
408
+ kind: "selected",
409
+ holdbacks,
410
+ bypasses,
411
+ dependencies: {
412
+ resolvedSkills: toResolvedMap(resolvedSkills),
413
+ resolvedMcpServers: toResolvedMap(resolvedMcpServers),
414
+ resolvedSubagents: toResolvedMap(resolvedSubagents),
415
+ resolvedRules: toResolvedMap(resolvedRules),
416
+ resolvedHooks: toResolvedMap(resolvedHooks),
417
+ resolvedKnowledge: toResolvedMap(resolvedKnowledge),
418
+ dependencyRefs: selected.map((dependency) => dependency.ref),
419
+ },
420
+ };
421
+ });
422
+ //# sourceMappingURL=pack-dependency-resolution.js.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Publisher binding trust classification.
3
+ *
4
+ * A Registry resolution binds a publisher identity. When an operation
5
+ * proposes to replace an already accepted binding for the same configured
6
+ * extension with a different one, that transition is a trust decision a
7
+ * person makes at a prompt: no flag and no unattended mode can approve it.
8
+ * Every route that can replace an accepted binding — root and type update
9
+ * forms, reinstall and re-resolution, and pack closures — classifies the
10
+ * transition from the structured accepted and proposed identities here, so
11
+ * the same rule holds everywhere the transition can occur.
12
+ *
13
+ * A first acceptance is not a change, and an integrity mismatch while
14
+ * reacquiring the same accepted identity remains a hard failure elsewhere.
15
+ *
16
+ * Classification is all this module does: shaping the resulting plan risk
17
+ * condition belongs to the feature that owns the plan.
18
+ *
19
+ * @experimental This API is unstable and may change without notice.
20
+ * @packageDocumentation
21
+ */
22
+ import * as Option from "effect/Option";
23
+ import type { ExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/extension-ref";
24
+ import type { ExtensionType } from "@agentxm/extension-model/unstable/extensions";
25
+ /**
26
+ * The Registry identity a step proposes to accept. Trust classification
27
+ * compares it with the accepted resolution for the same configured target,
28
+ * so a change of publisher is identified from structured data rather than
29
+ * from warning text.
30
+ */
31
+ export interface RegistryBindingProposal {
32
+ readonly extensionType: ExtensionType;
33
+ /** The configured (local) name whose accepted resolution the step replaces. */
34
+ readonly target: string;
35
+ readonly owner: string;
36
+ readonly packageName: string;
37
+ readonly version: string;
38
+ readonly publisherBindingId: string;
39
+ }
40
+ export interface PublisherBindingTransition {
41
+ readonly extensionType: ExtensionType;
42
+ /** The configured (local) name whose accepted resolution changes publisher. */
43
+ readonly target: string;
44
+ readonly owner: string;
45
+ readonly packageName: string;
46
+ /** The accepted publisher binding being replaced. */
47
+ readonly accepted: string;
48
+ /** The publisher binding the operation proposes to accept. */
49
+ readonly proposed: string;
50
+ }
51
+ /** The structured proposal an extension ref makes, when it is a Registry ref. */
52
+ export declare const registryBindingProposal: (ref: ExtensionRef) => RegistryBindingProposal | undefined;
53
+ /**
54
+ * Classify one proposed acceptance against the accepted resolution for the
55
+ * same configured target. Only a Registry binding replacing a different
56
+ * Registry binding is a publisher change.
57
+ */
58
+ export declare const classifyPublisherBindingTransition: (args: {
59
+ readonly accepted: Option.Option<ExtensionRef>;
60
+ readonly proposed: RegistryBindingProposal;
61
+ }) => Option.Option<PublisherBindingTransition>;
62
+ /** The identity sentence a transition names in operator-facing wording. */
63
+ export declare const describePublisherBindingTransition: (transition: PublisherBindingTransition) => string;
64
+ /** The warning a step carries when its acceptance changes publisher. */
65
+ export declare const publisherTransitionWarning: (transition: PublisherBindingTransition) => string;
66
+ //# sourceMappingURL=publisher-binding.d.ts.map
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Publisher binding trust classification.
3
+ *
4
+ * A Registry resolution binds a publisher identity. When an operation
5
+ * proposes to replace an already accepted binding for the same configured
6
+ * extension with a different one, that transition is a trust decision a
7
+ * person makes at a prompt: no flag and no unattended mode can approve it.
8
+ * Every route that can replace an accepted binding — root and type update
9
+ * forms, reinstall and re-resolution, and pack closures — classifies the
10
+ * transition from the structured accepted and proposed identities here, so
11
+ * the same rule holds everywhere the transition can occur.
12
+ *
13
+ * A first acceptance is not a change, and an integrity mismatch while
14
+ * reacquiring the same accepted identity remains a hard failure elsewhere.
15
+ *
16
+ * Classification is all this module does: shaping the resulting plan risk
17
+ * condition belongs to the feature that owns the plan.
18
+ *
19
+ * @experimental This API is unstable and may change without notice.
20
+ * @packageDocumentation
21
+ */
22
+ import * as Option from "effect/Option";
23
+ import { toExtensionTypePlural } from "@agentxm/extension-model/unstable/extensions";
24
+ /** The configured (local) name a ref occupies in workspace settings. */
25
+ const configuredNameFromRef = (ref) => {
26
+ switch (ref.type) {
27
+ case "skill":
28
+ return ref.skill.name;
29
+ case "pack":
30
+ return ref.pack.name;
31
+ case "mcp-server":
32
+ return ref.server.name;
33
+ case "subagent":
34
+ return ref.subagent.name;
35
+ case "rule":
36
+ return ref.rule.name;
37
+ case "hook":
38
+ return ref.hook.name;
39
+ case "knowledge":
40
+ return ref.knowledge.name;
41
+ }
42
+ };
43
+ /** The structured proposal an extension ref makes, when it is a Registry ref. */
44
+ export const registryBindingProposal = (ref) => ref.refType === "registry"
45
+ ? {
46
+ extensionType: ref.type,
47
+ target: configuredNameFromRef(ref),
48
+ owner: ref.owner,
49
+ packageName: ref.name,
50
+ version: ref.version,
51
+ publisherBindingId: ref.publisherBindingId,
52
+ }
53
+ : undefined;
54
+ /**
55
+ * Classify one proposed acceptance against the accepted resolution for the
56
+ * same configured target. Only a Registry binding replacing a different
57
+ * Registry binding is a publisher change.
58
+ */
59
+ export const classifyPublisherBindingTransition = (args) => Option.flatMap(args.accepted, (accepted) => accepted.refType === "registry" &&
60
+ accepted.type === args.proposed.extensionType &&
61
+ accepted.publisherBindingId !== args.proposed.publisherBindingId
62
+ ? Option.some({
63
+ extensionType: args.proposed.extensionType,
64
+ target: args.proposed.target,
65
+ owner: args.proposed.owner,
66
+ packageName: args.proposed.packageName,
67
+ accepted: accepted.publisherBindingId,
68
+ proposed: args.proposed.publisherBindingId,
69
+ })
70
+ : Option.none());
71
+ /** The identity sentence a transition names in operator-facing wording. */
72
+ export const describePublisherBindingTransition = (transition) => `${transition.owner}/${toExtensionTypePlural(transition.extensionType)}/${transition.packageName}`;
73
+ /** The warning a step carries when its acceptance changes publisher. */
74
+ export const publisherTransitionWarning = (transition) => `Publisher identity changed (${transition.accepted} → ${transition.proposed}); confirm only if you trust the current publisher`;
75
+ //# sourceMappingURL=publisher-binding.js.map
@@ -0,0 +1,55 @@
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 "@agentxm/registry-protocol/unstable/registry/schema";
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 parseMinimumReleaseAge: (value: string) => Option.Option<Duration.Duration>;
36
+ /**
37
+ * Render a release-age window in the compact grammar the setting accepts, so
38
+ * output names the policy in the same units the reader would configure.
39
+ *
40
+ * Days are used only from two days up: the default window is written `24h`,
41
+ * and reporting it as `1d` would not match the setting the reader would edit.
42
+ * Falls back to whole seconds when the window is not a clean larger unit.
43
+ */
44
+ export declare const formatMinimumReleaseAgeSeconds: (seconds: number) => string;
45
+ export declare const isVersionEntryMature: (entry: VersionEntry, minimumAge: Duration.Duration) => Effect.Effect<boolean>;
46
+ export declare const isVersionEntryEligibleAt: (entry: VersionEntry, evaluation: ReleaseAgeEvaluation) => boolean;
47
+ export declare const releaseAgeEvidence: (entry: VersionEntry, evaluation: ReleaseAgeEvaluation) => ReleaseAgeEvidence;
48
+ export declare const filterMatureVersions: (versions: ReadonlyArray<VersionEntry>, minimumAge: Duration.Duration) => Effect.Effect<ReadonlyArray<VersionEntry>>;
49
+ export declare const releaseAgeHoldbackWarning: (args: {
50
+ readonly fqn: string;
51
+ readonly selectedVersion: string;
52
+ readonly heldVersion: string;
53
+ readonly minimumReleaseAge: string;
54
+ }) => string;
55
+ //# sourceMappingURL=release-age-policy.d.ts.map