@@ -4295,6 +4295,82 @@ let toolInputSchemaRegistry = null;
4295
4295
/**
4296
4296
* ENHANCED: Tool testing with improved race condition handling
4297
4297
*/
4298
+
4299
+ function detectArrayLike ( p ) {
4300
+ // Returns { isArrayLike, itemsSchema, isNullable }
4301
+ const out = {
4302
+ isArrayLike : false ,
4303
+ itemsSchema : undefined ,
4304
+ isNullable : false ,
4305
+ } ;
4306
+ if ( ! p || typeof p !== "object" ) {
4307
+ return out ;
4308
+ }
4309
+
4310
+ const t = Array . isArray ( p . type ) ? p . type : p . type ? [ p . type ] : [ ] ;
4311
+ if ( t . includes ( "null" ) ) {
4312
+ out . isNullable = true ;
4313
+ }
4314
+ if ( p . nullable === true ) {
4315
+ out . isNullable = true ;
4316
+ }
4317
+ if ( Array . isArray ( p . enum ) && p . enum . includes ( null ) ) {
4318
+ out . isNullable = true ;
4319
+ }
4320
+ if ( p . const === null ) {
4321
+ out . isNullable = true ;
4322
+ }
4323
+
4324
+ // A) explicit array on prop
4325
+ if ( t . includes ( "array" ) ) {
4326
+ out . isArrayLike = true ;
4327
+ out . itemsSchema = p . items ;
4328
+ return out ;
4329
+ }
4330
+ // B) items present without explicit type
4331
+ if ( p . items ) {
4332
+ out . isArrayLike = true ;
4333
+ out . itemsSchema = p . items ;
4334
+ return out ;
4335
+ }
4336
+
4337
+ // C) union variants (anyOf/oneOf) detect array + null
4338
+ const unions = [ ]
4339
+ . concat ( Array . isArray ( p . anyOf ) ? p . anyOf : [ ] )
4340
+ . concat ( Array . isArray ( p . oneOf ) ? p . oneOf : [ ] ) ;
4341
+
4342
+ for ( const v of unions ) {
4343
+ if ( ! v ) {
4344
+ continue ;
4345
+ }
4346
+ const vt = Array . isArray ( v . type ) ? v . type : v . type ? [ v . type ] : [ ] ;
4347
+ if ( vt . includes ( "null" ) ) {
4348
+ out . isNullable = true ;
4349
+ }
4350
+ if (
4351
+ v . nullable === true ||
4352
+ ( Array . isArray ( v . enum ) && v . enum . includes ( null ) ) ||
4353
+ v . const === null
4354
+ ) {
4355
+ out . isNullable = true ;
4356
+ }
4357
+ }
4358
+
4359
+ for ( const v of unions ) {
4360
+ if ( ! v ) {
4361
+ continue ;
4362
+ }
4363
+ const vt = Array . isArray ( v . type ) ? v . type : v . type ? [ v . type ] : [ ] ;
4364
+ if ( vt . includes ( "array" ) || v . items ) {
4365
+ out . isArrayLike = true ;
4366
+ out . itemsSchema = v . items ;
4367
+ return out ;
4368
+ }
4369
+ }
4370
+
4371
+ return out ;
4372
+ }
4373
+
4298
4374
async function testTool ( toolId ) {
4299
4375
try {
4300
4376
console . log ( `Testing tool ID: ${ toolId } ` ) ;
@@ -4476,20 +4552,24 @@ async function testTool(toolId) {
4476
4552
fieldDiv . appendChild ( description ) ;
4477
4553
}
4478
4554
4479
- if ( prop . type === "array" ) {
4555
+ const { isArrayLike, itemsSchema, isNullable } =
4556
+ detectArrayLike ( prop ) ;
4557
+
4558
+ if ( isArrayLike ) {
4480
4559
const arrayContainer = document . createElement ( "div" ) ;
4481
4560
arrayContainer . className = "space-y-2" ;
4482
4561
4483
4562
function createArrayInput ( value = "" ) {
4484
4563
const wrapper = document . createElement ( "div" ) ;
4485
4564
wrapper . className = "flex items-center space-x-2" ;
4486
4565
4487
- const itemTypes = Array . isArray ( prop . items ?. anyOf )
4488
- ? prop . items . anyOf . map ( ( t ) => t . type )
4489
- : [ prop . items ?. type ] ;
4566
+ // Use itemsSchema (NOT prop.items)
4567
+ const items = itemsSchema || { } ;
4568
+ const itemTypes = Array . isArray ( items . anyOf )
4569
+ ? items . anyOf . map ( ( t ) => t . type ) . filter ( Boolean )
4570
+ : [ items . type ] . filter ( Boolean ) ;
4490
4571
4491
4572
let input ;
4492
-
4493
4573
if (
4494
4574
itemTypes . includes ( "number" ) ||
4495
4575
itemTypes . includes ( "integer" )
@@ -4548,7 +4628,6 @@ async function testTool(toolId) {
4548
4628
arrayContainer . removeChild ( wrapper ) ;
4549
4629
} ) ;
4550
4630
4551
- // only append if not boolean (boolean branch already appended input above)
4552
4631
if ( ! itemTypes . includes ( "boolean" ) ) {
4553
4632
wrapper . appendChild ( input ) ;
4554
4633
}
@@ -4566,20 +4645,19 @@ async function testTool(toolId) {
4566
4645
arrayContainer . appendChild ( createArrayInput ( ) ) ;
4567
4646
} ) ;
4568
4647
4569
- if ( Array . isArray ( prop . default ) ) {
4570
- if ( prop . default . length > 0 ) {
4571
- prop . default . forEach ( ( val ) => {
4572
- arrayContainer . appendChild (
4573
- createArrayInput ( val ) ,
4574
- ) ;
4575
- } ) ;
4576
- } else {
4577
- // Create one empty input for empty default arrays
4578
- arrayContainer . appendChild ( createArrayInput ( ) ) ;
4579
- }
4580
- } else {
4648
+ // Seed rows:
4649
+ if (
4650
+ Array . isArray ( prop . default ) &&
4651
+ prop . default . length > 0
4652
+ ) {
4653
+ prop . default . forEach ( ( val ) =>
4654
+ arrayContainer . appendChild ( createArrayInput ( val ) ) ,
4655
+ ) ;
4656
+ } else if ( ! isNullable ) {
4657
+ // Non-nullable: start with one empty row
4581
4658
arrayContainer . appendChild ( createArrayInput ( ) ) ;
4582
4659
}
4660
+ // Nullable + default null/undefined → start with zero rows (button still shown)
4583
4661
4584
4662
fieldDiv . appendChild ( arrayContainer ) ;
4585
4663
fieldDiv . appendChild ( addBtn ) ;
0 commit comments