@@ -8,6 +8,7 @@ import process from 'node:process';
88
99import { DAEMON_CLIENT_NAME } from '../daemon/utils.js' ;
1010import { logger } from '../logger.js' ;
11+ import { zod , type ShapeOutput } from '../third_party/index.js' ;
1112
1213import type { LocalState , Persistence } from './persistence.js' ;
1314import { FilePersistence } from './persistence.js' ;
@@ -20,6 +21,99 @@ import {
2021import { WatchdogClient } from './WatchdogClient.js' ;
2122
2223const MS_PER_DAY = 24 * 60 * 60 * 1000 ;
24+ const PARAM_BLOCKLIST = new Set ( [ 'uid' ] ) ;
25+
26+ const SUPPORTED_ZOD_TYPES = [
27+ 'ZodString' ,
28+ 'ZodNumber' ,
29+ 'ZodBoolean' ,
30+ 'ZodArray' ,
31+ 'ZodEnum' ,
32+ ] as const ;
33+ type ZodType = typeof SUPPORTED_ZOD_TYPES [ number ] ;
34+
35+ function isZodType ( type : string ) : type is ZodType {
36+ return SUPPORTED_ZOD_TYPES . includes ( type as ZodType ) ;
37+ }
38+
39+ function getZodType ( zodType : any ) : ZodType {
40+ const def = zodType . _def ;
41+ const typeName = def . typeName ;
42+
43+ if (
44+ typeName === 'ZodOptional' ||
45+ typeName === 'ZodDefault' ||
46+ typeName === 'ZodNullable'
47+ ) {
48+ return getZodType ( def . innerType ) ;
49+ }
50+ if ( typeName === 'ZodEffects' ) {
51+ return getZodType ( def . schema ) ;
52+ }
53+
54+ if ( isZodType ( typeName ) ) {
55+ return typeName ;
56+ }
57+ throw new Error ( `Unsupported zod type for tool parameter: ${ typeName } ` ) ;
58+ }
59+
60+ type LoggedToolCallArgValue = string | number | boolean ;
61+
62+ function transformName ( zodType : ZodType , name : string ) : string {
63+ if ( zodType === 'ZodString' ) {
64+ return `${ name } _length` ;
65+ } else if ( zodType === 'ZodArray' ) {
66+ return `${ name } _count` ;
67+ } else {
68+ return name ;
69+ }
70+ }
71+
72+ function transformValue ( zodType : ZodType , value : unknown ) : LoggedToolCallArgValue {
73+ if ( zodType === 'ZodString' ) {
74+ return ( value as string ) . length ;
75+ } else if ( zodType === 'ZodArray' ) {
76+ return ( value as Array < unknown > ) . length ;
77+ } else {
78+ return value as LoggedToolCallArgValue ;
79+ }
80+ }
81+
82+ function hasEquivalentType ( zodType : ZodType , value : unknown ) : boolean {
83+ if ( zodType === 'ZodString' ) {
84+ return typeof value === 'string' ;
85+ } else if ( zodType === 'ZodArray' ) {
86+ return Array . isArray ( value ) ;
87+ } else if ( zodType === 'ZodNumber' ) {
88+ return typeof value === 'number' ;
89+ } else if ( zodType === 'ZodBoolean' ) {
90+ return typeof value === 'boolean' ;
91+ } else if ( zodType === 'ZodEnum' ) {
92+ return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ;
93+ } else {
94+ return false ;
95+ }
96+ }
97+
98+ export function sanitizeParams (
99+ params : ShapeOutput < zod . ZodRawShape > ,
100+ schema : zod . ZodRawShape ,
101+ ) : ShapeOutput < zod . ZodRawShape > {
102+ const transformed : ShapeOutput < zod . ZodRawShape > = { } ;
103+ for ( const [ name , value ] of Object . entries ( params ) ) {
104+ if ( PARAM_BLOCKLIST . has ( name ) ) {
105+ continue ;
106+ }
107+ const zodType = getZodType ( schema [ name ] ) ;
108+ if ( ! hasEquivalentType ( zodType , value ) ) {
109+ throw new Error ( `parameter ${ name } has type ${ zodType } but value ${ value } is not of equivalent type` ) ;
110+ }
111+ const transformedName = transformName ( zodType , name ) ;
112+ const transformedValue = transformValue ( zodType , value ) ;
113+ transformed [ transformedName ] = transformedValue ;
114+ }
115+ return transformed ;
116+ }
23117
24118function detectOsType ( ) : OsType {
25119 switch ( process . platform ) {
0 commit comments