2026-07-03 05:19:11 +00:00
import { describe , expect , test } from "bun:test"
import { Effect , Schema } from "effect"
import { CodeMode , Tool } from "../src/index.js"
// Key enumeration: Object.keys and for...in share one surface over plain objects, arrays
// (index strings), and tool references (namespace/tool names from the host tool tree), so a
// model can discover what it may call instead of guessing names from the instructions. The
// motivating transcript: `Object.keys(tools)` failed with the generic plain-objects-only
// message and `for (const key in tools)` was unsupported syntax, forcing blind guesses.
const echo = ( description : string ) = >
Tool . make ( {
description ,
input : Schema.Struct ( { value : Schema.String } ) ,
output : Schema.String ,
run : ( { value } ) = > Effect . succeed ( value ) ,
} )
const tools = {
github : { list_issues : echo ( "List issues" ) , get_issue : echo ( "Get one issue" ) } ,
memory : { search : echo ( "Search memory" ) } ,
playwright : { navigate : echo ( "Navigate somewhere" ) } ,
}
const run = ( code : string ) = > Effect . runPromise ( CodeMode . execute ( { tools , code } ) )
const value = async ( code : string ) = > {
const result = await run ( code )
if ( ! result . ok ) throw new Error ( ` expected success, got ${ result . error . kind } : ${ result . error . message } ` )
return result . value
}
const error = async ( code : string ) = > {
const result = await run ( code )
if ( result . ok ) throw new Error ( ` expected failure, got value ${ JSON . stringify ( result . value ) } ` )
return result . error
}
describe ( "Object.keys over tool references" , ( ) = > {
test ( "enumerates top-level namespaces (the transcript program)" , async ( ) = > {
2026-07-03 05:20:28 +00:00
expect (
await value ( `
2026-07-03 05:19:11 +00:00
const namespaces = Object . keys ( tools )
return { namespaces , count : namespaces.length }
2026-07-03 05:20:28 +00:00
` ),
2026-07-06 05:52:37 +00:00
) . toEqual ( { namespaces : [ "github" , "memory" , "playwright" , "$codemode" ] , count : 4 } )
2026-07-03 05:19:11 +00:00
} )
test ( "enumerates tool names at a nested namespace" , async ( ) = > {
expect ( await value ( ` return Object.keys(tools.github) ` ) ) . toEqual ( [ "list_issues" , "get_issue" ] )
} )
test ( "a callable tool is a leaf and enumerates as []" , async ( ) = > {
expect ( await value ( ` return Object.keys(tools.github.list_issues) ` ) ) . toEqual ( [ ] )
} )
2026-07-06 05:52:37 +00:00
test ( "the internal discovery namespace enumerates its callable surface" , async ( ) = > {
2026-07-03 05:19:11 +00:00
expect ( await value ( ` return Object.keys(tools. $ codemode) ` ) ) . toEqual ( [ "search" ] )
} )
test ( "an unknown namespace is an UnknownTool error pointing at the discovery idioms" , async ( ) = > {
const failure = await error ( ` return Object.keys(tools.nonexistent) ` )
expect ( failure . kind ) . toBe ( "UnknownTool" )
expect ( failure . message ) . toContain ( "Unknown tool namespace 'nonexistent'" )
expect ( failure . suggestions ? . join ( " " ) ) . toContain ( "Object.keys(tools)" )
} )
test ( "Object.values/entries on a tool reference explain the working idioms" , async ( ) = > {
for ( const method of [ "values" , "entries" ] as const ) {
const failure = await error ( ` return Object. ${ method } (tools) ` )
expect ( failure . kind ) . toBe ( "InvalidDataValue" )
expect ( failure . message ) . toContain (
` Object. ${ method } (...) cannot read tool references: they are not plain data. Use Object.keys(tools) for names, or tools. $ codemode.search({ query }) for signatures. ` ,
)
}
const nested = await error ( ` return Object.entries(tools.github) ` )
expect ( nested . message ) . toContain ( "Use Object.keys(tools) for names" )
} )
} )
describe ( "Object.keys over arrays" , ( ) = > {
test ( "returns index strings, like JS" , async ( ) = > {
expect ( await value ( ` return Object.keys(["a", "b", "c"]) ` ) ) . toEqual ( [ "0" , "1" , "2" ] )
expect ( await value ( ` return Object.keys([]) ` ) ) . toEqual ( [ ] )
} )
test ( "objects keep their own enumerable keys" , async ( ) = > {
expect ( await value ( ` return Object.keys({ a: 1, b: 2 }) ` ) ) . toEqual ( [ "a" , "b" ] )
} )
test ( "non-object inputs still fail clearly" , async ( ) = > {
const failure = await error ( ` return Object.keys("nope") ` )
expect ( failure . message ) . toContain ( "Object.keys expects a data object or array" )
} )
} )
describe ( "for...in" , ( ) = > {
test ( "iterates own enumerable keys of a plain object with break/continue" , async ( ) = > {
2026-07-03 05:20:28 +00:00
expect (
await value ( `
2026-07-03 05:19:11 +00:00
const seen = [ ]
for ( const key in { a : 1 , b : 2 , c : 3 , d : 4 } ) {
if ( key === "b" ) continue
if ( key === "d" ) break
seen . push ( key )
}
return seen
2026-07-03 05:20:28 +00:00
` ),
) . toEqual ( [ "a" , "c" ] )
2026-07-03 05:19:11 +00:00
} )
test ( "iterates index strings over arrays" , async ( ) = > {
2026-07-03 05:20:28 +00:00
expect (
await value ( `
2026-07-03 05:19:11 +00:00
const indexes = [ ]
for ( const i in [ "x" , "y" , "z" ] ) {
if ( i === "2" ) break
indexes . push ( i )
}
return indexes
2026-07-03 05:20:28 +00:00
` ),
) . toEqual ( [ "0" , "1" ] )
2026-07-03 05:19:11 +00:00
} )
test ( "supports let declarations and bare identifiers" , async ( ) = > {
2026-07-03 05:20:28 +00:00
expect (
await value ( `
2026-07-03 05:19:11 +00:00
let last = ""
for ( let key in { a : 1 , b : 2 } ) last = key
return last
2026-07-03 05:20:28 +00:00
` ),
) . toBe ( "b" )
expect (
await value ( `
2026-07-03 05:19:11 +00:00
let key = "before"
for ( key in { only : 1 } ) { }
return key
2026-07-03 05:20:28 +00:00
` ),
) . toBe ( "only" )
2026-07-03 05:19:11 +00:00
} )
2026-07-06 05:52:37 +00:00
test ( "enumerates namespaces and tools from the callable tool tree" , async ( ) = > {
2026-07-03 05:20:28 +00:00
expect (
await value ( `
2026-07-03 05:19:11 +00:00
const names = [ ]
for ( const ns in tools ) {
for ( const name in tools [ ns ] ) names . push ( ns + "." + name )
}
return names
2026-07-03 05:20:28 +00:00
` ),
2026-07-06 05:53:41 +00:00
) . toEqual ( [ "github.list_issues" , "github.get_issue" , "memory.search" , "playwright.navigate" , "$codemode.search" ] )
2026-07-03 05:19:11 +00:00
} )
test ( "unsupported values fail with a hint at for...of and Object.keys" , async ( ) = > {
for ( const expression of [ ` "text" ` , "new Map([[1, 2]])" , "new Set([1])" , "42" , "null" ] ) {
const failure = await error ( ` for (const key in ${ expression } ) {}; return "no" ` )
expect ( failure . message ) . toContain ( "for...in requires a plain object, array, or tools reference" )
expect ( failure . message ) . toContain ( "Use for...of for arrays/strings/Maps/Sets, or Object.keys(value)" )
}
} )
} )