@jigging/agent-method 0.0.0 → 0.1.0-alpha.4

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 (52) hide show
  1. package/AGENTS.md +98 -0
  2. package/FLOW.contract.json +241 -0
  3. package/FLOW.meta.json +8 -0
  4. package/FLOW.ts +3 -0
  5. package/LICENSE +373 -0
  6. package/README.md +369 -0
  7. package/THIRD_PARTY_NOTICES +9 -0
  8. package/contracts/acp-public-updates.json +75 -0
  9. package/contracts/agent-commands.json +88 -0
  10. package/contracts/agent-replies.json +202 -0
  11. package/contracts/http-request/contract.json +37 -0
  12. package/dist/api.d.ts +11 -0
  13. package/dist/api.js +164 -0
  14. package/dist/conversation.d.ts +68 -0
  15. package/dist/conversation.js +346 -0
  16. package/dist/errors.d.ts +5 -0
  17. package/dist/errors.js +8 -0
  18. package/dist/flow.d.ts +3 -0
  19. package/dist/flow.js +2784 -0
  20. package/dist/index.d.ts +67 -0
  21. package/dist/index.js +220 -0
  22. package/dist/json.d.ts +21 -0
  23. package/dist/json.js +409 -0
  24. package/dist/schema.d.ts +7 -0
  25. package/dist/schema.js +180 -0
  26. package/dist/skills.d.ts +3 -0
  27. package/dist/skills.js +132 -0
  28. package/dist/values.d.ts +11 -0
  29. package/dist/values.js +65 -0
  30. package/justfile +32 -0
  31. package/licenses/flow.LICENSE +202 -0
  32. package/package.json +45 -5
  33. package/settings.schema.json +12 -0
  34. package/skills/answer-check/SKILL.md +5 -0
  35. package/src/api.ts +191 -0
  36. package/src/conversation.ts +387 -0
  37. package/src/errors.ts +11 -0
  38. package/src/flow.ts +66 -0
  39. package/src/index.ts +325 -0
  40. package/src/json.ts +406 -0
  41. package/src/schema.ts +230 -0
  42. package/src/skills.ts +138 -0
  43. package/src/values.ts +77 -0
  44. package/test/api.test.ts +326 -0
  45. package/test/conversation-fixture.ts +73 -0
  46. package/test/conversation.test.ts +328 -0
  47. package/test/json.test.ts +88 -0
  48. package/test/method.test.ts +308 -0
  49. package/test/pack.test.ts +81 -0
  50. package/test/result.test.ts +103 -0
  51. package/test/skills-flow.test.ts +252 -0
  52. package/tsconfig.json +17 -0
package/dist/json.js ADDED
@@ -0,0 +1,409 @@
1
+ export const JSON_1_LIMITS = Object.freeze({
2
+ bytes: 16_777_216,
3
+ depth: 128,
4
+ nodes: 262_144,
5
+ containerEntries: 65_536,
6
+ stringBytes: 8_388_608,
7
+ memberNameBytes: 1_024,
8
+ numberTokenBytes: 128,
9
+ });
10
+ const encoder = new TextEncoder();
11
+ const decoder = new TextDecoder('utf-8', { fatal: true, ignoreBOM: true });
12
+ export class Json1Error extends Error {
13
+ constructor(message) {
14
+ super(message);
15
+ this.name = 'Json1Error';
16
+ }
17
+ }
18
+ class Parser {
19
+ source;
20
+ position = 0;
21
+ nodes = 0;
22
+ constructor(source) {
23
+ this.source = source;
24
+ }
25
+ parse() {
26
+ this.skipWhitespace();
27
+ const value = this.parseValue(1);
28
+ this.skipWhitespace();
29
+ if (this.position !== this.source.length)
30
+ this.fail('unexpected trailing characters');
31
+ return value;
32
+ }
33
+ parseValue(depth) {
34
+ if (depth > JSON_1_LIMITS.depth)
35
+ this.fail('maximum value depth exceeded');
36
+ this.nodes += 1;
37
+ if (this.nodes > JSON_1_LIMITS.nodes)
38
+ this.fail('maximum value nodes exceeded');
39
+ const char = this.source[this.position];
40
+ if (char === '"')
41
+ return this.parseString(false);
42
+ if (char === '{')
43
+ return this.parseObject(depth);
44
+ if (char === '[')
45
+ return this.parseArray(depth);
46
+ if (char === 't')
47
+ return this.parseLiteral('true', true);
48
+ if (char === 'f')
49
+ return this.parseLiteral('false', false);
50
+ if (char === 'n')
51
+ return this.parseLiteral('null', null);
52
+ if (char === '-' || (char !== undefined && char >= '0' && char <= '9')) {
53
+ return this.parseNumber();
54
+ }
55
+ return this.fail('expected a JSON value');
56
+ }
57
+ parseObject(depth) {
58
+ this.position += 1;
59
+ const result = Object.create(null);
60
+ const keys = new Set();
61
+ this.skipWhitespace();
62
+ if (this.consume('}'))
63
+ return result;
64
+ let members = 0;
65
+ while (true) {
66
+ if (this.source[this.position] !== '"')
67
+ this.fail('expected an object member name');
68
+ const key = this.parseString(true);
69
+ if (keys.has(key))
70
+ this.fail(`duplicate object member ${key}`);
71
+ keys.add(key);
72
+ members += 1;
73
+ if (members > JSON_1_LIMITS.containerEntries) {
74
+ this.fail('maximum object members exceeded');
75
+ }
76
+ this.skipWhitespace();
77
+ if (!this.consume(':'))
78
+ this.fail("expected ':' after member name");
79
+ this.skipWhitespace();
80
+ result[key] = this.parseValue(depth + 1);
81
+ this.skipWhitespace();
82
+ if (this.consume('}'))
83
+ return result;
84
+ if (!this.consume(','))
85
+ this.fail("expected ',' or '}'");
86
+ this.skipWhitespace();
87
+ }
88
+ }
89
+ parseArray(depth) {
90
+ this.position += 1;
91
+ const result = [];
92
+ this.skipWhitespace();
93
+ if (this.consume(']'))
94
+ return result;
95
+ while (true) {
96
+ if (result.length >= JSON_1_LIMITS.containerEntries) {
97
+ this.fail('maximum array items exceeded');
98
+ }
99
+ result.push(this.parseValue(depth + 1));
100
+ this.skipWhitespace();
101
+ if (this.consume(']'))
102
+ return result;
103
+ if (!this.consume(','))
104
+ this.fail("expected ',' or ']'");
105
+ this.skipWhitespace();
106
+ }
107
+ }
108
+ parseString(memberName) {
109
+ this.position += 1;
110
+ const parts = [];
111
+ let segmentStart = this.position;
112
+ while (this.position < this.source.length) {
113
+ const char = this.source[this.position];
114
+ this.position += 1;
115
+ if (char === '"') {
116
+ parts.push(this.source.slice(segmentStart, this.position - 1));
117
+ const value = parts.join('');
118
+ const limit = memberName ? JSON_1_LIMITS.memberNameBytes : JSON_1_LIMITS.stringBytes;
119
+ if (encoder.encode(value).byteLength > limit)
120
+ this.fail('maximum string bytes exceeded');
121
+ return value;
122
+ }
123
+ if (char === '\\') {
124
+ parts.push(this.source.slice(segmentStart, this.position - 1));
125
+ parts.push(this.parseEscape());
126
+ segmentStart = this.position;
127
+ continue;
128
+ }
129
+ if (char === undefined || char.charCodeAt(0) <= 0x1f) {
130
+ this.fail('invalid character in string');
131
+ }
132
+ const code = char.charCodeAt(0);
133
+ if (code >= 0xd800 && code <= 0xdfff) {
134
+ const next = this.source[this.position];
135
+ if (code > 0xdbff ||
136
+ next === undefined ||
137
+ next.charCodeAt(0) < 0xdc00 ||
138
+ next.charCodeAt(0) > 0xdfff)
139
+ this.fail('lone Unicode surrogate');
140
+ this.position += 1;
141
+ }
142
+ }
143
+ return this.fail('unterminated string');
144
+ }
145
+ parseEscape() {
146
+ const escaped = this.source[this.position];
147
+ this.position += 1;
148
+ switch (escaped) {
149
+ case '"':
150
+ case '\\':
151
+ case '/':
152
+ return escaped;
153
+ case 'b':
154
+ return '\b';
155
+ case 'f':
156
+ return '\f';
157
+ case 'n':
158
+ return '\n';
159
+ case 'r':
160
+ return '\r';
161
+ case 't':
162
+ return '\t';
163
+ case 'u': {
164
+ const first = this.parseHexCodeUnit();
165
+ if (first >= 0xd800 && first <= 0xdbff) {
166
+ if (this.source.slice(this.position, this.position + 2) !== '\\u') {
167
+ this.fail('high surrogate without low surrogate');
168
+ }
169
+ this.position += 2;
170
+ const second = this.parseHexCodeUnit();
171
+ if (second < 0xdc00 || second > 0xdfff) {
172
+ this.fail('high surrogate without low surrogate');
173
+ }
174
+ return String.fromCharCode(first, second);
175
+ }
176
+ if (first >= 0xdc00 && first <= 0xdfff)
177
+ this.fail('lone low surrogate');
178
+ return String.fromCharCode(first);
179
+ }
180
+ default:
181
+ return this.fail('invalid string escape');
182
+ }
183
+ }
184
+ parseHexCodeUnit() {
185
+ const token = this.source.slice(this.position, this.position + 4);
186
+ if (!/^[0-9A-Fa-f]{4}$/.test(token))
187
+ this.fail('invalid Unicode escape');
188
+ this.position += 4;
189
+ return Number.parseInt(token, 16);
190
+ }
191
+ parseNumber() {
192
+ const start = this.position;
193
+ this.consume('-');
194
+ if (this.consume('0')) {
195
+ const next = this.source[this.position];
196
+ if (next !== undefined && next >= '0' && next <= '9') {
197
+ this.fail('leading zero in number');
198
+ }
199
+ }
200
+ else {
201
+ if (!this.consumeDigit('1', '9'))
202
+ this.fail('invalid number');
203
+ while (this.consumeDigit('0', '9')) { }
204
+ }
205
+ if (this.consume('.')) {
206
+ if (!this.consumeDigit('0', '9'))
207
+ this.fail('fraction requires a digit');
208
+ while (this.consumeDigit('0', '9')) { }
209
+ }
210
+ const exponent = this.source[this.position];
211
+ if (exponent === 'e' || exponent === 'E') {
212
+ this.position += 1;
213
+ const sign = this.source[this.position];
214
+ if (sign === '+' || sign === '-')
215
+ this.position += 1;
216
+ if (!this.consumeDigit('0', '9'))
217
+ this.fail('exponent requires a digit');
218
+ while (this.consumeDigit('0', '9')) { }
219
+ }
220
+ const token = this.source.slice(start, this.position);
221
+ if (token.length > JSON_1_LIMITS.numberTokenBytes) {
222
+ this.fail('maximum number token bytes exceeded');
223
+ }
224
+ const value = Number(token);
225
+ if (!Number.isFinite(value))
226
+ this.fail('non-finite number');
227
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
228
+ this.fail('integral number outside safe range');
229
+ }
230
+ return value;
231
+ }
232
+ parseLiteral(token, value) {
233
+ if (this.source.slice(this.position, this.position + token.length) !== token) {
234
+ this.fail(`invalid literal ${token}`);
235
+ }
236
+ this.position += token.length;
237
+ return value;
238
+ }
239
+ consume(expected) {
240
+ if (this.source[this.position] !== expected)
241
+ return false;
242
+ this.position += 1;
243
+ return true;
244
+ }
245
+ consumeDigit(minimum, maximum) {
246
+ const char = this.source[this.position];
247
+ if (char === undefined || char < minimum || char > maximum)
248
+ return false;
249
+ this.position += 1;
250
+ return true;
251
+ }
252
+ skipWhitespace() {
253
+ while (true) {
254
+ const char = this.source[this.position];
255
+ if (char !== ' ' && char !== '\n' && char !== '\r' && char !== '\t')
256
+ return;
257
+ this.position += 1;
258
+ }
259
+ }
260
+ fail(message) {
261
+ throw new Json1Error(`${message} at character ${this.position}`);
262
+ }
263
+ }
264
+ export function decodeJson1(bytes) {
265
+ if (bytes.byteLength === 0)
266
+ throw new Json1Error('empty JSON/0 document');
267
+ if (bytes.byteLength > JSON_1_LIMITS.bytes)
268
+ throw new Json1Error('maximum bytes exceeded');
269
+ if (bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) {
270
+ throw new Json1Error('UTF-8 BOM is not allowed');
271
+ }
272
+ let text;
273
+ try {
274
+ text = decoder.decode(bytes);
275
+ }
276
+ catch {
277
+ throw new Json1Error('invalid UTF-8');
278
+ }
279
+ return new Parser(text).parse();
280
+ }
281
+ export function validateJson1(value) {
282
+ visitJson(value, 1, { nodes: 0, encodedBytes: 0, active: new WeakSet() }, false);
283
+ }
284
+ function visitJson(value, depth, state, memberName) {
285
+ if (depth > JSON_1_LIMITS.depth)
286
+ throw new Json1Error('maximum value depth exceeded');
287
+ state.nodes += 1;
288
+ if (state.nodes > JSON_1_LIMITS.nodes)
289
+ throw new Json1Error('maximum value nodes exceeded');
290
+ if (value === null) {
291
+ chargeEncodedBytes(state, 4);
292
+ return;
293
+ }
294
+ if (typeof value === 'boolean') {
295
+ chargeEncodedBytes(state, value ? 4 : 5);
296
+ return;
297
+ }
298
+ if (typeof value === 'number') {
299
+ if (!Number.isFinite(value))
300
+ throw new Json1Error('non-finite number');
301
+ if (Number.isInteger(value) && !Number.isSafeInteger(value)) {
302
+ throw new Json1Error('integral number outside safe range');
303
+ }
304
+ chargeEncodedBytes(state, (Object.is(value, -0) ? '0' : JSON.stringify(value)).length);
305
+ return;
306
+ }
307
+ if (typeof value === 'string') {
308
+ validateString(value, memberName ? JSON_1_LIMITS.memberNameBytes : JSON_1_LIMITS.stringBytes);
309
+ chargeEncodedBytes(state, encodedJsonStringBytes(value));
310
+ return;
311
+ }
312
+ if (typeof value !== 'object')
313
+ throw new Json1Error('value is not JSON/0');
314
+ if (state.active.has(value))
315
+ throw new Json1Error('cyclic value');
316
+ state.active.add(value);
317
+ try {
318
+ if (Array.isArray(value)) {
319
+ if (value.length > JSON_1_LIMITS.containerEntries) {
320
+ throw new Json1Error('maximum array items exceeded');
321
+ }
322
+ if (Object.getPrototypeOf(value) !== Array.prototype ||
323
+ Reflect.ownKeys(value).length !== value.length + 1) {
324
+ throw new Json1Error('array must contain only ordinary indexed data');
325
+ }
326
+ chargeEncodedBytes(state, 2 + Math.max(0, value.length - 1));
327
+ for (let index = 0; index < value.length; index += 1) {
328
+ const field = Object.getOwnPropertyDescriptor(value, String(index));
329
+ if (field === undefined || !('value' in field) || !field.enumerable) {
330
+ throw new Json1Error('array items must be ordinary data');
331
+ }
332
+ visitJson(field.value, depth + 1, state, false);
333
+ }
334
+ return;
335
+ }
336
+ const object = value;
337
+ const prototype = Object.getPrototypeOf(object);
338
+ if (prototype !== Object.prototype && prototype !== null) {
339
+ throw new Json1Error('object must be an ordinary record');
340
+ }
341
+ const keys = Object.keys(object);
342
+ if (Reflect.ownKeys(object).length !== keys.length) {
343
+ throw new Json1Error('object members must be enumerable string data');
344
+ }
345
+ if (keys.length > JSON_1_LIMITS.containerEntries) {
346
+ throw new Json1Error('maximum object members exceeded');
347
+ }
348
+ chargeEncodedBytes(state, 2 + keys.length + Math.max(0, keys.length - 1));
349
+ for (const key of keys) {
350
+ validateString(key, JSON_1_LIMITS.memberNameBytes);
351
+ chargeEncodedBytes(state, encodedJsonStringBytes(key));
352
+ const field = Object.getOwnPropertyDescriptor(object, key);
353
+ if (!('value' in field))
354
+ throw new Json1Error('object members must be ordinary data');
355
+ visitJson(field.value, depth + 1, state, false);
356
+ }
357
+ }
358
+ finally {
359
+ state.active.delete(value);
360
+ }
361
+ }
362
+ function chargeEncodedBytes(state, bytes) {
363
+ if (bytes > JSON_1_LIMITS.bytes - state.encodedBytes) {
364
+ throw new Json1Error('maximum encoded bytes exceeded');
365
+ }
366
+ state.encodedBytes += bytes;
367
+ }
368
+ function encodedJsonStringBytes(value) {
369
+ return encoder.encode(JSON.stringify(value)).byteLength;
370
+ }
371
+ function validateString(value, byteLimit) {
372
+ if (encoder.encode(value).byteLength > byteLimit) {
373
+ throw new Json1Error('maximum string bytes exceeded');
374
+ }
375
+ for (let index = 0; index < value.length; index += 1) {
376
+ const code = value.charCodeAt(index);
377
+ if (code >= 0xd800 && code <= 0xdbff) {
378
+ const next = value.charCodeAt(index + 1);
379
+ if (!(next >= 0xdc00 && next <= 0xdfff))
380
+ throw new Json1Error('lone Unicode surrogate');
381
+ index += 1;
382
+ }
383
+ else if (code >= 0xdc00 && code <= 0xdfff) {
384
+ throw new Json1Error('lone Unicode surrogate');
385
+ }
386
+ }
387
+ }
388
+ /** RFC 8785 canonical bytes for an already validated JSON/0 value. */
389
+ export function canonicalJson(value) {
390
+ validateJson1(value);
391
+ return encoder.encode(canonicalText(value));
392
+ }
393
+ function canonicalText(value) {
394
+ if (value === null || typeof value === 'boolean' || typeof value === 'string') {
395
+ return JSON.stringify(value);
396
+ }
397
+ if (typeof value === 'number')
398
+ return Object.is(value, -0) ? '0' : JSON.stringify(value);
399
+ if (Array.isArray(value))
400
+ return `[${value.map(canonicalText).join(',')}]`;
401
+ const object = value;
402
+ const members = Object.keys(object)
403
+ .sort(compareUtf16)
404
+ .map((key) => `${JSON.stringify(key)}:${canonicalText(object[key])}`);
405
+ return `{${members.join(',')}}`;
406
+ }
407
+ function compareUtf16(left, right) {
408
+ return left < right ? -1 : left > right ? 1 : 0;
409
+ }
@@ -0,0 +1,7 @@
1
+ import { type JsonObject, type JsonValue } from './json.js';
2
+ /** Validate Jig's deliberately bounded recursive Agent structured-output profile. */
3
+ export declare function assertResponseSchema(schema: JsonObject): void;
4
+ /** Remove only the FLOW dialect declaration from a fresh provider-facing copy. */
5
+ export declare function projectResponseSchema(schema: JsonObject): JsonObject;
6
+ /** Check a value against the already asserted closed structured-output profile. */
7
+ export declare function matchesResponseSchema(schema: JsonObject, value: JsonValue): boolean;
package/dist/schema.js ADDED
@@ -0,0 +1,180 @@
1
+ import { AgentMethodError } from './errors.js';
2
+ import { canonicalJson, validateJson1 } from './json.js';
3
+ import { exactKeys, ordinaryRecord } from './values.js';
4
+ const MAX_RESPONSE_SCHEMA_DEPTH = 8;
5
+ const MAX_PROPERTIES_PER_OBJECT = 32;
6
+ const MAX_RESPONSE_SCHEMA_PROPERTIES = 128;
7
+ const MAX_ARRAY_ITEMS = 256;
8
+ const MAX_RESPONSE_SCHEMA_ENUM_VALUES = 256;
9
+ const MAX_RESPONSE_SCHEMA_SYMBOL_CHARACTERS = 120_000;
10
+ const MAX_LARGE_ENUM_CHARACTERS = 15_000;
11
+ /** Validate Jig's deliberately bounded recursive Agent structured-output profile. */
12
+ export function assertResponseSchema(schema) {
13
+ requireSchemaJson(schema);
14
+ if (schema.$schema !== 'https://flow.jig.md/schemas/schema-0.json')
15
+ invalidSchemaProfile();
16
+ const state = {
17
+ properties: 0,
18
+ enumValues: 0,
19
+ symbolCharacters: 0,
20
+ };
21
+ assertResponseSchemaNode(schema, 1, true, state);
22
+ }
23
+ function assertResponseSchemaNode(value, depth, root, state) {
24
+ const schema = ordinaryRecord(value);
25
+ if (schema === undefined || depth > MAX_RESPONSE_SCHEMA_DEPTH)
26
+ invalidSchemaProfile();
27
+ if (schema.type === 'object') {
28
+ assertClosedResponseObject(schema, depth, root, state);
29
+ return;
30
+ }
31
+ if (root || Object.hasOwn(schema, '$schema'))
32
+ invalidSchemaProfile();
33
+ if (schema.type === 'array') {
34
+ const baseExpected = Object.hasOwn(schema, 'minItems')
35
+ ? ['items', 'maxItems', 'minItems', 'type']
36
+ : ['items', 'maxItems', 'type'];
37
+ if (!exactProfileKeys(schema, baseExpected) ||
38
+ !boundedNonnegativeInteger(schema.maxItems, MAX_ARRAY_ITEMS) ||
39
+ (Object.hasOwn(schema, 'minItems') &&
40
+ (!boundedNonnegativeInteger(schema.minItems, MAX_ARRAY_ITEMS) ||
41
+ schema.minItems > schema.maxItems))) {
42
+ invalidSchemaProfile();
43
+ }
44
+ assertResponseSchemaNode(schema.items, depth + 1, false, state);
45
+ return;
46
+ }
47
+ if (schema.type === 'integer' || isNullableType(schema.type, 'integer')) {
48
+ if (!exactProfileKeys(schema, ['type']))
49
+ invalidSchemaProfile();
50
+ return;
51
+ }
52
+ if (schema.type === 'string' || isNullableType(schema.type, 'string')) {
53
+ assertResponseString(schema, isNullableType(schema.type, 'string'), state);
54
+ return;
55
+ }
56
+ invalidSchemaProfile();
57
+ }
58
+ function assertClosedResponseObject(schema, depth, root, state) {
59
+ const expected = root
60
+ ? ['$schema', 'additionalProperties', 'properties', 'required', 'type']
61
+ : ['additionalProperties', 'properties', 'required', 'type'];
62
+ const properties = ordinaryRecord(schema.properties);
63
+ if (!exactProfileKeys(schema, expected) ||
64
+ schema.additionalProperties !== false ||
65
+ properties === undefined ||
66
+ !Array.isArray(schema.required)) {
67
+ invalidSchemaProfile();
68
+ }
69
+ const names = Object.keys(properties);
70
+ if (names.length === 0 ||
71
+ names.length > MAX_PROPERTIES_PER_OBJECT ||
72
+ schema.required.length !== names.length ||
73
+ new Set(schema.required).size !== names.length ||
74
+ schema.required.some((name) => typeof name !== 'string' || !Object.hasOwn(properties, name))) {
75
+ invalidSchemaProfile();
76
+ }
77
+ state.properties += names.length;
78
+ if (state.properties > MAX_RESPONSE_SCHEMA_PROPERTIES)
79
+ invalidSchemaProfile();
80
+ for (const name of names) {
81
+ state.symbolCharacters += unicodeScalarLength(name);
82
+ if (state.symbolCharacters > MAX_RESPONSE_SCHEMA_SYMBOL_CHARACTERS)
83
+ invalidSchemaProfile();
84
+ assertResponseSchemaNode(properties[name], depth + 1, false, state);
85
+ }
86
+ }
87
+ function assertResponseString(schema, nullable, state) {
88
+ if (!Object.hasOwn(schema, 'enum')) {
89
+ if (!exactProfileKeys(schema, ['type']))
90
+ invalidSchemaProfile();
91
+ return;
92
+ }
93
+ if (!exactProfileKeys(schema, ['enum', 'type']) ||
94
+ !Array.isArray(schema.enum) ||
95
+ schema.enum.length === 0 ||
96
+ schema.enum.length > MAX_RESPONSE_SCHEMA_ENUM_VALUES ||
97
+ schema.enum.some((item) => typeof item !== 'string' && (!nullable || item !== null)) ||
98
+ new Set(schema.enum).size !== schema.enum.length ||
99
+ (nullable &&
100
+ (!schema.enum.includes(null) || !schema.enum.some((item) => typeof item === 'string')))) {
101
+ invalidSchemaProfile();
102
+ }
103
+ state.enumValues += schema.enum.length;
104
+ const enumCharacters = schema.enum.reduce((total, item) => total + (typeof item === 'string' ? unicodeScalarLength(item) : 0), 0);
105
+ if (schema.enum.length > 250 && enumCharacters > MAX_LARGE_ENUM_CHARACTERS) {
106
+ invalidSchemaProfile();
107
+ }
108
+ state.symbolCharacters += enumCharacters;
109
+ if (state.enumValues > MAX_RESPONSE_SCHEMA_ENUM_VALUES)
110
+ invalidSchemaProfile();
111
+ if (state.symbolCharacters > MAX_RESPONSE_SCHEMA_SYMBOL_CHARACTERS)
112
+ invalidSchemaProfile();
113
+ }
114
+ function isNullableType(value, base) {
115
+ return (Array.isArray(value) &&
116
+ value.length === 2 &&
117
+ new Set(value).size === 2 &&
118
+ value.includes(base) &&
119
+ value.includes('null'));
120
+ }
121
+ function exactProfileKeys(value, expected) {
122
+ if (Object.hasOwn(value, 'description') && typeof value.description !== 'string')
123
+ return false;
124
+ return exactKeys(value, Object.hasOwn(value, 'description') ? [...expected, 'description'] : expected);
125
+ }
126
+ function boundedNonnegativeInteger(value, maximum) {
127
+ return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 && value <= maximum;
128
+ }
129
+ function unicodeScalarLength(value) {
130
+ return [...value].length;
131
+ }
132
+ function invalidSchemaProfile() {
133
+ throw new AgentMethodError('INVALID_INPUT', 'Agent responseSchema must use the bounded structured-output profile');
134
+ }
135
+ function requireSchemaJson(schema) {
136
+ try {
137
+ validateJson1(schema);
138
+ if (ordinaryRecord(schema) === undefined)
139
+ invalidSchemaProfile();
140
+ if (canonicalJson(schema).byteLength > 256 * 1024) {
141
+ throw new AgentMethodError('RESOURCE_EXHAUSTED', 'Agent responseSchema exceeds 256 KiB');
142
+ }
143
+ }
144
+ catch (error) {
145
+ if (error instanceof AgentMethodError)
146
+ throw error;
147
+ throw new AgentMethodError('INVALID_INPUT', 'Agent responseSchema must be valid JSON/0');
148
+ }
149
+ }
150
+ /** Remove only the FLOW dialect declaration from a fresh provider-facing copy. */
151
+ export function projectResponseSchema(schema) {
152
+ assertResponseSchema(schema);
153
+ const copy = JSON.parse(new TextDecoder().decode(canonicalJson(schema)));
154
+ delete copy.$schema;
155
+ return copy;
156
+ }
157
+ /** Check a value against the already asserted closed structured-output profile. */
158
+ export function matchesResponseSchema(schema, value) {
159
+ if (value === null && Array.isArray(schema.type) && schema.type.includes('null')) {
160
+ return !Array.isArray(schema.enum) || schema.enum.includes(null);
161
+ }
162
+ if (schema.type === 'object') {
163
+ const record = ordinaryRecord(value);
164
+ if (record === undefined)
165
+ return false;
166
+ const properties = schema.properties;
167
+ return (exactKeys(record, Object.keys(properties)) &&
168
+ Object.entries(properties).every(([name, child]) => matchesResponseSchema(child, record[name])));
169
+ }
170
+ if (schema.type === 'array') {
171
+ return (Array.isArray(value) &&
172
+ value.length <= schema.maxItems &&
173
+ value.length >= (schema.minItems ?? 0) &&
174
+ value.every((item) => matchesResponseSchema(schema.items, item)));
175
+ }
176
+ if (schema.type === 'integer' || isNullableType(schema.type, 'integer')) {
177
+ return typeof value === 'number' && Number.isSafeInteger(value);
178
+ }
179
+ return typeof value === 'string' && (!Array.isArray(schema.enum) || schema.enum.includes(value));
180
+ }
@@ -0,0 +1,3 @@
1
+ import type { SkillText } from './index.js';
2
+ /** Read explicit selections from an immutable package root, never from the CWD. */
3
+ export declare function readPackageSkills(packageRoot: URL, names: readonly string[]): Promise<readonly SkillText[]>;