Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
96fe940
upd
dimaMachina Feb 4, 2026
c8fc0b8
upd
dimaMachina Feb 4, 2026
207666f
upd
dimaMachina Feb 4, 2026
6bef309
upd
dimaMachina Feb 4, 2026
f9a0c49
upd
dimaMachina Feb 4, 2026
d8821b8
upd
dimaMachina Feb 4, 2026
f5de369
upd
dimaMachina Feb 4, 2026
2232e41
typecheck is ok now
dimaMachina Feb 4, 2026
68471b6
wip custom headers dialog
dimaMachina Feb 4, 2026
e9d6af3
upddd
dimaMachina Feb 4, 2026
9a282d1
upddd
dimaMachina Feb 4, 2026
39ccf59
upddd
dimaMachina Feb 4, 2026
c572bd7
brand color
dimaMachina Feb 4, 2026
b68a294
upd
dimaMachina Feb 4, 2026
c1c46eb
validate on mount
dimaMachina Feb 4, 2026
7ec3606
polish
dimaMachina Feb 4, 2026
b703149
fix lint
dimaMachina Feb 4, 2026
1f39d29
format
dimaMachina Feb 4, 2026
3063376
review fixes
dimaMachina Feb 4, 2026
fdbe15d
feat(pr-review): add clickable links to inline comments in review sum…
nick-inkeep Feb 4, 2026
a736a90
Revert "fix(agents-core): remove refine call in resource id schema (#…
dimaMachina Feb 4, 2026
beb813c
fix: pin claude-code-action to SDK 0.2.25 to avoid AJV crash (#1716)
nick-inkeep Feb 4, 2026
f7c705c
bump zod to latest 4.3.6 and fix `.omit() cannot be used on object sc…
dimaMachina Feb 4, 2026
fc8e1d6
Version Packages (#1701)
github-actions[bot] Feb 4, 2026
96fbe28
feat: add Vercel production deployment workflow (#1679)
amikofalvy Feb 4, 2026
af84da3
remove cursor specific rules in favor or skills and agents.md (#1717)
amikofalvy Feb 4, 2026
0b4d47c
chore: trigger release for all packages (#1718)
amikofalvy Feb 5, 2026
0398815
Version Packages (#1719)
github-actions[bot] Feb 5, 2026
9cca3ea
updating release action (#1720)
amikofalvy Feb 5, 2026
11f869f
fix: add --scope flag to Vercel CLI commands in production workflow (…
amikofalvy Feb 5, 2026
c04dcda
fix: use staged production deployments in Vercel workflow (#1722)
amikofalvy Feb 5, 2026
756adf7
fix: add --archive=tgz to prevent CLI hanging during deploy (#1724)
amikofalvy Feb 5, 2026
eb3c969
fix: use repository variables instead of secrets for non-sensitive va…
amikofalvy Feb 5, 2026
8031b8e
fix: simplify Vercel workflow to use direct production deploy (#1725)
amikofalvy Feb 5, 2026
0f87f6a
fix: use secrets for VERCEL_ORG_ID (#1726)
amikofalvy Feb 5, 2026
372bab8
apply review
dimaMachina Feb 5, 2026
6f8f73d
Merge branch 'main' into prd-4917
dimaMachina Feb 5, 2026
45d6053
pnpm i
dimaMachina Feb 5, 2026
0f8b1d3
polish
dimaMachina Feb 5, 2026
1804941
Merge branch 'main' into prd-4917
dimaMachina Feb 5, 2026
67702a8
format
dimaMachina Feb 5, 2026
6eac16a
Rename convert-json-schema-to-zod.ts to convert-json-schema-to-zod.te…
dimaMachina Feb 5, 2026
0a1c828
add tests
dimaMachina Feb 5, 2026
8087665
wip tests
dimaMachina Feb 5, 2026
91b79a0
wip tests
dimaMachina Feb 5, 2026
2a071c1
wip tests
dimaMachina Feb 5, 2026
5f33745
upd
dimaMachina Feb 5, 2026
3a52f97
upd
dimaMachina Feb 5, 2026
f9a57a2
upd
dimaMachina Feb 5, 2026
9d9a440
upd
dimaMachina Feb 5, 2026
a29ccd5
polish error names
dimaMachina Feb 5, 2026
92e2946
upd
dimaMachina Feb 5, 2026
11260e1
Merge branch 'main' into prd-4917
dimaMachina Feb 5, 2026
b9452b8
move to __tests__
dimaMachina Feb 5, 2026
1928059
rm jsonSchemaToZod
dimaMachina Feb 5, 2026
5a2153a
rm jsonSchemaToZod
dimaMachina Feb 5, 2026
990bf13
rm jsonSchemaToZod
dimaMachina Feb 5, 2026
274011d
rm jsonSchemaToZod
dimaMachina Feb 5, 2026
0a98b3f
format
dimaMachina Feb 5, 2026
95facf0
chore: add changeset for custom headers validation feature
github-actions[bot] Feb 5, 2026
d5b3ece
Merge branch 'prd-4917' into prd-5966
dimaMachina Feb 5, 2026
c500854
chore: add changeset for jsonSchemaToZod removal
github-actions[bot] Feb 5, 2026
f5ccb4f
Merge branch 'main' into prd-5966
dimaMachina Feb 5, 2026
7af0a13
fix lint
dimaMachina Feb 5, 2026
6bbb8b7
Update validation.test.ts
dimaMachina Feb 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/tidy-foxes-refactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@inkeep/agents-api": patch
"@inkeep/agents-core": patch
---

Replace custom jsonSchemaToZod implementation with Zod's native z.fromJSONSchema() method
69 changes: 1 addition & 68 deletions agents-api/src/domains/evals/services/EvaluationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,73 +33,6 @@ import { getLogger } from '../../../logger';

const logger = getLogger('EvaluationService');

/**
* Converts JSON Schema objects to Zod schema types
*/
function jsonSchemaToZod(jsonSchema: any): z.ZodType<any> {
if (!jsonSchema || typeof jsonSchema !== 'object') {
logger.warn({ jsonSchema }, 'Invalid JSON schema provided, using string fallback');
return z.string();
}

switch (jsonSchema.type) {
case 'object':
if (jsonSchema.properties) {
const shape: Record<string, z.ZodType<any>> = {};
const required = jsonSchema.required || [];

for (const [key, prop] of Object.entries(jsonSchema.properties)) {
const propSchema = prop as Record<string, unknown>;
let zodType = jsonSchemaToZod(propSchema);

// Add description if present
if (propSchema.description) {
zodType = zodType.describe(String(propSchema.description));
}

// Mark as optional if not in required array
if (!required.includes(key)) {
zodType = zodType.optional();
}

shape[key] = zodType;
}
return z.object(shape);
}
return z.record(z.string(), z.unknown());

case 'array': {
const itemSchema = jsonSchema.items ? jsonSchemaToZod(jsonSchema.items) : z.unknown();
return z.array(itemSchema);
}

case 'string':
return z.string();

case 'number':
return z.number();

case 'integer':
return z.number().int();

case 'boolean':
return z.boolean();

case 'null':
return z.null();

default:
logger.warn(
{
unsupportedType: jsonSchema.type,
schema: jsonSchema,
},
'Unsupported JSON schema type, using unknown validation'
);
return z.unknown();
}
}

export interface RunDatasetItemOptions {
tenantId: string;
projectId: string;
Expand Down Expand Up @@ -1137,7 +1070,7 @@ Return your evaluation as a JSON object matching the schema above.`;
// Convert JSON schema to Zod schema
let resultSchema: z.ZodType<any>;
try {
resultSchema = jsonSchemaToZod(schema);
resultSchema = z.fromJSONSchema(schema);
logger.info(
{
schemaType: typeof schema,
Expand Down
31 changes: 15 additions & 16 deletions agents-api/src/domains/run/agents/Agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
getLedgerArtifacts,
isGithubWorkAppTool,
JsonTransformer,
jsonSchemaToZod,
listTaskIdsByContextId,
MCPServerType,
type MCPToolConfig,
Expand Down Expand Up @@ -1393,7 +1392,9 @@ export class Agent {
continue;
}

const zodSchema = jsonSchemaToZod(functionData.inputSchema);
const zodSchema = functionData.inputSchema
? z.fromJSONSchema(functionData.inputSchema)
: z.string();
Comment on lines +1395 to +1397
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backward compatibility, previous jsonSchemaToZod returns z.string for falsy values

Comment on lines +1395 to +1397
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAJOR Missing try-catch for user-controlled inputSchema

functionData.inputSchema has NO upstream JSON Schema validation — it only validates as z.record(z.string(), z.unknown()) at the API layer, allowing malformed schemas to reach this code. When z.fromJSONSchema() throws, the entire getFunctionTools() method fails silently, returning empty tools.

The same codebase has proper error handling at line 2198-2214 for the exact same reason.

Suggested change
const zodSchema = functionData.inputSchema
? z.fromJSONSchema(functionData.inputSchema)
: z.string();
let zodSchema: z.ZodType<any>;
try {
zodSchema = functionData.inputSchema
? z.fromJSONSchema(functionData.inputSchema)
: z.string();
} catch (error) {
logger.warn({ functionId, error }, 'Failed to convert inputSchema, using permissive fallback');
zodSchema = z.record(z.string(), z.unknown());
}

const toolPolicies = (functionToolDef as any).toolPolicies as
| Record<string, { needsApproval?: boolean }>
| null
Expand Down Expand Up @@ -1575,7 +1576,7 @@ export class Agent {

const result = await sandboxExecutor.executeFunctionTool(
functionToolDef.id,
finalArgs,
finalArgs as Record<string, unknown>,
{
description: functionToolDef.description || functionToolDef.name,
inputSchema: functionData.inputSchema || {},
Expand Down Expand Up @@ -2196,7 +2197,7 @@ export class Agent {
let inputSchema: any;
try {
inputSchema = override.schema
? jsonSchemaToZod(override.schema)
? z.fromJSONSchema(override.schema)
: (toolDef as any).inputSchema;
} catch (schemaError) {
logger.error(
Expand Down Expand Up @@ -3654,18 +3655,16 @@ ${output}${structureHintsFormatted}`;
private buildDataComponentsSchema() {
const componentSchemas: z.ZodType<any>[] = [];

if (this.config.dataComponents && this.config.dataComponents.length > 0) {
this.config.dataComponents.forEach((dc) => {
const propsSchema = jsonSchemaToZod(dc.props);
componentSchemas.push(
z.object({
id: z.string(),
name: z.literal(dc.name),
props: propsSchema,
})
);
});
}
this.config.dataComponents?.forEach((dc) => {
const propsSchema = dc.props ? z.fromJSONSchema(dc.props) : z.string();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

componentSchemas.push(
z.object({
id: z.string(),
name: z.literal(dc.name),
props: propsSchema,
})
);
});

if (this.artifactComponents.length > 0) {
const artifactCreateSchemas = ArtifactCreateSchema.getSchemas(this.artifactComponents);
Expand Down
77 changes: 11 additions & 66 deletions agents-api/src/domains/run/utils/artifact-component-schema.ts
Original file line number Diff line number Diff line change
@@ -1,73 +1,18 @@
import { z } from '@hono/zod-openapi';
import {
type ArtifactComponentApiInsert,
type ArtifactComponentApiSelect,
type DataComponentInsert,
jsonSchemaToZod,
import type {
ArtifactComponentApiInsert,
ArtifactComponentApiSelect,
DataComponentInsert,
} from '@inkeep/agents-core';
import { getLogger } from '../../../logger';
import type { JSONSchema } from 'zod/v4/core';
import { SchemaProcessor } from './SchemaProcessor';
import type { ExtendedJsonSchema } from './schema-validation';

const _logger = getLogger('ArtifactComponentSchema');

/**
* Converts artifact component configurations to Zod schema for structured generation
*/
export function createArtifactComponentsSchema(artifactComponents?: ArtifactComponentApiSelect[]) {
// Convert artifact component configs to a union schema
const componentSchemas =
artifactComponents?.map((component) => {
// Use the unified props schema directly - remove inPreview flags for LLM
const cleanSchema = component.props
? removePreviewFlags(component.props as ExtendedJsonSchema)
: {};
const propsSchema = jsonSchemaToZod(cleanSchema);

return z
.object({
id: z.string().describe(component.id),
name: z.literal(component.name).describe(component.name),
props: propsSchema,
})
.describe(`${component.name}: ${component.description}`);
}) || [];

// Return union of all component schemas - z.union requires at least 2 schemas
if (componentSchemas.length === 0) {
return z.object({}); // Empty object for no components
}
if (componentSchemas.length === 1) {
return componentSchemas[0]; // Single schema doesn't need union
}
return z.union(componentSchemas as any); // Safe union with 2+ schemas
}

/**
* Remove inPreview flags from schema properties (for LLM consumption)
*/
function removePreviewFlags(schema: ExtendedJsonSchema): Record<string, any> {
const cleanSchema = { ...schema };

if (cleanSchema.properties) {
const cleanProperties: Record<string, any> = {};
for (const [key, prop] of Object.entries(cleanSchema.properties)) {
const cleanProp = { ...prop };
delete cleanProp.inPreview;
cleanProperties[key] = cleanProp;
}
cleanSchema.properties = cleanProperties;
}

return cleanSchema;
}

/**
* Standard artifact reference component schema for tool responses
*/
export class ArtifactReferenceSchema {
// Standard artifact props schema - single source of truth
private static readonly ARTIFACT_PROPS_SCHEMA = {
private static readonly ARTIFACT_PROPS_SCHEMA: JSONSchema.BaseSchema = {
type: 'object',
properties: {
artifact_id: {
Expand All @@ -90,7 +35,7 @@ export class ArtifactReferenceSchema {
return z.object({
id: z.string(),
name: z.literal('Artifact'),
props: jsonSchemaToZod(ArtifactReferenceSchema.ARTIFACT_PROPS_SCHEMA),
props: z.fromJSONSchema(ArtifactReferenceSchema.ARTIFACT_PROPS_SCHEMA),
});
}

Expand Down Expand Up @@ -128,7 +73,7 @@ export class ArtifactCreateSchema {
? SchemaProcessor.enhanceSchemaWithJMESPathGuidance(component.props)
: { type: 'object', properties: {} };

const propsSchema = {
const propsSchema: JSONSchema.BaseSchema = {
type: 'object',
properties: {
id: {
Expand Down Expand Up @@ -158,7 +103,7 @@ export class ArtifactCreateSchema {
return z.object({
id: z.string(),
name: z.literal(`ArtifactCreate_${component.name}`),
props: jsonSchemaToZod(propsSchema),
props: z.fromJSONSchema(propsSchema),
});
});
}
Expand All @@ -170,7 +115,7 @@ export class ArtifactCreateSchema {
*/
static getDataComponents(
tenantId: string,
projectId: string = '',
projectId = '',
artifactComponents: Array<ArtifactComponentApiInsert | ArtifactComponentApiSelect>
): DataComponentInsert[] {
return artifactComponents.map((component) => {
Expand All @@ -179,7 +124,7 @@ export class ArtifactCreateSchema {
? SchemaProcessor.enhanceSchemaWithJMESPathGuidance(component.props)
: { type: 'object', properties: {} };

const propsSchema = {
const propsSchema: JSONSchema.BaseSchema = {
type: 'object',
properties: {
id: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 4. Streams NDJSON response back to client
*/

import { jsonSchemaToZod, ModelFactory } from '@inkeep/agents-core';
import { ModelFactory } from '@inkeep/agents-core';
import { Output, streamText } from 'ai';
import type { NextRequest } from 'next/server';
import { z } from 'zod';
Expand Down Expand Up @@ -68,7 +68,9 @@ export async function POST(
const modelConfig = ModelFactory.prepareGenerationConfig(project.models?.base as any);

// Define schema for generated output
const mockDataSchema = jsonSchemaToZod(artifactComponent.props);
const mockDataSchema = artifactComponent.props
? z.fromJSONSchema(artifactComponent.props)
: z.string();
Comment on lines +71 to +73
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const renderSchema = z.object({
component: z.string().describe('The React component code'),
mockData: mockDataSchema.describe('Sample data matching the props schema'),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* 4. Streams NDJSON response back to client
*/

import { jsonSchemaToZod, ModelFactory } from '@inkeep/agents-core';
import { ModelFactory } from '@inkeep/agents-core';
import { Output, streamText } from 'ai';
import type { NextRequest } from 'next/server';
import { z } from 'zod';
Expand Down Expand Up @@ -66,7 +66,7 @@ export async function POST(
// Define schema for generated output
// Dynamically create mockData schema from component's props JSON Schema.
// This ensures Anthropic gets proper types instead of z.any() which it rejects.
const mockDataSchema = jsonSchemaToZod(dataComponent.props);
const mockDataSchema = z.fromJSONSchema(dataComponent.props);
const renderSchema = z.object({
component: z.string().describe('The React component code'),
mockData: mockDataSchema.describe('Sample data matching the props schema'),
Expand Down
2 changes: 1 addition & 1 deletion agents-manage-ui/src/lib/validation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('validation', () => {
{
path: [],
message:
"Error during parsing JSON schema headers: Cannot read properties of null (reading 'const')",
"Error during parsing JSON schema headers: Cannot read properties of null (reading '$schema')",
},
]);
});
Expand Down
3 changes: 1 addition & 2 deletions agents-manage-ui/src/lib/validation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { convertJsonSchemaToZod } from '@inkeep/agents-core/client-exports';
import { z } from 'zod';
import { transformToJson } from '@/lib/json-schema-validation';

Expand Down Expand Up @@ -43,7 +42,7 @@ export function createCustomHeadersSchema(customHeaders: string) {
}
if (customHeaders) {
try {
const customSchema = convertJsonSchemaToZod(JSON.parse(customHeaders));
const customSchema = z.fromJSONSchema(JSON.parse(customHeaders));
const result = customSchema.safeParse(value);
if (result.success) return;
addIssue(ctx, result.error);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';
import type { JSONSchema } from 'zod/v4/core';
import { convertJsonSchemaToZod } from 'zod-from-json-schema';

const jsonSchema: JSONSchema.BaseSchema = {
type: 'object',
Expand All @@ -14,7 +14,7 @@ const jsonSchema: JSONSchema.BaseSchema = {

describe('jsonSchemaToZod', () => {
test('should return json schema', () => {
const zodSchema = convertJsonSchemaToZod(jsonSchema);
const zodSchema = z.fromJSONSchema(jsonSchema);
expect(zodSchema.toJSONSchema()).toStrictEqual({
$schema: 'https://json-schema.org/draft/2020-12/schema',
type: 'object',
Expand Down
Loading