@tangle-network/agent-interface 0.36.0 → 0.37.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.
@@ -1,26 +1,140 @@
1
+ import parseSpdxExpression from "spdx-expression-parse";
1
2
  import { z } from "zod";
2
- import { sha256DigestSchema } from "./agent-candidate-schema-common.js";
3
+ import { isWellFormedUnicode, looksLikeCredential, sha256DigestSchema, } from "./agent-candidate-schema-common.js";
3
4
  /** Metadata key that binds a measured improvement to its exact external source. */
4
5
  export const AGENT_IMPROVEMENT_SOURCE_METADATA_KEY = "agentImprovementSource";
6
+ function publicSourceTextSchema(maxLength, label) {
7
+ return z
8
+ .string()
9
+ .max(maxLength)
10
+ .refine(isWellFormedUnicode, `${label} must be well-formed Unicode`)
11
+ .refine((value) => !looksLikeCredential(value), `${label} cannot carry credentials`);
12
+ }
13
+ const sourceIdentitySchema = publicSourceTextSchema(256, "source identity")
14
+ .transform((value) => value.trim())
15
+ .pipe(z.string().min(1, "source identity cannot be blank"));
16
+ const sourceRevisionSchema = z.union([
17
+ publicSourceTextSchema(256, "source revision")
18
+ .transform((value) => value.trim())
19
+ .pipe(z.string().min(1, "source revision cannot be blank")),
20
+ z.number().int().nonnegative(),
21
+ ]);
22
+ const sourceStatementSchema = publicSourceTextSchema(16_384, "source statement").refine((value) => value.trim().length > 0, "statement cannot be blank");
23
+ const sourceStatementListSchema = z
24
+ .array(sourceStatementSchema)
25
+ .min(1)
26
+ .max(128)
27
+ .refine((values) => new Set(values).size === values.length, "statements must be unique");
28
+ function containsCustomLicenseReference(expression) {
29
+ if ("license" in expression) {
30
+ return (expression.license.startsWith("LicenseRef-") ||
31
+ expression.license.includes(":LicenseRef-"));
32
+ }
33
+ return (containsCustomLicenseReference(expression.left) ||
34
+ containsCustomLicenseReference(expression.right));
35
+ }
36
+ /** License terms retained with an imported source. */
37
+ export const agentSourceLicenseSchema = z.discriminatedUnion("kind", [
38
+ z
39
+ .object({
40
+ kind: z.literal("spdx"),
41
+ expression: z
42
+ .string()
43
+ .trim()
44
+ .min(1)
45
+ .max(1_024)
46
+ .refine((expression) => {
47
+ try {
48
+ return !containsCustomLicenseReference(parseSpdxExpression(expression));
49
+ }
50
+ catch {
51
+ return false;
52
+ }
53
+ }, "license expression must use standard SPDX identifiers; content-pin custom terms with kind=custom"),
54
+ })
55
+ .strict(),
56
+ z
57
+ .object({
58
+ kind: z.literal("custom"),
59
+ name: sourceIdentitySchema,
60
+ /** URL or repository-relative path from which the exact terms can be recovered. */
61
+ reference: publicSourceTextSchema(2_048, "license reference")
62
+ .transform((value) => value.trim())
63
+ .pipe(z.string().min(1, "license reference cannot be blank")),
64
+ /** Digest of the custom license text, not of its URL or file name. */
65
+ termsDigest: sha256DigestSchema,
66
+ })
67
+ .strict(),
68
+ ]);
69
+ /**
70
+ * One content transition applied while importing a source.
71
+ *
72
+ * `identity` and `revision` pin the transformer. Input/output digests make the
73
+ * ordered chain auditable without embedding executable transformation code.
74
+ * `procedureDigest` pins the implementation and non-secret configuration used
75
+ * for that step.
76
+ */
77
+ export const agentSourceTransformationSchema = z
78
+ .object({
79
+ kind: z.enum(["normalization", "transformation"]),
80
+ identity: sourceIdentitySchema,
81
+ revision: sourceRevisionSchema,
82
+ procedureDigest: sha256DigestSchema,
83
+ inputDigest: sha256DigestSchema,
84
+ outputDigest: sha256DigestSchema,
85
+ })
86
+ .strict();
5
87
  /**
6
88
  * Stable reference for the state from which an improvement candidate was made.
7
89
  * `sourceIdentity` identifies the provider object; `sourceDigest` is the exact
8
- * measured source state; `sourceRevision` is an
90
+ * measured source state consumed by the candidate; `sourceRevision` is an
9
91
  * opaque provider version retained to make stale-source errors intelligible to
10
92
  * callers. A revision may be numeric or textual because providers use both
11
93
  * counters and immutable revision identifiers.
94
+ *
95
+ * Optional license, attribution, and notice fields preserve obligations when a
96
+ * public source becomes an improvement input. When transformations are
97
+ * present, their first input pins the fetched source bytes, every adjacent
98
+ * digest must join, and the final output must equal `sourceDigest`.
12
99
  */
13
100
  export const agentImprovementSourceSchema = z
14
101
  .object({
15
102
  kind: z.string().trim().min(1).max(100).regex(/^[a-z][a-z0-9-]*$/),
16
- sourceIdentity: z.string().trim().min(1).max(256),
103
+ sourceIdentity: sourceIdentitySchema,
17
104
  sourceDigest: sha256DigestSchema,
18
- sourceRevision: z.union([
19
- z.string().trim().min(1).max(256),
20
- z.number().int().nonnegative(),
21
- ]),
105
+ sourceRevision: sourceRevisionSchema,
106
+ license: agentSourceLicenseSchema.optional(),
107
+ attribution: sourceStatementListSchema.optional(),
108
+ notices: sourceStatementListSchema.optional(),
109
+ transformations: z
110
+ .array(agentSourceTransformationSchema)
111
+ .min(1)
112
+ .max(128)
113
+ .optional(),
22
114
  })
23
- .strict();
115
+ .strict()
116
+ .superRefine((source, ctx) => {
117
+ const transformations = source.transformations;
118
+ if (transformations === undefined)
119
+ return;
120
+ for (let index = 1; index < transformations.length; index++) {
121
+ if (transformations[index]?.inputDigest !==
122
+ transformations[index - 1]?.outputDigest) {
123
+ ctx.addIssue({
124
+ code: "custom",
125
+ path: ["transformations", index, "inputDigest"],
126
+ message: "transformation input must equal the previous output",
127
+ });
128
+ }
129
+ }
130
+ if (transformations.at(-1)?.outputDigest !== source.sourceDigest) {
131
+ ctx.addIssue({
132
+ code: "custom",
133
+ path: ["sourceDigest"],
134
+ message: "source digest must equal the final transformation output",
135
+ });
136
+ }
137
+ });
24
138
  /** Attach one validated source reference to signed improvement metadata. */
25
139
  export function agentImprovementSourceMetadata(source) {
26
140
  return {
@@ -195,9 +195,31 @@ export declare const agentProfileImprovementExperimentSchema: z.ZodObject<{
195
195
  digestAlgorithm: z.ZodLiteral<"rfc8785-sha256">;
196
196
  source: z.ZodObject<{
197
197
  kind: z.ZodString;
198
- sourceIdentity: z.ZodString;
198
+ sourceIdentity: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
199
199
  sourceDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
200
- sourceRevision: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
200
+ sourceRevision: z.ZodUnion<readonly [z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>, z.ZodNumber]>;
201
+ license: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
202
+ kind: z.ZodLiteral<"spdx">;
203
+ expression: z.ZodString;
204
+ }, z.core.$strict>, z.ZodObject<{
205
+ kind: z.ZodLiteral<"custom">;
206
+ name: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
207
+ reference: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
208
+ termsDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
209
+ }, z.core.$strict>], "kind">>;
210
+ attribution: z.ZodOptional<z.ZodArray<z.ZodString>>;
211
+ notices: z.ZodOptional<z.ZodArray<z.ZodString>>;
212
+ transformations: z.ZodOptional<z.ZodArray<z.ZodObject<{
213
+ kind: z.ZodEnum<{
214
+ normalization: "normalization";
215
+ transformation: "transformation";
216
+ }>;
217
+ identity: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
218
+ revision: z.ZodUnion<readonly [z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>, z.ZodNumber]>;
219
+ procedureDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
220
+ inputDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
221
+ outputDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
222
+ }, z.core.$strict>>>;
201
223
  }, z.core.$strict>;
202
224
  executionRef: z.ZodObject<{
203
225
  identity: z.ZodString;
@@ -702,9 +724,31 @@ export declare const agentProfileImprovementMeasuredComparisonSchema: z.ZodObjec
702
724
  digestAlgorithm: z.ZodLiteral<"rfc8785-sha256">;
703
725
  source: z.ZodObject<{
704
726
  kind: z.ZodString;
705
- sourceIdentity: z.ZodString;
727
+ sourceIdentity: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
706
728
  sourceDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
707
- sourceRevision: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
729
+ sourceRevision: z.ZodUnion<readonly [z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>, z.ZodNumber]>;
730
+ license: z.ZodOptional<z.ZodDiscriminatedUnion<[z.ZodObject<{
731
+ kind: z.ZodLiteral<"spdx">;
732
+ expression: z.ZodString;
733
+ }, z.core.$strict>, z.ZodObject<{
734
+ kind: z.ZodLiteral<"custom">;
735
+ name: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
736
+ reference: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
737
+ termsDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
738
+ }, z.core.$strict>], "kind">>;
739
+ attribution: z.ZodOptional<z.ZodArray<z.ZodString>>;
740
+ notices: z.ZodOptional<z.ZodArray<z.ZodString>>;
741
+ transformations: z.ZodOptional<z.ZodArray<z.ZodObject<{
742
+ kind: z.ZodEnum<{
743
+ normalization: "normalization";
744
+ transformation: "transformation";
745
+ }>;
746
+ identity: z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>;
747
+ revision: z.ZodUnion<readonly [z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodString>, z.ZodNumber]>;
748
+ procedureDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
749
+ inputDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
750
+ outputDigest: z.ZodType<`sha256:${string}`, unknown, z.core.$ZodTypeInternals<`sha256:${string}`, unknown>>;
751
+ }, z.core.$strict>>>;
708
752
  }, z.core.$strict>;
709
753
  executionRef: z.ZodObject<{
710
754
  identity: z.ZodString;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/agent-interface",
3
- "version": "0.36.0",
3
+ "version": "0.37.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "license": "MIT",
@@ -34,10 +34,12 @@
34
34
  ],
35
35
  "dependencies": {
36
36
  "@noble/hashes": "1.8.0",
37
+ "spdx-expression-parse": "5.0.0",
37
38
  "zod": "4.4.3"
38
39
  },
39
40
  "devDependencies": {
40
41
  "@types/node": "25.6.0",
42
+ "@types/spdx-expression-parse": "4.0.0",
41
43
  "typescript": "^6.0.3",
42
44
  "vitest": "^4.1.5"
43
45
  },