@vertesia/common 1.5.0-dev.20260806.125520Z → 1.5.0-dev.20260807.073259Z
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/lib/api-schemas/adapter.d.ts +1 -0
- package/lib/api-schemas/adapter.d.ts.map +1 -1
- package/lib/api-schemas/adapter.js +1 -1
- package/lib/api-schemas/adapter.js.map +1 -1
- package/lib/api-schemas/agent-runs.d.ts +140 -0
- package/lib/api-schemas/agent-runs.d.ts.map +1 -1
- package/lib/api-schemas/app-runtime.d.ts +476 -0
- package/lib/api-schemas/app-runtime.d.ts.map +1 -1
- package/lib/api-schemas/content.d.ts +140 -0
- package/lib/api-schemas/content.d.ts.map +1 -1
- package/lib/api-schemas/document-processing.d.ts +84 -0
- package/lib/api-schemas/document-processing.d.ts.map +1 -1
- package/lib/api-schemas/environment.d.ts +2 -0
- package/lib/api-schemas/environment.d.ts.map +1 -1
- package/lib/api-schemas/events.d.ts +252 -0
- package/lib/api-schemas/events.d.ts.map +1 -1
- package/lib/api-schemas/interaction.d.ts +1156 -16
- package/lib/api-schemas/interaction.d.ts.map +1 -1
- package/lib/api-schemas/project-configuration.d.ts +280 -0
- package/lib/api-schemas/project-configuration.d.ts.map +1 -1
- package/lib/api-schemas/project.d.ts +420 -0
- package/lib/api-schemas/project.d.ts.map +1 -1
- package/lib/api-schemas/registry.d.ts +4515 -1
- package/lib/api-schemas/registry.d.ts.map +1 -1
- package/lib/api-schemas/registry.js +148 -16
- package/lib/api-schemas/registry.js.map +1 -1
- package/lib/api-schemas/store.d.ts +1404 -4
- package/lib/api-schemas/store.d.ts.map +1 -1
- package/lib/api-schemas/view-execution.d.ts +56 -0
- package/lib/api-schemas/view-execution.d.ts.map +1 -1
- package/lib/api-schemas/views.d.ts +168 -0
- package/lib/api-schemas/views.d.ts.map +1 -1
- package/lib/api-schemas/workflow-runs.d.ts +56 -0
- package/lib/api-schemas/workflow-runs.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.d.ts.map +1 -1
- package/lib/store/intake-policy-schema.generated.js +36 -0
- package/lib/store/intake-policy-schema.generated.js.map +1 -1
- package/lib/vertesia-common.js +3 -3
- package/lib/vertesia-common.js.map +1 -1
- package/package.json +3 -3
- package/src/api-schemas/adapter.ts +1 -1
- package/src/api-schemas/api-discriminators.test.ts +84 -0
- package/src/api-schemas/registry.ts +175 -18
- package/src/store/intake-policy-schema.generated.ts +36 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertesia/common",
|
|
3
|
-
"version": "1.5.0-dev.
|
|
3
|
+
"version": "1.5.0-dev.20260807.073259Z",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"ajv": "^8.20.0",
|
|
33
33
|
"ajv-formats": "^3.0.1",
|
|
34
34
|
"zod": "^4.4.3",
|
|
35
|
-
"@llumiverse/common": "1.5.0-dev.
|
|
35
|
+
"@llumiverse/common": "1.5.0-dev.20260807.072147Z"
|
|
36
36
|
},
|
|
37
37
|
"repository": {
|
|
38
38
|
"type": "git",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"ai",
|
|
48
48
|
"typescript"
|
|
49
49
|
],
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "e6ebc1482ad6c4df965d74c79f82eb25fc6e2ce3",
|
|
51
51
|
"scripts": {
|
|
52
52
|
"lint": "biome lint src",
|
|
53
53
|
"test": "vitest run",
|
|
@@ -42,7 +42,7 @@ export class SchemaAdapterError extends Error {}
|
|
|
42
42
|
const REF = '$ref';
|
|
43
43
|
const COMPONENT_PREFIX = '#/components/schemas/';
|
|
44
44
|
|
|
45
|
-
function isPlainObject(value: unknown): value is JsonObject {
|
|
45
|
+
export function isPlainObject(value: unknown): value is JsonObject {
|
|
46
46
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { type ApiComponentName, ApiSchemaComponents, validateApiResponse } from './registry.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The build-time half of `discriminator: true`.
|
|
6
|
+
*
|
|
7
|
+
* AJV applies its discriminator rules when it COMPILES a schema, not when it validates one, and it
|
|
8
|
+
* signals a union it cannot handle by throwing. Left unchecked that turns into a 500 on the first
|
|
9
|
+
* request to whichever endpoint publishes the offending component — a failure mode that depends on
|
|
10
|
+
* traffic and would reach a deployed environment before anyone saw it. Compiling every registered
|
|
11
|
+
* component here moves that to the build, and names the component when it happens.
|
|
12
|
+
*
|
|
13
|
+
* The rules a union has to satisfy, all of which `synthesizeDiscriminator` in the adapter already
|
|
14
|
+
* enforces before emitting a discriminator: `oneOf` members resolving to object schemas, a tag
|
|
15
|
+
* property present and REQUIRED in every branch, and a `const` or single-valued `enum` for the tag
|
|
16
|
+
* in each. A hand-written `.meta({ discriminator })` gets no such check when it is written, which
|
|
17
|
+
* is the case this test exists for.
|
|
18
|
+
*
|
|
19
|
+
* Generating a validator for each of the ~1000 components is real work: well under a second on a
|
|
20
|
+
* developer machine, ~8s on a loaded CI runner, past vitest's 5s default, hence the explicit budget
|
|
21
|
+
* below. It is generous on purpose — an unsupported discriminator makes AJV THROW during
|
|
22
|
+
* compilation, so a genuine regression is caught by the try/catch and never by the clock.
|
|
23
|
+
*/
|
|
24
|
+
const COMPILE_ALL_TIMEOUT_MS = 60_000;
|
|
25
|
+
|
|
26
|
+
describe('published discriminators', () => {
|
|
27
|
+
it(
|
|
28
|
+
'compiles every registered component under AJV discriminator support',
|
|
29
|
+
() => {
|
|
30
|
+
const failures: string[] = [];
|
|
31
|
+
for (const name of Object.keys(ApiSchemaComponents)) {
|
|
32
|
+
try {
|
|
33
|
+
// Compilation is the subject; the value only has to reach the validator. An
|
|
34
|
+
// invalid result is expected and irrelevant.
|
|
35
|
+
validateApiResponse(name as ApiComponentName, undefined);
|
|
36
|
+
} catch (err: unknown) {
|
|
37
|
+
failures.push(`${name}: ${err instanceof Error ? err.message : String(err)}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
expect(failures, `components AJV could not compile:\n${failures.join('\n')}`).toEqual([]);
|
|
41
|
+
},
|
|
42
|
+
COMPILE_ALL_TIMEOUT_MS,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
it('keeps discriminator mapping in the published components for generated clients', () => {
|
|
46
|
+
// AJV's copy drops `mapping`; the OpenAPI document must not. A Java or Go client reads the
|
|
47
|
+
// mapping to pick a concrete subtype, so losing it here would be a silent codegen
|
|
48
|
+
// regression that no schema assertion elsewhere would catch.
|
|
49
|
+
const union = ApiSchemaComponents.ToolCollectionObject as {
|
|
50
|
+
discriminator?: { propertyName?: string; mapping?: Record<string, string> };
|
|
51
|
+
};
|
|
52
|
+
expect(union.discriminator?.propertyName).toBe('type');
|
|
53
|
+
expect(union.discriminator?.mapping).toMatchObject({
|
|
54
|
+
mcp: '#/components/schemas/MCPToolCollectionObject',
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('reports only the tagged branch when a legacy MCP collection is missing its id', () => {
|
|
59
|
+
// The `hubspot-mcp-dev` shape: created before the write path required `id`, still served by
|
|
60
|
+
// the five-level resolution chain in fetch-tools.ts. Without discriminator support AJV also
|
|
61
|
+
// reported `oauth_app` as an undeclared property and `type` as not matching a constant —
|
|
62
|
+
// both from the vertesia_sdk branch this value never claimed to be.
|
|
63
|
+
const legacy = {
|
|
64
|
+
type: 'mcp',
|
|
65
|
+
name: 'HubSpot',
|
|
66
|
+
namespace: 'hubspot',
|
|
67
|
+
description: 'HubSpot MCP',
|
|
68
|
+
auth: 'oauth',
|
|
69
|
+
url: 'https://mcp.hubspot.com',
|
|
70
|
+
oauth_app: 'hubspot',
|
|
71
|
+
};
|
|
72
|
+
const result = validateApiResponse('ToolCollectionObject', legacy);
|
|
73
|
+
expect(result.valid).toBe(false);
|
|
74
|
+
if (result.valid) return;
|
|
75
|
+
expect(result.errors).toEqual(["/ must have required property 'id'"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('rejects a value whose tag names no branch', () => {
|
|
79
|
+
const result = validateApiResponse('ToolCollectionObject', { type: 'nonesuch', url: 'https://example.com' });
|
|
80
|
+
expect(result.valid).toBe(false);
|
|
81
|
+
if (result.valid) return;
|
|
82
|
+
expect(result.errors.join('; ')).toContain('tag');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -80,7 +80,7 @@ import {
|
|
|
80
80
|
SystemRoleDefinitionArraySchema,
|
|
81
81
|
} from './access-control.js';
|
|
82
82
|
import { AccountSchema, StripeBillingStatusResponseSchema, UpdateAccountPayloadSchema } from './account.js';
|
|
83
|
-
import { findUnprunablePaths, type JsonObject, pruneToSchema, toOpenApiComponents } from './adapter.js';
|
|
83
|
+
import { findUnprunablePaths, isPlainObject, type JsonObject, pruneToSchema, toOpenApiComponents } from './adapter.js';
|
|
84
84
|
import * as AgentRunSchemas from './agent-runs.js';
|
|
85
85
|
import {
|
|
86
86
|
AnalyticsAxisSchema,
|
|
@@ -3356,14 +3356,88 @@ const validators = new Map<string, ValidateFunction>();
|
|
|
3356
3356
|
*/
|
|
3357
3357
|
let ajvInstance: Ajv2020 | undefined;
|
|
3358
3358
|
|
|
3359
|
+
/**
|
|
3360
|
+
* The published components as AJV can consume them: the same schemas, with the two adjustments
|
|
3361
|
+
* AJV's discriminator support requires and the OpenAPI document must not have.
|
|
3362
|
+
*
|
|
3363
|
+
* `discriminator: true` below is what makes AJV validate a discriminated union through the branch
|
|
3364
|
+
* its tag names, instead of trying every branch and reporting all of their failures at once. The
|
|
3365
|
+
* difference is not cosmetic — a legacy MCP tool collection missing `id` used to report the real
|
|
3366
|
+
* error alongside two impossible ones from the branch it was never meant to match:
|
|
3367
|
+
*
|
|
3368
|
+
* / must have required property 'id' <- the real one
|
|
3369
|
+
* / must NOT have additional properties: oauth_app <- vertesia_sdk branch
|
|
3370
|
+
* /type must be equal to constant <- vertesia_sdk branch
|
|
3371
|
+
* / must match exactly one schema in oneOf
|
|
3372
|
+
*
|
|
3373
|
+
* AJV rejects `mapping` outright ("discriminator: mapping is not supported"), because it derives
|
|
3374
|
+
* the tag-to-branch map from each branch's own `const`/`enum` instead. The OpenAPI document still
|
|
3375
|
+
* needs the mapping — a generated Java or Go client reads it to pick a concrete subtype — so both
|
|
3376
|
+
* fixups here apply to AJV's copy only, and `ApiSchemaComponents` is published exactly as built.
|
|
3377
|
+
*
|
|
3378
|
+
* AJV's remaining requirements — the tag required in every branch, carrying `const` or a
|
|
3379
|
+
* single-valued `enum`, with the union node typed as an object — are what `synthesizeDiscriminator`
|
|
3380
|
+
* in the adapter already checks before it emits a discriminator at all, and what the hand-written
|
|
3381
|
+
* `.meta({ discriminator })` declarations restate. `api-discriminators.test.ts` compiles every
|
|
3382
|
+
* registered component so a union satisfying neither fails the build rather than the request.
|
|
3383
|
+
*/
|
|
3384
|
+
function toAjvComponents(): Record<string, JsonObject> {
|
|
3385
|
+
const schemas = structuredClone(ApiSchemaComponents) as Record<string, JsonObject>;
|
|
3386
|
+
const resolve = (value: unknown): JsonObject | undefined => {
|
|
3387
|
+
if (!isPlainObject(value)) return undefined;
|
|
3388
|
+
const ref = value.$ref;
|
|
3389
|
+
if (typeof ref !== 'string' || !ref.startsWith(COMPONENT_REF_PREFIX)) return value;
|
|
3390
|
+
return schemas[ref.slice(COMPONENT_REF_PREFIX.length)];
|
|
3391
|
+
};
|
|
3392
|
+
|
|
3393
|
+
visitSchemaNodes(schemas, (node) => {
|
|
3394
|
+
const discriminator = node.discriminator;
|
|
3395
|
+
if (!isPlainObject(discriminator) || !Array.isArray(node.oneOf)) return;
|
|
3396
|
+
// AJV derives the tag-to-branch map from each branch's own literal, so `mapping` is both
|
|
3397
|
+
// redundant and rejected.
|
|
3398
|
+
delete discriminator.mapping;
|
|
3399
|
+
|
|
3400
|
+
const tag = discriminator.propertyName;
|
|
3401
|
+
if (typeof tag !== 'string') return;
|
|
3402
|
+
for (const member of node.oneOf) {
|
|
3403
|
+
const branch = resolve(member);
|
|
3404
|
+
const properties = branch && isPlainObject(branch.properties) ? branch.properties : undefined;
|
|
3405
|
+
const tagSchema = properties?.[tag];
|
|
3406
|
+
if (!isPlainObject(tagSchema) || tagSchema.const !== undefined || tagSchema.enum !== undefined) continue;
|
|
3407
|
+
// The tag is a `$ref` to a named literal component — `TaskType_ACTIVITY`,
|
|
3408
|
+
// `SupportedIntegrations_gladia` — because the literal carries its own `.meta({ id })`.
|
|
3409
|
+
// AJV reads `properties/<tag>` looking for `const` or `enum` and does not follow a
|
|
3410
|
+
// `$ref` to find one, so it refuses to compile the union at all. Restating the resolved
|
|
3411
|
+
// literal ALONGSIDE the `$ref` is validation-neutral: `$ref` has no special precedence
|
|
3412
|
+
// in 2020-12, both keywords apply, and they carry the same value by construction.
|
|
3413
|
+
const target = resolve(tagSchema);
|
|
3414
|
+
if (!target) continue;
|
|
3415
|
+
if (target.const !== undefined) tagSchema.const = target.const;
|
|
3416
|
+
else if (Array.isArray(target.enum)) tagSchema.enum = target.enum;
|
|
3417
|
+
}
|
|
3418
|
+
});
|
|
3419
|
+
return schemas;
|
|
3420
|
+
}
|
|
3421
|
+
|
|
3422
|
+
/** Every object node in the component graph, parents before children. */
|
|
3423
|
+
function visitSchemaNodes(value: unknown, visit: (node: JsonObject) => void): void {
|
|
3424
|
+
if (Array.isArray(value)) {
|
|
3425
|
+
for (const item of value) visitSchemaNodes(item, visit);
|
|
3426
|
+
return;
|
|
3427
|
+
}
|
|
3428
|
+
if (!isPlainObject(value)) return;
|
|
3429
|
+
visit(value);
|
|
3430
|
+
for (const item of Object.values(value)) visitSchemaNodes(item, visit);
|
|
3431
|
+
}
|
|
3432
|
+
|
|
3359
3433
|
function getAjv(): Ajv2020 {
|
|
3360
3434
|
if (ajvInstance) return ajvInstance;
|
|
3361
|
-
const ajv = new Ajv2020({ strictSchema: false, allErrors: true });
|
|
3435
|
+
const ajv = new Ajv2020({ strictSchema: false, allErrors: true, discriminator: true });
|
|
3362
3436
|
// Without this, AJV treats `format` as an annotation and ignores it, so a `date-time` property
|
|
3363
3437
|
// would document a constraint nothing checks — the exact spec/enforcement gap this design is
|
|
3364
3438
|
// meant to close.
|
|
3365
3439
|
addFormats(ajv);
|
|
3366
|
-
ajv.addSchema({ $id: 'vertesia://openapi', components: { schemas:
|
|
3440
|
+
ajv.addSchema({ $id: 'vertesia://openapi', components: { schemas: toAjvComponents() } });
|
|
3367
3441
|
ajvInstance = ajv;
|
|
3368
3442
|
return ajv;
|
|
3369
3443
|
}
|
|
@@ -3376,20 +3450,101 @@ function getValidator(name: ApiComponentName): ValidateFunction {
|
|
|
3376
3450
|
return validate;
|
|
3377
3451
|
}
|
|
3378
3452
|
|
|
3379
|
-
function
|
|
3380
|
-
|
|
3453
|
+
function additionalProperty(error: { params?: unknown }): string | undefined {
|
|
3454
|
+
// AJV writes "must NOT have additional properties" and puts the offending key in `params`,
|
|
3455
|
+
// so the message alone says a body is wrong without saying which property made it wrong.
|
|
3456
|
+
// Every other keyword names its subject already — `required` quotes the missing property,
|
|
3457
|
+
// `enum` and `type` describe the value in place — and this is the one a caller most often
|
|
3458
|
+
// trips, so it is the one worth spelling out rather than reformatting all of them.
|
|
3459
|
+
return (error.params as { additionalProperty?: string } | undefined)?.additionalProperty;
|
|
3460
|
+
}
|
|
3461
|
+
|
|
3462
|
+
/**
|
|
3463
|
+
* One validation failure, with the undeclared property names kept AS NAMES.
|
|
3464
|
+
*
|
|
3465
|
+
* The structure exists because the rendered line cannot be taken apart again. Property names are
|
|
3466
|
+
* arbitrary JSON strings: `{"customer secret token": 1}` and `{"a, b": 1}` are both valid, so
|
|
3467
|
+
* neither a space nor a comma is a reliable separator once the names have been joined. Any consumer
|
|
3468
|
+
* that needs to shorten the list — the HTTP boundary does, the log does not — has to do it here,
|
|
3469
|
+
* on the array, where a name can be dropped whole.
|
|
3470
|
+
*/
|
|
3471
|
+
export interface ApiValidationIssue {
|
|
3472
|
+
/**
|
|
3473
|
+
* AJV's `instancePath` for the failing value, with `'/'` substituted for the root.
|
|
3474
|
+
*
|
|
3475
|
+
* Not quite a JSON Pointer: the pointer for a document root is the empty string, which reads as
|
|
3476
|
+
* a missing path in a message. `'/'` is a display sentinel, so treat this as human-facing rather
|
|
3477
|
+
* than as something to resolve against the payload.
|
|
3478
|
+
*/
|
|
3479
|
+
path: string;
|
|
3480
|
+
/** AJV's own message, e.g. `must NOT have additional properties`. */
|
|
3481
|
+
message: string;
|
|
3482
|
+
/** Undeclared property names at {@link path}, in the order AJV reported them. */
|
|
3483
|
+
undeclared?: string[];
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3486
|
+
/**
|
|
3487
|
+
* The failures, with all undeclared properties at a given path gathered into ONE issue.
|
|
3488
|
+
*
|
|
3489
|
+
* AJV reports `additionalProperties` per property, so a value carrying a whole foreign object —
|
|
3490
|
+
* a Mongoose document that reached the response mapper unmapped, say — produces one error per own
|
|
3491
|
+
* key and buries every other failure in the payload. Gathering is lossless: every name is kept, in
|
|
3492
|
+
* the order AJV found it, on the issue for the path it belongs to.
|
|
3493
|
+
*/
|
|
3494
|
+
function collectIssues(validate: ValidateFunction): ApiValidationIssue[] {
|
|
3495
|
+
const errors = validate.errors ?? [];
|
|
3496
|
+
const undeclaredByPath = new Map<string, Set<string>>();
|
|
3497
|
+
for (const error of errors) {
|
|
3498
|
+
const additional = additionalProperty(error);
|
|
3499
|
+
if (additional === undefined) continue;
|
|
3381
3500
|
const where = error.instancePath || '/';
|
|
3382
|
-
|
|
3383
|
-
//
|
|
3384
|
-
//
|
|
3385
|
-
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3501
|
+
const names = undeclaredByPath.get(where);
|
|
3502
|
+
// A union whose branches all reject the same property reports it once per branch. The name
|
|
3503
|
+
// is the same fact each time, so it is listed once.
|
|
3504
|
+
if (names) names.add(additional);
|
|
3505
|
+
else undeclaredByPath.set(where, new Set([additional]));
|
|
3506
|
+
}
|
|
3507
|
+
|
|
3508
|
+
const gathered = new Set<string>();
|
|
3509
|
+
const issues: ApiValidationIssue[] = [];
|
|
3510
|
+
for (const error of errors) {
|
|
3511
|
+
const path = error.instancePath || '/';
|
|
3512
|
+
const message = error.message ?? 'is invalid';
|
|
3513
|
+
if (additionalProperty(error) === undefined) {
|
|
3514
|
+
issues.push({ path, message });
|
|
3515
|
+
continue;
|
|
3516
|
+
}
|
|
3517
|
+
if (gathered.has(path)) continue;
|
|
3518
|
+
gathered.add(path);
|
|
3519
|
+
issues.push({ path, message, undeclared: [...(undeclaredByPath.get(path) ?? [])] });
|
|
3520
|
+
}
|
|
3521
|
+
return issues;
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
/**
|
|
3525
|
+
* The complete rendering of one issue — every name, no budget.
|
|
3526
|
+
*
|
|
3527
|
+
* This is what gets logged. A caller-facing message renders from {@link ApiValidationIssue} itself
|
|
3528
|
+
* with a length limit rather than shortening this string; see `reportableErrors` in the enforcer.
|
|
3529
|
+
*/
|
|
3530
|
+
export function renderApiValidationIssue(issue: ApiValidationIssue): string {
|
|
3531
|
+
const head = `${issue.path} ${issue.message}`;
|
|
3532
|
+
return issue.undeclared?.length ? `${head}: ${issue.undeclared.join(', ')}` : head;
|
|
3533
|
+
}
|
|
3534
|
+
|
|
3535
|
+
/** The failed branch of every validator here, so the two views cannot be built inconsistently. */
|
|
3536
|
+
function invalidResult(validate: ValidateFunction): { valid: false; errors: string[]; issues: ApiValidationIssue[] } {
|
|
3537
|
+
const issues = collectIssues(validate);
|
|
3538
|
+
return { valid: false, errors: issues.map(renderApiValidationIssue), issues };
|
|
3390
3539
|
}
|
|
3391
3540
|
|
|
3392
|
-
export type ValidateApiPayloadResult<T> =
|
|
3541
|
+
export type ValidateApiPayloadResult<T> =
|
|
3542
|
+
| { valid: true; data: T }
|
|
3543
|
+
/**
|
|
3544
|
+
* `errors` is the rendered form, complete and ready to log; `issues` is the same failures with
|
|
3545
|
+
* the property names still separable. Shorten from `issues`, never from `errors`.
|
|
3546
|
+
*/
|
|
3547
|
+
| { valid: false; errors: string[]; issues: ApiValidationIssue[] };
|
|
3393
3548
|
|
|
3394
3549
|
/**
|
|
3395
3550
|
* Validates an untyped request body against the component the endpoint publishes.
|
|
@@ -3411,7 +3566,7 @@ export function validateApiRequest<N extends ApiComponentName>(
|
|
|
3411
3566
|
if (validate(value)) {
|
|
3412
3567
|
return { valid: true, data: value as ApiComponentType<N> };
|
|
3413
3568
|
}
|
|
3414
|
-
return
|
|
3569
|
+
return invalidResult(validate);
|
|
3415
3570
|
}
|
|
3416
3571
|
|
|
3417
3572
|
/**
|
|
@@ -3429,7 +3584,7 @@ export function validateApiResponse<N extends ApiComponentName>(
|
|
|
3429
3584
|
if (validate(value)) {
|
|
3430
3585
|
return { valid: true, data: value as ApiComponentType<N> };
|
|
3431
3586
|
}
|
|
3432
|
-
return
|
|
3587
|
+
return invalidResult(validate);
|
|
3433
3588
|
}
|
|
3434
3589
|
|
|
3435
3590
|
/**
|
|
@@ -3457,7 +3612,9 @@ export function pruneApiResponse<N extends ApiComponentName>(name: N, value: Api
|
|
|
3457
3612
|
return pruneToSchema(value, { $ref: apiComponentRef(name) }, ApiSchemaComponents) as ApiComponentType<N>;
|
|
3458
3613
|
}
|
|
3459
3614
|
|
|
3460
|
-
export type PruneAndValidateResult<T> =
|
|
3615
|
+
export type PruneAndValidateResult<T> =
|
|
3616
|
+
| { valid: true; data: T }
|
|
3617
|
+
| { valid: false; data: unknown; errors: string[]; issues: ApiValidationIssue[] };
|
|
3461
3618
|
|
|
3462
3619
|
/**
|
|
3463
3620
|
* Prunes an untyped payload and validates the result against the published component.
|
|
@@ -3478,7 +3635,7 @@ export function pruneAndValidateApiResponse<N extends ApiComponentName>(
|
|
|
3478
3635
|
if (validate(pruned)) {
|
|
3479
3636
|
return { valid: true, data: pruned as ApiComponentType<N> };
|
|
3480
3637
|
}
|
|
3481
|
-
return {
|
|
3638
|
+
return { ...invalidResult(validate), data: pruned };
|
|
3482
3639
|
}
|
|
3483
3640
|
|
|
3484
3641
|
/**
|
|
@@ -273,6 +273,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
273
273
|
type: 'string',
|
|
274
274
|
},
|
|
275
275
|
},
|
|
276
|
+
include_thoughts: {
|
|
277
|
+
type: 'boolean',
|
|
278
|
+
},
|
|
276
279
|
},
|
|
277
280
|
required: ['_option_id'],
|
|
278
281
|
additionalProperties: false,
|
|
@@ -345,6 +348,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
345
348
|
type: 'string',
|
|
346
349
|
},
|
|
347
350
|
},
|
|
351
|
+
include_thoughts: {
|
|
352
|
+
type: 'boolean',
|
|
353
|
+
},
|
|
348
354
|
},
|
|
349
355
|
required: ['_option_id'],
|
|
350
356
|
additionalProperties: false,
|
|
@@ -371,6 +377,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
371
377
|
type: 'string',
|
|
372
378
|
},
|
|
373
379
|
},
|
|
380
|
+
include_thoughts: {
|
|
381
|
+
type: 'boolean',
|
|
382
|
+
},
|
|
374
383
|
},
|
|
375
384
|
required: ['_option_id'],
|
|
376
385
|
additionalProperties: false,
|
|
@@ -433,6 +442,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
433
442
|
type: 'string',
|
|
434
443
|
},
|
|
435
444
|
},
|
|
445
|
+
include_thoughts: {
|
|
446
|
+
type: 'boolean',
|
|
447
|
+
},
|
|
436
448
|
},
|
|
437
449
|
required: ['_option_id'],
|
|
438
450
|
additionalProperties: false,
|
|
@@ -515,6 +527,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
515
527
|
type: 'string',
|
|
516
528
|
enum: ['low', 'high', 'auto'],
|
|
517
529
|
},
|
|
530
|
+
include_thoughts: {
|
|
531
|
+
type: 'boolean',
|
|
532
|
+
},
|
|
518
533
|
},
|
|
519
534
|
required: ['_option_id'],
|
|
520
535
|
additionalProperties: false,
|
|
@@ -541,6 +556,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
541
556
|
type: 'string',
|
|
542
557
|
},
|
|
543
558
|
},
|
|
559
|
+
include_thoughts: {
|
|
560
|
+
type: 'boolean',
|
|
561
|
+
},
|
|
544
562
|
},
|
|
545
563
|
required: ['_option_id'],
|
|
546
564
|
additionalProperties: false,
|
|
@@ -567,6 +585,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
567
585
|
type: 'string',
|
|
568
586
|
},
|
|
569
587
|
},
|
|
588
|
+
include_thoughts: {
|
|
589
|
+
type: 'boolean',
|
|
590
|
+
},
|
|
570
591
|
},
|
|
571
592
|
required: ['_option_id'],
|
|
572
593
|
additionalProperties: false,
|
|
@@ -1169,6 +1190,12 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
1169
1190
|
max_tokens: {
|
|
1170
1191
|
type: 'number',
|
|
1171
1192
|
},
|
|
1193
|
+
effort: {
|
|
1194
|
+
$ref: '#/$defs/ReasoningEffort',
|
|
1195
|
+
},
|
|
1196
|
+
reasoning_effort: {
|
|
1197
|
+
$ref: '#/$defs/ReasoningEffort',
|
|
1198
|
+
},
|
|
1172
1199
|
temperature: {
|
|
1173
1200
|
type: 'number',
|
|
1174
1201
|
},
|
|
@@ -1191,6 +1218,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
1191
1218
|
type: 'string',
|
|
1192
1219
|
enum: ['low', 'high', 'auto'],
|
|
1193
1220
|
},
|
|
1221
|
+
include_thoughts: {
|
|
1222
|
+
type: 'boolean',
|
|
1223
|
+
},
|
|
1194
1224
|
},
|
|
1195
1225
|
required: ['_option_id'],
|
|
1196
1226
|
additionalProperties: false,
|
|
@@ -1221,6 +1251,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
1221
1251
|
type: 'string',
|
|
1222
1252
|
enum: ['low', 'high', 'auto'],
|
|
1223
1253
|
},
|
|
1254
|
+
include_thoughts: {
|
|
1255
|
+
type: 'boolean',
|
|
1256
|
+
},
|
|
1224
1257
|
},
|
|
1225
1258
|
required: ['_option_id'],
|
|
1226
1259
|
additionalProperties: false,
|
|
@@ -1264,6 +1297,9 @@ export const ContentTypeIntakePolicySchema: JSONObject = {
|
|
|
1264
1297
|
type: 'string',
|
|
1265
1298
|
},
|
|
1266
1299
|
},
|
|
1300
|
+
include_thoughts: {
|
|
1301
|
+
type: 'boolean',
|
|
1302
|
+
},
|
|
1267
1303
|
},
|
|
1268
1304
|
required: ['_option_id'],
|
|
1269
1305
|
additionalProperties: false,
|