@tangle-network/agent-interface 5.9.0 → 5.10.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.
- package/README.md +2 -0
- package/dist/agent-profile-canonical.d.ts +14 -0
- package/dist/agent-profile-canonical.js +52 -51
- package/dist/agent-profile-snapshot.js +1 -1
- package/dist/contract-limits.d.ts +6 -13
- package/dist/contract-limits.js +62 -208
- package/dist/own-property-record.d.ts +4 -1
- package/dist/own-property-record.js +44 -15
- package/dist/profile-schema.js +56 -31
- package/dist/proxy-secrets.js +2 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -424,3 +424,5 @@ must validate source ownership and active generation bindings for its own runtim
|
|
|
424
424
|
before admitting a knowledge tool. A profile copied to another owner does not
|
|
425
425
|
confer access. Hosts unable to resolve the saved pins must fail closed rather than
|
|
426
426
|
inline JSON into instructions, substitute a newer generation or silently omit them.
|
|
427
|
+
|
|
428
|
+
Profile collection admission uses the shared [contract limits](src/contract-limits.ts) before parsing elements or encoding record keys. Training receipts and knowledge references retain their own typed limits in [the profile schema](src/profile-schema.ts). Opaque metadata is admitted and detached by value as bounded portable data; accessors are refused and cross-key object identity is not preserved. Snapshot validation precedes the final detached copy, preserving explicit undefined fields, canonical hashes and nested isolation. The generic serialized-JSON byte bound is not a whole-profile transport limit.
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { AgentCandidateJsonValue } from "./agent-candidate.js";
|
|
2
|
+
type ProfilePath = readonly (string | number)[];
|
|
3
|
+
/** Why a value cannot be AgentProfile JSON, with the path of the offending entry. */
|
|
4
|
+
export declare class AgentProfileValueError extends Error {
|
|
5
|
+
readonly path: ProfilePath;
|
|
6
|
+
constructor(path: ProfilePath, reason: string);
|
|
7
|
+
}
|
|
2
8
|
/**
|
|
3
9
|
* Normalize profile-shaped data into the canonical JSON domain used for public
|
|
4
10
|
* profile identity. Undefined object entries are omitted; unsupported values,
|
|
@@ -8,5 +14,13 @@ import type { AgentCandidateJsonValue } from "./agent-candidate.js";
|
|
|
8
14
|
* profile-level operations that own validation, identity, or diff semantics.
|
|
9
15
|
*/
|
|
10
16
|
export declare function canonicalAgentProfileValue(value: unknown): AgentCandidateJsonValue | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Throw the path-specific reason a value refused by the bounded admission walker is
|
|
19
|
+
* not profile JSON. Reads no accessor and throws at the contract node and depth
|
|
20
|
+
* bounds, so hostile input stays cheap; returns only when the refusal was a bound
|
|
21
|
+
* this walker does not model, such as string or entry length.
|
|
22
|
+
*/
|
|
23
|
+
export declare function assertAgentProfileValue(value: unknown, path: ProfilePath): void;
|
|
11
24
|
/** Canonical serialization after applying AgentProfile normalization rules. */
|
|
12
25
|
export declare function canonicalAgentProfileJson(value: unknown): string | undefined;
|
|
26
|
+
export {};
|
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { canonicalCandidateJson, isWellFormedUnicode, } from "./agent-candidate-schema-common.js";
|
|
2
|
+
import { CONTRACT_MAX_JSON_DEPTH, CONTRACT_MAX_JSON_NODES } from "./contract-limits.js";
|
|
3
|
+
/** Why a value cannot be AgentProfile JSON, with the path of the offending entry. */
|
|
4
|
+
export class AgentProfileValueError extends Error {
|
|
5
|
+
path;
|
|
6
|
+
constructor(path, reason) {
|
|
7
|
+
super(`AgentProfile ${renderPath(path)} ${reason}`);
|
|
8
|
+
this.path = path;
|
|
9
|
+
this.name = "AgentProfileValueError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
2
12
|
/**
|
|
3
13
|
* Normalize profile-shaped data into the canonical JSON domain used for public
|
|
4
14
|
* profile identity. Undefined object entries are omitted; unsupported values,
|
|
@@ -10,66 +20,57 @@ import { canonicalCandidateJson, isWellFormedUnicode, } from "./agent-candidate-
|
|
|
10
20
|
export function canonicalAgentProfileValue(value) {
|
|
11
21
|
return normalizeAgentProfileValue(value, [], new Set());
|
|
12
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Throw the path-specific reason a value refused by the bounded admission walker is
|
|
25
|
+
* not profile JSON. Reads no accessor and throws at the contract node and depth
|
|
26
|
+
* bounds, so hostile input stays cheap; returns only when the refusal was a bound
|
|
27
|
+
* this walker does not model, such as string or entry length.
|
|
28
|
+
*/
|
|
29
|
+
export function assertAgentProfileValue(value, path) {
|
|
30
|
+
normalizeAgentProfileValue(value, path, new Set(), { nodes: CONTRACT_MAX_JSON_NODES, base: path.length });
|
|
31
|
+
}
|
|
13
32
|
/** Canonical serialization after applying AgentProfile normalization rules. */
|
|
14
33
|
export function canonicalAgentProfileJson(value) {
|
|
15
34
|
const normalized = canonicalAgentProfileValue(value);
|
|
16
|
-
return normalized === undefined
|
|
17
|
-
? undefined
|
|
18
|
-
: canonicalCandidateJson(normalized);
|
|
35
|
+
return normalized === undefined ? undefined : canonicalCandidateJson(normalized);
|
|
19
36
|
}
|
|
20
|
-
function normalizeAgentProfileValue(value, path, ancestors) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
typeof value === "string") {
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
if (typeof value === "number") {
|
|
29
|
-
if (!Number.isFinite(value)) {
|
|
30
|
-
throw new Error(`AgentProfile ${renderPath(path)} must be finite`);
|
|
31
|
-
}
|
|
37
|
+
function normalizeAgentProfileValue(value, path, ancestors, bounds) {
|
|
38
|
+
const refuse = (reason, at = path) => { throw new AgentProfileValueError(at, reason); };
|
|
39
|
+
if (bounds && --bounds.nodes < 0)
|
|
40
|
+
refuse("exceeds the contract node bound");
|
|
41
|
+
if (value === undefined || value === null || typeof value === "boolean" || typeof value === "string")
|
|
32
42
|
return value;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if (ancestors.has(value))
|
|
38
|
-
|
|
39
|
-
}
|
|
43
|
+
if (typeof value === "number")
|
|
44
|
+
return Number.isFinite(value) ? value : refuse("must be finite");
|
|
45
|
+
if (typeof value !== "object")
|
|
46
|
+
return refuse("is not JSON serializable");
|
|
47
|
+
if (ancestors.has(value))
|
|
48
|
+
refuse("must be acyclic");
|
|
40
49
|
const prototype = Object.getPrototypeOf(value);
|
|
41
|
-
if (!Array.isArray(value) &&
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
50
|
+
if (!Array.isArray(value) && prototype !== Object.prototype && prototype !== null)
|
|
51
|
+
refuse("must be a plain JSON object");
|
|
52
|
+
if (bounds && path.length - bounds.base >= CONTRACT_MAX_JSON_DEPTH)
|
|
53
|
+
refuse("exceeds the contract depth");
|
|
46
54
|
const nextAncestors = new Set(ancestors).add(value);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
59
|
-
}
|
|
55
|
+
// Entries are read through own data descriptors so refusing a value never runs an accessor.
|
|
56
|
+
const entry = (key) => {
|
|
57
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
58
|
+
if (!descriptor)
|
|
59
|
+
return refuse("cannot be a sparse array hole", [...path, key]);
|
|
60
|
+
if (!("value" in descriptor))
|
|
61
|
+
return refuse("must be a data property", [...path, key]);
|
|
62
|
+
const normalized = normalizeAgentProfileValue(descriptor.value, [...path, key], nextAncestors, bounds);
|
|
63
|
+
return normalized === undefined && typeof key === "number" ? refuse("cannot be undefined", [...path, key]) : normalized;
|
|
64
|
+
};
|
|
65
|
+
if (Array.isArray(value))
|
|
66
|
+
return Array.from({ length: value.length }, (_, index) => entry(index));
|
|
60
67
|
const material = Object.create(null);
|
|
61
|
-
for (const
|
|
62
|
-
if (!isWellFormedUnicode(key))
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const normalized = normalizeAgentProfileValue(entry, [...path, key], nextAncestors);
|
|
68
|
+
for (const key of Object.keys(value)) {
|
|
69
|
+
if (!isWellFormedUnicode(key))
|
|
70
|
+
refuse("has a record key that is not valid Unicode");
|
|
71
|
+
const normalized = entry(key);
|
|
66
72
|
if (normalized !== undefined) {
|
|
67
|
-
Object.defineProperty(material, key, {
|
|
68
|
-
value: normalized,
|
|
69
|
-
enumerable: true,
|
|
70
|
-
configurable: true,
|
|
71
|
-
writable: true,
|
|
72
|
-
});
|
|
73
|
+
Object.defineProperty(material, key, { value: normalized, enumerable: true, configurable: true, writable: true });
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
return material;
|
|
@@ -9,7 +9,7 @@ import { agentProfileSchema } from "./profile-schema.js";
|
|
|
9
9
|
* existing canonical profile JSON domain fail instead of becoming mutable state.
|
|
10
10
|
*/
|
|
11
11
|
export function snapshotAgentProfile(value) {
|
|
12
|
-
const parsed = agentProfileSchema.parse(
|
|
12
|
+
const parsed = structuredClone(agentProfileSchema.parse(value));
|
|
13
13
|
canonicalAgentProfileValue(parsed);
|
|
14
14
|
return deepFreeze(parsed);
|
|
15
15
|
}
|
|
@@ -12,23 +12,16 @@ export declare const CONTRACT_MAX_JSON_NODES = 8192;
|
|
|
12
12
|
export declare const boundedStringSchema: z.ZodString;
|
|
13
13
|
export declare const confidentialAttestationQuoteSchema: z.ZodString;
|
|
14
14
|
export declare const boundedIdentifierSchema: z.ZodString;
|
|
15
|
-
/**
|
|
16
|
-
* Validate JSON without asking Zod to recursively copy an attacker-sized
|
|
17
|
-
* value. This is intentionally iterative so depth and collection limits are
|
|
18
|
-
* checked before canonical serialization.
|
|
19
|
-
*/
|
|
15
|
+
/** Admit ordinary JSON before recursive parsing, copying or hashing. */
|
|
20
16
|
export declare function isBoundedJsonValue(value: unknown): boolean;
|
|
21
|
-
/**
|
|
22
|
-
* Validate provider event and terminal content. Unlike ordinary metadata,
|
|
23
|
-
* content may contain a single large transcript or tool result. It remains
|
|
24
|
-
* finite, plain JSON with the normal structural limits, and its complete JSON
|
|
25
|
-
* representation is capped exactly by UTF-8 bytes.
|
|
26
|
-
*/
|
|
17
|
+
/** Event content permits large strings within the exact serialized UTF-8 budget. */
|
|
27
18
|
export declare function isBoundedEventContentJson(value: unknown, { omitUndefinedObjectFields }?: {
|
|
28
19
|
omitUndefinedObjectFields?: boolean;
|
|
29
20
|
}): boolean;
|
|
30
|
-
/**
|
|
31
|
-
export declare function isBoundedJsonMaterial(value: unknown
|
|
21
|
+
/** Digest material omits undefined object fields; portable profile values require data properties. */
|
|
22
|
+
export declare function isBoundedJsonMaterial(value: unknown, { dataPropertiesOnly }?: {
|
|
23
|
+
dataPropertiesOnly?: boolean;
|
|
24
|
+
}): boolean;
|
|
32
25
|
export declare const boundedJsonSchema: z.ZodCustom<unknown, unknown>;
|
|
33
26
|
export declare const boundedJsonRecordSchema: z.ZodCustom<Record<string, unknown>, Record<string, unknown>>;
|
|
34
27
|
/** One provider event payload or terminal response, bounded as serialized UTF-8 JSON. */
|
package/dist/contract-limits.js
CHANGED
|
@@ -17,72 +17,9 @@ export const boundedIdentifierSchema = boundedStringSchema
|
|
|
17
17
|
.min(1)
|
|
18
18
|
.max(CONTRACT_MAX_IDENTIFIER_LENGTH)
|
|
19
19
|
.refine((value) => value.trim() === value, "identifier cannot have outer whitespace");
|
|
20
|
-
/**
|
|
21
|
-
* Validate JSON without asking Zod to recursively copy an attacker-sized
|
|
22
|
-
* value. This is intentionally iterative so depth and collection limits are
|
|
23
|
-
* checked before canonical serialization.
|
|
24
|
-
*/
|
|
20
|
+
/** Admit ordinary JSON before recursive parsing, copying or hashing. */
|
|
25
21
|
export function isBoundedJsonValue(value) {
|
|
26
|
-
|
|
27
|
-
const ancestors = new Set();
|
|
28
|
-
let nodes = 0;
|
|
29
|
-
while (pending.length > 0) {
|
|
30
|
-
const item = pending.pop();
|
|
31
|
-
if (!item)
|
|
32
|
-
continue;
|
|
33
|
-
nodes += 1;
|
|
34
|
-
if (nodes > CONTRACT_MAX_JSON_NODES)
|
|
35
|
-
return false;
|
|
36
|
-
const current = item.value;
|
|
37
|
-
if (item.leave) {
|
|
38
|
-
ancestors.delete(current);
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
if (current === undefined)
|
|
42
|
-
return false;
|
|
43
|
-
if (current === null || typeof current === "boolean")
|
|
44
|
-
continue;
|
|
45
|
-
if (typeof current === "string") {
|
|
46
|
-
if (current.length > CONTRACT_MAX_STRING_LENGTH)
|
|
47
|
-
return false;
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
if (typeof current === "number") {
|
|
51
|
-
if (!Number.isFinite(current))
|
|
52
|
-
return false;
|
|
53
|
-
continue;
|
|
54
|
-
}
|
|
55
|
-
if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
if (ancestors.has(current))
|
|
59
|
-
return false;
|
|
60
|
-
ancestors.add(current);
|
|
61
|
-
pending.push({ value: current, depth: item.depth, leave: true });
|
|
62
|
-
if (Array.isArray(current)) {
|
|
63
|
-
if (current.length > CONTRACT_MAX_ARRAY_LENGTH)
|
|
64
|
-
return false;
|
|
65
|
-
for (const entry of current) {
|
|
66
|
-
pending.push({ value: entry, depth: item.depth + 1 });
|
|
67
|
-
}
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
const prototype = Object.getPrototypeOf(current);
|
|
71
|
-
if (prototype !== Object.prototype && prototype !== null)
|
|
72
|
-
return false;
|
|
73
|
-
const keys = Object.keys(current);
|
|
74
|
-
if (keys.length > CONTRACT_MAX_MAP_ENTRIES)
|
|
75
|
-
return false;
|
|
76
|
-
for (const key of keys) {
|
|
77
|
-
if (key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
|
|
78
|
-
return false;
|
|
79
|
-
pending.push({
|
|
80
|
-
value: current[key],
|
|
81
|
-
depth: item.depth + 1,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return true;
|
|
22
|
+
return boundedJson(value);
|
|
86
23
|
}
|
|
87
24
|
/** Exact serialized JSON UTF-8 byte count for one string scalar. */
|
|
88
25
|
function serializedJsonStringBytes(value) {
|
|
@@ -122,187 +59,104 @@ function serializedJsonStringBytes(value) {
|
|
|
122
59
|
}
|
|
123
60
|
return bytes;
|
|
124
61
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Validate provider event and terminal content. Unlike ordinary metadata,
|
|
127
|
-
* content may contain a single large transcript or tool result. It remains
|
|
128
|
-
* finite, plain JSON with the normal structural limits, and its complete JSON
|
|
129
|
-
* representation is capped exactly by UTF-8 bytes.
|
|
130
|
-
*/
|
|
62
|
+
/** Event content permits large strings within the exact serialized UTF-8 budget. */
|
|
131
63
|
export function isBoundedEventContentJson(value, { omitUndefinedObjectFields = false } = {}) {
|
|
64
|
+
return boundedJson(value, { content: true, omitUndefinedObjectFields });
|
|
65
|
+
}
|
|
66
|
+
/** Digest material omits undefined object fields; portable profile values require data properties. */
|
|
67
|
+
export function isBoundedJsonMaterial(value, { dataPropertiesOnly = false } = {}) {
|
|
68
|
+
return boundedJson(value, { material: true, omitUndefinedObjectFields: true, dataPropertiesOnly });
|
|
69
|
+
}
|
|
70
|
+
/** One iterative walker owns structural admission; typed domains keep their own schema limits. */
|
|
71
|
+
function boundedJson(value, options = {}) {
|
|
132
72
|
const pending = [
|
|
133
73
|
{ value, depth: 0 },
|
|
134
74
|
];
|
|
135
75
|
const ancestors = new Set();
|
|
136
76
|
let nodes = 0;
|
|
137
77
|
let bytes = 0;
|
|
138
|
-
const addBytes = (
|
|
139
|
-
|
|
140
|
-
return bytes <= CONTRACT_MAX_JSON_BYTES;
|
|
141
|
-
};
|
|
142
|
-
while (pending.length > 0) {
|
|
78
|
+
const addBytes = (count) => !options.content || (bytes += count) <= CONTRACT_MAX_JSON_BYTES;
|
|
79
|
+
while (pending.length) {
|
|
143
80
|
const item = pending.pop();
|
|
144
|
-
if (!item)
|
|
145
|
-
continue;
|
|
146
81
|
const current = item.value;
|
|
147
|
-
if (
|
|
148
|
-
continue;
|
|
149
|
-
nodes += 1;
|
|
150
|
-
if (nodes > CONTRACT_MAX_JSON_NODES)
|
|
82
|
+
if ((!item.leave || !options.material) && ++nodes > CONTRACT_MAX_JSON_NODES)
|
|
151
83
|
return false;
|
|
84
|
+
if (current === undefined && item.omitUndefined)
|
|
85
|
+
continue;
|
|
152
86
|
if (item.leave) {
|
|
153
87
|
ancestors.delete(current);
|
|
154
88
|
continue;
|
|
155
89
|
}
|
|
156
|
-
if (current === null) {
|
|
157
|
-
if (!addBytes(4))
|
|
158
|
-
return false;
|
|
159
|
-
continue;
|
|
160
|
-
}
|
|
161
|
-
if (typeof current === "boolean") {
|
|
162
|
-
if (!addBytes(current ? 4 : 5))
|
|
90
|
+
if (current === null || typeof current === "boolean") {
|
|
91
|
+
if (!addBytes(current === null ? 4 : current ? 4 : 5))
|
|
163
92
|
return false;
|
|
164
93
|
continue;
|
|
165
94
|
}
|
|
166
95
|
if (typeof current === "string") {
|
|
167
|
-
if (!addBytes(serializedJsonStringBytes(current)))
|
|
96
|
+
if (options.content ? !addBytes(serializedJsonStringBytes(current)) : current.length > CONTRACT_MAX_STRING_LENGTH)
|
|
168
97
|
return false;
|
|
169
98
|
continue;
|
|
170
99
|
}
|
|
171
100
|
if (typeof current === "number") {
|
|
172
|
-
if (!Number.isFinite(current))
|
|
173
|
-
return false;
|
|
174
|
-
if (!addBytes(JSON.stringify(current).length))
|
|
101
|
+
if (!Number.isFinite(current) || !addBytes(JSON.stringify(current).length))
|
|
175
102
|
return false;
|
|
176
103
|
continue;
|
|
177
104
|
}
|
|
178
|
-
if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH)
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
if (ancestors.has(current))
|
|
105
|
+
if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH || ancestors.has(current))
|
|
182
106
|
return false;
|
|
183
107
|
ancestors.add(current);
|
|
184
108
|
pending.push({ value: current, depth: item.depth, leave: true });
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
const keys = Reflect.ownKeys(current);
|
|
194
|
-
|
|
195
|
-
if (entryKeys.length !== current.length)
|
|
196
|
-
return false;
|
|
197
|
-
if (entryKeys.some((key) => {
|
|
198
|
-
if (typeof key !== "string" || !/^(?:0|[1-9][0-9]*)$/u.test(key))
|
|
199
|
-
return true;
|
|
200
|
-
const index = Number(key);
|
|
201
|
-
return !Number.isSafeInteger(index) || index < 0 || index >= current.length || index >= 4_294_967_295;
|
|
202
|
-
})) {
|
|
109
|
+
const array = Array.isArray(current);
|
|
110
|
+
if (array && current.length > CONTRACT_MAX_ARRAY_LENGTH)
|
|
111
|
+
return false;
|
|
112
|
+
const prototype = Object.getPrototypeOf(current);
|
|
113
|
+
if ((!array && prototype !== Object.prototype && prototype !== null) ||
|
|
114
|
+
(array && options.content && prototype !== Array.prototype && prototype !== null))
|
|
115
|
+
return false;
|
|
116
|
+
if (options.content || options.dataPropertiesOnly) {
|
|
117
|
+
const keys = Reflect.ownKeys(current).filter((key) => !array || key !== "length");
|
|
118
|
+
if (array ? keys.length !== current.length : keys.length > CONTRACT_MAX_MAP_ENTRIES)
|
|
203
119
|
return false;
|
|
204
|
-
|
|
205
|
-
for (const key of
|
|
120
|
+
let entries = 0;
|
|
121
|
+
for (const key of keys) {
|
|
122
|
+
if (typeof key !== "string" || (!array && key.length > CONTRACT_MAX_IDENTIFIER_LENGTH))
|
|
123
|
+
return false;
|
|
124
|
+
if (array && (!/^(?:0|[1-9][0-9]*)$/u.test(key) || Number(key) >= current.length || Number(key) >= 4_294_967_295))
|
|
125
|
+
return false;
|
|
206
126
|
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
207
|
-
if (
|
|
127
|
+
if (!descriptor?.enumerable || !("value" in descriptor))
|
|
128
|
+
return false;
|
|
129
|
+
if (!array && descriptor.value === undefined && options.omitUndefinedObjectFields) {
|
|
130
|
+
if (++nodes > CONTRACT_MAX_JSON_NODES)
|
|
131
|
+
return false;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (++entries > (array ? CONTRACT_MAX_ARRAY_LENGTH : CONTRACT_MAX_MAP_ENTRIES))
|
|
135
|
+
return false;
|
|
136
|
+
if (!array && !addBytes(serializedJsonStringBytes(key) + 1))
|
|
208
137
|
return false;
|
|
209
138
|
pending.push({ value: descriptor.value, depth: item.depth + 1 });
|
|
210
139
|
}
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
const prototype = Object.getPrototypeOf(current);
|
|
214
|
-
if (prototype !== Object.prototype && prototype !== null)
|
|
215
|
-
return false;
|
|
216
|
-
const entries = [];
|
|
217
|
-
for (const key of Reflect.ownKeys(current)) {
|
|
218
|
-
if (typeof key !== "string" || key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
|
|
219
|
-
return false;
|
|
220
|
-
const descriptor = Object.getOwnPropertyDescriptor(current, key);
|
|
221
|
-
if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
|
|
222
|
-
return false;
|
|
223
|
-
if (descriptor.value === undefined && omitUndefinedObjectFields)
|
|
224
|
-
continue;
|
|
225
|
-
entries.push([key, descriptor]);
|
|
226
|
-
if (entries.length > CONTRACT_MAX_MAP_ENTRIES)
|
|
140
|
+
if (!addBytes(2 + Math.max(entries - 1, 0)))
|
|
227
141
|
return false;
|
|
228
142
|
}
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
for (const [key, descriptor] of entries) {
|
|
232
|
-
if (!addBytes(serializedJsonStringBytes(key) + 1))
|
|
233
|
-
return false;
|
|
234
|
-
pending.push({
|
|
235
|
-
value: descriptor.value,
|
|
236
|
-
depth: item.depth + 1,
|
|
237
|
-
omitUndefined: omitUndefinedObjectFields,
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return true;
|
|
242
|
-
}
|
|
243
|
-
/** Validate digest input while matching JSON's omission of undefined object fields. */
|
|
244
|
-
export function isBoundedJsonMaterial(value) {
|
|
245
|
-
const pending = [{ value, depth: 0 }];
|
|
246
|
-
const ancestors = new Set();
|
|
247
|
-
let nodes = 0;
|
|
248
|
-
while (pending.length > 0) {
|
|
249
|
-
const item = pending.pop();
|
|
250
|
-
if (!item)
|
|
251
|
-
continue;
|
|
252
|
-
if (item.leave) {
|
|
253
|
-
ancestors.delete(item.value);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (item.value === undefined) {
|
|
257
|
-
if (item.omitUndefined === true)
|
|
258
|
-
continue;
|
|
259
|
-
return false;
|
|
260
|
-
}
|
|
261
|
-
nodes += 1;
|
|
262
|
-
if (nodes > CONTRACT_MAX_JSON_NODES)
|
|
263
|
-
return false;
|
|
264
|
-
const current = item.value;
|
|
265
|
-
if (current === null || typeof current === "boolean")
|
|
266
|
-
continue;
|
|
267
|
-
if (typeof current === "string") {
|
|
268
|
-
if (current.length > CONTRACT_MAX_STRING_LENGTH)
|
|
269
|
-
return false;
|
|
270
|
-
continue;
|
|
271
|
-
}
|
|
272
|
-
if (typeof current === "number") {
|
|
273
|
-
if (!Number.isFinite(current))
|
|
274
|
-
return false;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
if (typeof current !== "object" || item.depth >= CONTRACT_MAX_JSON_DEPTH) {
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
if (ancestors.has(current))
|
|
281
|
-
return false;
|
|
282
|
-
ancestors.add(current);
|
|
283
|
-
pending.push({ value: current, depth: item.depth, leave: true });
|
|
284
|
-
if (Array.isArray(current)) {
|
|
285
|
-
if (current.length > CONTRACT_MAX_ARRAY_LENGTH)
|
|
286
|
-
return false;
|
|
287
|
-
for (const entry of current) {
|
|
143
|
+
else if (array) {
|
|
144
|
+
for (const entry of current)
|
|
288
145
|
pending.push({ value: entry, depth: item.depth + 1 });
|
|
289
|
-
}
|
|
290
|
-
continue;
|
|
291
146
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const keys = Object.keys(current);
|
|
296
|
-
if (keys.length > CONTRACT_MAX_MAP_ENTRIES)
|
|
297
|
-
return false;
|
|
298
|
-
for (const key of keys) {
|
|
299
|
-
if (key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
|
|
147
|
+
else {
|
|
148
|
+
const keys = Object.keys(current);
|
|
149
|
+
if (keys.length > CONTRACT_MAX_MAP_ENTRIES)
|
|
300
150
|
return false;
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
151
|
+
for (const key of keys) {
|
|
152
|
+
if (key.length > CONTRACT_MAX_IDENTIFIER_LENGTH)
|
|
153
|
+
return false;
|
|
154
|
+
pending.push({
|
|
155
|
+
value: current[key],
|
|
156
|
+
depth: item.depth + 1,
|
|
157
|
+
omitUndefined: options.omitUndefinedObjectFields,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
306
160
|
}
|
|
307
161
|
}
|
|
308
162
|
return true;
|
|
@@ -6,4 +6,7 @@ import { z } from "zod";
|
|
|
6
6
|
* sees it, then restore the exact UTF-16 key with defineProperty so all string
|
|
7
7
|
* keys allowed by `Record<string, ...>` remain data rather than object syntax.
|
|
8
8
|
*/
|
|
9
|
-
export declare function ownPropertyRecordSchema<ValueSchema extends z.ZodType>(valueSchema: ValueSchema
|
|
9
|
+
export declare function ownPropertyRecordSchema<ValueSchema extends z.ZodType>(valueSchema: ValueSchema, { maxEntries, prepareValue }?: {
|
|
10
|
+
maxEntries?: number;
|
|
11
|
+
prepareValue?: (key: string, value: unknown) => unknown;
|
|
12
|
+
}): z.ZodType<Record<string, z.output<ValueSchema>>>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { CONTRACT_MAX_IDENTIFIER_LENGTH, CONTRACT_MAX_MAP_ENTRIES } from "./contract-limits.js";
|
|
2
3
|
import { isWellFormedUnicode } from "./agent-candidate-schema-common.js";
|
|
4
|
+
import { AgentProfileValueError } from "./agent-profile-canonical.js";
|
|
3
5
|
/**
|
|
4
6
|
* Zod records currently assign parsed values onto an ordinary object. The
|
|
5
7
|
* special own key `__proto__` therefore invokes the legacy prototype setter
|
|
@@ -7,8 +9,48 @@ import { isWellFormedUnicode } from "./agent-candidate-schema-common.js";
|
|
|
7
9
|
* sees it, then restore the exact UTF-16 key with defineProperty so all string
|
|
8
10
|
* keys allowed by `Record<string, ...>` remain data rather than object syntax.
|
|
9
11
|
*/
|
|
10
|
-
export function ownPropertyRecordSchema(valueSchema) {
|
|
11
|
-
return z.preprocess(
|
|
12
|
+
export function ownPropertyRecordSchema(valueSchema, { maxEntries = CONTRACT_MAX_MAP_ENTRIES, prepareValue } = {}) {
|
|
13
|
+
return z.preprocess((value, context) => {
|
|
14
|
+
if (value === null || typeof value !== "object" || Array.isArray(value))
|
|
15
|
+
return value;
|
|
16
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17
|
+
if (prototype !== Object.prototype && prototype !== null)
|
|
18
|
+
return [];
|
|
19
|
+
let entries = 0;
|
|
20
|
+
for (const key in value) {
|
|
21
|
+
if (!Object.hasOwn(value, key))
|
|
22
|
+
continue;
|
|
23
|
+
if (++entries > maxEntries || key.length > CONTRACT_MAX_IDENTIFIER_LENGTH) {
|
|
24
|
+
context.addIssue({ code: "custom", message: "record exceeds the contract bounds" });
|
|
25
|
+
return z.NEVER;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const encoded = Object.create(null);
|
|
29
|
+
for (const key of Object.keys(value)) {
|
|
30
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
31
|
+
if (!descriptor || !("value" in descriptor)) {
|
|
32
|
+
context.addIssue({ code: "custom", message: "record entries must be data properties" });
|
|
33
|
+
return z.NEVER;
|
|
34
|
+
}
|
|
35
|
+
let entry;
|
|
36
|
+
try {
|
|
37
|
+
entry = prepareValue ? prepareValue(key, descriptor.value) : descriptor.value;
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
// Only a profile-value refusal is a validation issue; any other throw is a defect.
|
|
41
|
+
if (!(error instanceof AgentProfileValueError))
|
|
42
|
+
throw error;
|
|
43
|
+
context.addIssue({ code: "custom", path: [...error.path], message: error.message });
|
|
44
|
+
return z.NEVER;
|
|
45
|
+
}
|
|
46
|
+
if (entry === z.NEVER) {
|
|
47
|
+
context.addIssue({ code: "custom", message: "record value exceeds the contract bounds or is not portable JSON" });
|
|
48
|
+
return z.NEVER;
|
|
49
|
+
}
|
|
50
|
+
encoded[encodeRecordKey(key)] = entry;
|
|
51
|
+
}
|
|
52
|
+
return encoded;
|
|
53
|
+
}, z.record(z
|
|
12
54
|
.string()
|
|
13
55
|
.regex(/^u(?:[0-9a-f]{4})*$/, "record keys must contain valid Unicode"), valueSchema).transform((record) => {
|
|
14
56
|
const restored = {};
|
|
@@ -23,19 +65,6 @@ export function ownPropertyRecordSchema(valueSchema) {
|
|
|
23
65
|
return restored;
|
|
24
66
|
}));
|
|
25
67
|
}
|
|
26
|
-
function encodeOwnRecordKeys(value) {
|
|
27
|
-
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
const prototype = Object.getPrototypeOf(value);
|
|
31
|
-
if (prototype !== Object.prototype && prototype !== null)
|
|
32
|
-
return [];
|
|
33
|
-
const encoded = Object.create(null);
|
|
34
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
35
|
-
encoded[encodeRecordKey(key)] = entry;
|
|
36
|
-
}
|
|
37
|
-
return encoded;
|
|
38
|
-
}
|
|
39
68
|
function encodeRecordKey(value) {
|
|
40
69
|
let encoded = isWellFormedUnicode(value) ? "u" : "i";
|
|
41
70
|
for (let index = 0; index < value.length; index += 1) {
|
package/dist/profile-schema.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { agentCandidateSecretRefSchema } from "./agent-candidate-schema-common.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { CONTRACT_MAX_ARRAY_LENGTH, isBoundedJsonMaterial } from "./contract-limits.js";
|
|
3
4
|
import { ownPropertyRecordSchema } from "./own-property-record.js";
|
|
4
5
|
import { ProxySecretsSchema } from "./proxy-secrets.js";
|
|
5
6
|
import { agentProfileKnowledgeRefsSchema } from "./profile-knowledge.js";
|
|
@@ -7,9 +8,24 @@ export { agentProfileKnowledgeRefSchema, agentProfileKnowledgeRefsSchema, } from
|
|
|
7
8
|
import { REASONING_EFFORTS, TRAINED_MODEL_PREFIX, trainedModelIdForArtifact, } from "./agent-profile.js";
|
|
8
9
|
import { canonicalCandidateDigest, sha256DigestSchema, environmentNameSchema, headerNameSchema, isSafeExecutable, isSafeRelativePath, isWellFormedUnicode, looksLikeCredential, } from "./agent-candidate-schema-common.js";
|
|
9
10
|
import { harnessTypeSchema } from "./harness.js";
|
|
10
|
-
import { canonicalAgentProfileJson } from "./agent-profile-canonical.js";
|
|
11
|
+
import { AgentProfileValueError, assertAgentProfileValue, canonicalAgentProfileJson } from "./agent-profile-canonical.js";
|
|
11
12
|
import { harnessHonorsEffort, harnessHonorsModel, harnessModelSupport, harnessReasoningEfforts, harnessSystemPromptIntents, resolveModelProvider, } from "./harness-capabilities.js";
|
|
12
13
|
import { SANDBOX_SIZE_PRESET_NAMES, } from "./sandbox-size.js";
|
|
14
|
+
const profileOpaqueValueSchema = z.unknown().transform((value, context) => {
|
|
15
|
+
if (value === undefined || isBoundedJsonMaterial(value, { dataPropertiesOnly: true }))
|
|
16
|
+
return structuredClone(value);
|
|
17
|
+
try {
|
|
18
|
+
assertAgentProfileValue(value, []);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
if (!(error instanceof AgentProfileValueError))
|
|
22
|
+
throw error;
|
|
23
|
+
context.addIssue({ code: "custom", path: [...error.path], message: error.message });
|
|
24
|
+
return z.NEVER;
|
|
25
|
+
}
|
|
26
|
+
context.addIssue({ code: "custom", message: "profile metadata exceeds contract bounds or is not portable JSON" });
|
|
27
|
+
return z.NEVER;
|
|
28
|
+
});
|
|
13
29
|
export const agentProfilePermissionValueSchema = z.enum([
|
|
14
30
|
"allow",
|
|
15
31
|
"deny",
|
|
@@ -41,11 +57,11 @@ export const agentProfileFileMountSchema = z.strictObject({
|
|
|
41
57
|
});
|
|
42
58
|
export const agentProfileResourcesSchema = z.strictObject({
|
|
43
59
|
knowledge: agentProfileKnowledgeRefsSchema.optional(),
|
|
44
|
-
files: z.array(agentProfileFileMountSchema).optional(),
|
|
45
|
-
tools: z.array(agentProfileResourceRefSchema).optional(),
|
|
46
|
-
skills: z.array(agentProfileResourceRefSchema).optional(),
|
|
47
|
-
agents: z.array(agentProfileResourceRefSchema).optional(),
|
|
48
|
-
commands: z.array(agentProfileResourceRefSchema).optional(),
|
|
60
|
+
files: z.array(agentProfileFileMountSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
61
|
+
tools: z.array(agentProfileResourceRefSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
62
|
+
skills: z.array(agentProfileResourceRefSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
63
|
+
agents: z.array(agentProfileResourceRefSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
64
|
+
commands: z.array(agentProfileResourceRefSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
49
65
|
instructions: z
|
|
50
66
|
.union([z.string(), agentProfileResourceRefSchema])
|
|
51
67
|
.optional(),
|
|
@@ -88,7 +104,7 @@ export const agentProfileModelHintsBaseSchema = z.strictObject({
|
|
|
88
104
|
maxVisibleOutputTokens: z.number().int().positive().optional(),
|
|
89
105
|
maxReasoningTokens: z.number().int().positive().optional(),
|
|
90
106
|
maxTotalOutputTokens: z.number().int().positive().optional(),
|
|
91
|
-
metadata: ownPropertyRecordSchema(
|
|
107
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
92
108
|
});
|
|
93
109
|
export const agentProfileModelHintsSchema = agentProfileModelHintsBaseSchema.superRefine(enforceModelTokenBounds);
|
|
94
110
|
/** Ordered account selection policy, without credentials or ambient model defaults. */
|
|
@@ -113,7 +129,7 @@ export const agentProfileSeatSchema = z.strictObject({
|
|
|
113
129
|
export const agentProfilePromptSchema = z.strictObject({
|
|
114
130
|
systemPrompt: z.string().optional(),
|
|
115
131
|
appendSystemPrompt: z.string().optional(),
|
|
116
|
-
instructions: z.array(z.string()).optional(),
|
|
132
|
+
instructions: z.array(z.string()).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
117
133
|
});
|
|
118
134
|
const controlCharacterPattern = /[\u0000-\u001f\u007f]/;
|
|
119
135
|
const secretCapableNamePattern = /(?:^|[_-])(?:api[_-]?key|access[_-]?key|private[_-]?key|token|secret|password|credentials?|authorization|cookie|database[_-]?url|dsn|pat)(?:[_-]|$)/i;
|
|
@@ -166,7 +182,7 @@ export const agentSubagentProfileSchema = z.strictObject({
|
|
|
166
182
|
tools: ownPropertyRecordSchema(z.boolean()).optional(),
|
|
167
183
|
permissions: ownPropertyRecordSchema(agentProfilePermissionSchema).optional(),
|
|
168
184
|
maxSteps: z.number().optional(),
|
|
169
|
-
metadata: ownPropertyRecordSchema(
|
|
185
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
170
186
|
});
|
|
171
187
|
export const agentProfileHookCommandSchema = z.strictObject({
|
|
172
188
|
command: z.string(),
|
|
@@ -181,7 +197,7 @@ export const agentProfileModeSchema = z.strictObject({
|
|
|
181
197
|
prompt: z.string().optional(),
|
|
182
198
|
tools: ownPropertyRecordSchema(z.boolean()).optional(),
|
|
183
199
|
permissions: ownPropertyRecordSchema(agentProfilePermissionSchema).optional(),
|
|
184
|
-
metadata: ownPropertyRecordSchema(
|
|
200
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
185
201
|
});
|
|
186
202
|
export const agentProfileConfidentialSchema = z.strictObject({
|
|
187
203
|
tee: z.string().optional(),
|
|
@@ -204,7 +220,7 @@ const remoteMcpUrlSchema = nonBlankMcpValueSchema.refine((value) => {
|
|
|
204
220
|
const agentProfileLocalMcpServerSchema = z.strictObject({
|
|
205
221
|
transport: z.literal("stdio").optional(),
|
|
206
222
|
command: nonBlankMcpValueSchema.refine(isSafeExecutable, "local MCP command must be a canonical public executable"),
|
|
207
|
-
args: z.array(agentProfileConfigValueSchema).optional(),
|
|
223
|
+
args: z.array(agentProfileConfigValueSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
208
224
|
env: agentProfileEnvironmentSchema.optional(),
|
|
209
225
|
cwd: z
|
|
210
226
|
.string()
|
|
@@ -213,7 +229,7 @@ const agentProfileLocalMcpServerSchema = z.strictObject({
|
|
|
213
229
|
url: z.undefined().optional(),
|
|
214
230
|
headers: z.undefined().optional(),
|
|
215
231
|
enabled: z.literal(true).optional(),
|
|
216
|
-
metadata: ownPropertyRecordSchema(
|
|
232
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
217
233
|
});
|
|
218
234
|
const agentProfileRemoteMcpServerSchema = z.strictObject({
|
|
219
235
|
transport: z.enum(["sse", "http"]).optional(),
|
|
@@ -224,7 +240,7 @@ const agentProfileRemoteMcpServerSchema = z.strictObject({
|
|
|
224
240
|
url: remoteMcpUrlSchema,
|
|
225
241
|
headers: agentProfileHeaderSchema.optional(),
|
|
226
242
|
enabled: z.literal(true).optional(),
|
|
227
|
-
metadata: ownPropertyRecordSchema(
|
|
243
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
228
244
|
});
|
|
229
245
|
const agentProfileDisabledMcpServerSchema = z.strictObject({
|
|
230
246
|
enabled: z.literal(false),
|
|
@@ -235,7 +251,7 @@ const agentProfileDisabledMcpServerSchema = z.strictObject({
|
|
|
235
251
|
cwd: z.undefined().optional(),
|
|
236
252
|
url: z.undefined().optional(),
|
|
237
253
|
headers: z.undefined().optional(),
|
|
238
|
-
metadata: ownPropertyRecordSchema(
|
|
254
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
239
255
|
});
|
|
240
256
|
export const agentProfileMcpServerSchema = z.union([
|
|
241
257
|
agentProfileLocalMcpServerSchema,
|
|
@@ -244,10 +260,10 @@ export const agentProfileMcpServerSchema = z.union([
|
|
|
244
260
|
]);
|
|
245
261
|
export const agentProfileConnectionSchema = z.strictObject({
|
|
246
262
|
connectionId: z.string().min(1),
|
|
247
|
-
capabilities: z.array(z.string().min(1)).min(1),
|
|
263
|
+
capabilities: z.array(z.string().min(1)).max(CONTRACT_MAX_ARRAY_LENGTH).min(1),
|
|
248
264
|
alias: z.string().min(1).optional(),
|
|
249
265
|
});
|
|
250
|
-
const removeListSchema = z.union([z.literal(true), z.array(z.string().min(1))]);
|
|
266
|
+
const removeListSchema = z.union([z.literal(true), z.array(z.string().min(1)).max(CONTRACT_MAX_ARRAY_LENGTH)]);
|
|
251
267
|
export const agentProfilePromptRemovalSchema = z.strictObject({
|
|
252
268
|
systemPrompt: z.literal(true).optional(),
|
|
253
269
|
appendSystemPrompt: z.literal(true).optional(),
|
|
@@ -311,10 +327,7 @@ export const agentTrainingDatasetIdentitySchema = z.strictObject({
|
|
|
311
327
|
context.addIssue({ code: "custom", path: ["taskSetDigest"], message: "training task inventory digest mismatch" });
|
|
312
328
|
}
|
|
313
329
|
});
|
|
314
|
-
export const agentTrainingParametersSchema = ownPropertyRecordSchema(z.union([z.string().max(2_048).pipe(publicProfileConfigStringSchema), z.number().finite(), z.boolean(), z.null()])).superRefine((parameters, context) => {
|
|
315
|
-
if (Object.keys(parameters).length > 100) {
|
|
316
|
-
context.addIssue({ code: "custom", message: "at most 100 public training parameters are allowed" });
|
|
317
|
-
}
|
|
330
|
+
export const agentTrainingParametersSchema = ownPropertyRecordSchema(z.union([z.string().max(2_048).pipe(publicProfileConfigStringSchema), z.number().finite(), z.boolean(), z.null()]), { maxEntries: 100 }).superRefine((parameters, context) => {
|
|
318
331
|
for (const name of Object.keys(parameters)) {
|
|
319
332
|
if (!trainingIdentitySchema.safeParse(name).success || isCredentialBearingProfileConfigName(name)) {
|
|
320
333
|
context.addIssue({ code: "custom", path: [name], message: "training parameter name must be public and non-credential" });
|
|
@@ -359,13 +372,25 @@ export const agentProfileTrainingSchema = z.strictObject({
|
|
|
359
372
|
}
|
|
360
373
|
}
|
|
361
374
|
});
|
|
362
|
-
export const agentProfileMetadataSchema = ownPropertyRecordSchema(z.unknown()
|
|
375
|
+
export const agentProfileMetadataSchema = ownPropertyRecordSchema(z.unknown(), {
|
|
376
|
+
prepareValue: (key, value) => {
|
|
377
|
+
if (key === "training")
|
|
378
|
+
return value;
|
|
379
|
+
if (value === undefined || isBoundedJsonMaterial(value, { dataPropertiesOnly: true }))
|
|
380
|
+
return structuredClone(value);
|
|
381
|
+
assertAgentProfileValue(value, [key]);
|
|
382
|
+
return z.NEVER;
|
|
383
|
+
},
|
|
384
|
+
}).superRefine((metadata, context) => {
|
|
363
385
|
if (!Object.hasOwn(metadata, "training"))
|
|
364
386
|
return;
|
|
365
387
|
const result = agentProfileTrainingSchema.safeParse(metadata.training);
|
|
366
|
-
if (
|
|
388
|
+
if (result.success)
|
|
389
|
+
metadata.training = result.data;
|
|
390
|
+
else {
|
|
391
|
+
delete metadata.training;
|
|
367
392
|
for (const issue of result.error.issues) {
|
|
368
|
-
context.addIssue({ code: "custom", path: ["training", ...issue.path], message: issue.message });
|
|
393
|
+
context.addIssue({ code: "custom", path: ["training", ...issue.path], message: issue.message, continue: false });
|
|
369
394
|
}
|
|
370
395
|
}
|
|
371
396
|
});
|
|
@@ -379,23 +404,23 @@ export const agentProfileSchema = z
|
|
|
379
404
|
name: z.string().optional(),
|
|
380
405
|
description: z.string().optional(),
|
|
381
406
|
version: z.string().optional(),
|
|
382
|
-
tags: z.array(z.string()).optional(),
|
|
407
|
+
tags: z.array(z.string()).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
383
408
|
prompt: agentProfilePromptSchema.optional(),
|
|
384
409
|
model: agentProfileModelHintsSchema.optional(),
|
|
385
410
|
harness: harnessTypeSchema.optional(),
|
|
386
|
-
seats: z.array(agentProfileSeatSchema).nonempty().optional(),
|
|
411
|
+
seats: z.array(agentProfileSeatSchema).max(CONTRACT_MAX_ARRAY_LENGTH).nonempty().optional(),
|
|
387
412
|
proxySecrets: ProxySecretsSchema.optional(),
|
|
388
413
|
permissions: ownPropertyRecordSchema(agentProfilePermissionSchema).optional(),
|
|
389
414
|
tools: ownPropertyRecordSchema(z.boolean()).optional(),
|
|
390
415
|
mcp: ownPropertyRecordSchema(agentProfileMcpServerSchema).optional(),
|
|
391
|
-
connections: z.array(agentProfileConnectionSchema).optional(),
|
|
416
|
+
connections: z.array(agentProfileConnectionSchema).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
392
417
|
subagents: ownPropertyRecordSchema(agentSubagentProfileSchema).optional(),
|
|
393
418
|
resources: agentProfileResourcesSchema.optional(),
|
|
394
|
-
hooks: ownPropertyRecordSchema(z.array(agentProfileHookCommandSchema)).optional(),
|
|
419
|
+
hooks: ownPropertyRecordSchema(z.array(agentProfileHookCommandSchema).max(CONTRACT_MAX_ARRAY_LENGTH)).optional(),
|
|
395
420
|
modes: ownPropertyRecordSchema(agentProfileModeSchema).optional(),
|
|
396
421
|
confidential: agentProfileConfidentialSchema.optional(),
|
|
397
422
|
metadata: agentProfileMetadataSchema.optional(),
|
|
398
|
-
extensions: ownPropertyRecordSchema(z.union([ownPropertyRecordSchema(
|
|
423
|
+
extensions: ownPropertyRecordSchema(z.union([ownPropertyRecordSchema(profileOpaqueValueSchema), z.undefined()])).optional(),
|
|
399
424
|
})
|
|
400
425
|
.superRefine((profile, context) => {
|
|
401
426
|
validateNestedRecordKeys(profile, context, [], new Set());
|
|
@@ -533,13 +558,13 @@ export const agentProfileDiffSchema = z.strictObject({
|
|
|
533
558
|
"optimizer",
|
|
534
559
|
"compound",
|
|
535
560
|
]),
|
|
536
|
-
artifacts: z.array(z.string().min(1)).optional(),
|
|
537
|
-
notes: z.array(z.string()).optional(),
|
|
561
|
+
artifacts: z.array(z.string().min(1)).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
562
|
+
notes: z.array(z.string()).max(CONTRACT_MAX_ARRAY_LENGTH).optional(),
|
|
538
563
|
})
|
|
539
564
|
.optional(),
|
|
540
565
|
set: agentProfileSchema.optional(),
|
|
541
566
|
remove: agentProfileDiffRemovalSchema.optional(),
|
|
542
|
-
metadata: ownPropertyRecordSchema(
|
|
567
|
+
metadata: ownPropertyRecordSchema(profileOpaqueValueSchema).optional(),
|
|
543
568
|
});
|
|
544
569
|
const _agentProfileSchemaMatchesInterface = true;
|
|
545
570
|
void _agentProfileSchemaMatchesInterface;
|
package/dist/proxy-secrets.js
CHANGED
|
@@ -57,9 +57,8 @@ export const ProxySecretsSchema = z.array(ProxySecretBindingSchema).max(CONTRACT
|
|
|
57
57
|
* Strings select an API-verified parent sandbox; null explicitly selects independent issuance.
|
|
58
58
|
* When present, every destination in the final effective selection must have exactly one key.
|
|
59
59
|
*/
|
|
60
|
-
export const ProxySecretParentsSchema = ownPropertyRecordSchema(boundedIdentifierSchema.nullable())
|
|
61
|
-
.refine((parents) => Object.keys(parents).
|
|
62
|
-
Object.keys(parents).every((name) => envNameSchema.safeParse(name).success), "invalid parent selection keys");
|
|
60
|
+
export const ProxySecretParentsSchema = ownPropertyRecordSchema(boundedIdentifierSchema.nullable(), { maxEntries: CONTRACT_MAX_ARRAY_LENGTH })
|
|
61
|
+
.refine((parents) => Object.keys(parents).every((name) => envNameSchema.safeParse(name).success), "invalid parent selection keys");
|
|
63
62
|
/** Shape and totality only; the API verifies issuer authority and durable immutable pins. */
|
|
64
63
|
export function isProxySecretParentsTotal(bindings, parents) {
|
|
65
64
|
const selection = ProxySecretsSchema.safeParse(bindings);
|