2026-07-21 19:46:18 +00:00
import { describe , expect } from "bun:test"
import { CodeMode } from "@opencode-ai/core/codemode"
2026-07-24 15:20:26 +00:00
import { CodeModeCatalog } from "@opencode-ai/core/codemode/catalog"
2026-07-21 19:46:18 +00:00
import { CodeModeInstructions } from "@opencode-ai/core/codemode/instructions"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
2026-07-24 00:56:18 +00:00
import { Tool } from "@opencode-ai/core/tool/tool"
2026-07-24 02:54:22 +00:00
import { Effect , Schema } from "effect"
2026-07-21 19:46:18 +00:00
import { it } from "../lib/effect"
import { readInitial , readUpdate } from "../lib/instructions"
2026-07-24 15:20:26 +00:00
const echo : CodeModeCatalog.Entry = {
path : "notes.echo" ,
description : "Echo text" ,
signature : "tools.notes.echo(input: {\n text: string,\n}): Promise<string>" ,
}
const lookup : CodeModeCatalog.Entry = {
path : "orders.lookup" ,
description : "Look up an order" ,
signature : "tools.orders.lookup(input: {\n id: string,\n}): Promise<unknown>" ,
}
2026-07-21 19:46:18 +00:00
describe ( "CodeModeInstructions" , ( ) = > {
2026-07-25 18:01:05 +00:00
it . effect ( "instructs the model not to call execute while the catalog is empty" , ( ) = >
Effect . gen ( function * ( ) {
const initialized = yield * readInitial ( CodeModeInstructions . make ( [ ] ) )
expect ( initialized . text ) . toBe (
2026-07-25 18:20:16 +00:00
"No Code Mode tools are currently available. Later Code Mode catalog updates may add or remove tools. Do not call `execute` unless there is at least one available Code Mode tool." ,
2026-07-25 18:01:05 +00:00
)
const added = yield * readUpdate ( CodeModeInstructions . make ( [ echo ] ) , initialized )
expect ( added . text ) . toContain ( "New tools are available in addition to those previously listed:" )
expect ( added . text ) . toContain ( echo . signature )
expect ( yield * readUpdate ( CodeModeInstructions . make ( [ ] ) , { values : added.values } ) ) . toMatchObject ( {
text :
"The Code Mode tool catalog has changed. This catalog supersedes the previous Code Mode tool catalog.\n\n" +
2026-07-25 18:20:16 +00:00
"No Code Mode tools are currently available. Later Code Mode catalog updates may add or remove tools. Do not call `execute` unless there is at least one available Code Mode tool." ,
2026-07-25 18:01:05 +00:00
} )
} ) ,
)
2026-07-24 15:20:26 +00:00
it . effect ( "renders the initial catalog, semantic deltas, and removal" , ( ) = >
Effect . gen ( function * ( ) {
const initialized = yield * readInitial ( CodeModeInstructions . make ( [ echo ] ) )
expect ( initialized . text ) . toContain ( "## Available tools" )
expect ( initialized . text ) . not . toContain ( "## Search" )
expect ( initialized . text ) . toContain ( ` - ${ echo . signature } // Echo text ` )
const added = yield * readUpdate ( CodeModeInstructions . make ( [ echo , lookup ] ) , initialized )
expect ( added . text ) . toContain ( "The Code Mode tool catalog has changed." )
expect ( added . text ) . toContain ( "New tools are available in addition to those previously listed:" )
expect ( added . text ) . toContain ( ` - ${ lookup . signature } // Look up an order ` )
expect ( added . text ) . not . toContain ( "## Available tools" )
const removed = yield * readUpdate ( CodeModeInstructions . make ( [ echo ] ) , { values : added.values } )
expect ( removed . text ) . toBe (
"The Code Mode tool catalog has changed.\n\n" +
"The following tools are no longer available and must not be called: tools.orders.lookup." ,
)
expect ( yield * readUpdate ( CodeModeInstructions . make ( ) , initialized ) ) . toMatchObject ( {
text : "Code Mode tools are no longer available. Do not use any previously listed Code Mode tools." ,
} )
} ) ,
)
it . effect ( "stores a canonical sorted snapshot so registration order does not churn history" , ( ) = > {
2026-07-24 00:56:18 +00:00
const alpha = Tool . make ( {
description : "Alpha tool" ,
input : Schema.Struct ( { } ) ,
output : Schema.String ,
execute : ( ) = > Effect . succeed ( { output : "alpha" } ) ,
} )
const zeta = Tool . make ( {
description : "Zeta tool" ,
input : Schema.Struct ( { } ) ,
output : Schema.String ,
execute : ( ) = > Effect . succeed ( { output : "zeta" } ) ,
} )
2026-07-24 15:20:26 +00:00
const layer = AppNodeBuilder . build ( CodeMode . node )
2026-07-24 00:56:18 +00:00
return Effect . gen ( function * ( ) {
const codeMode = yield * CodeMode . Service
const initialized = yield * Effect . scoped (
Effect . gen ( function * ( ) {
yield * codeMode . register ( Tool . registrationEntries ( { zeta , alpha } , { namespace : "tools" } ) )
2026-07-24 15:20:26 +00:00
return yield * readInitial ( CodeModeInstructions . make ( ( yield * codeMode . materialize ( ) ) . catalog ) )
2026-07-24 00:56:18 +00:00
} ) ,
)
const reordered = yield * Effect . scoped (
Effect . gen ( function * ( ) {
yield * codeMode . register ( Tool . registrationEntries ( { alpha , zeta } , { namespace : "tools" } ) )
2026-07-24 15:20:26 +00:00
return yield * readUpdate ( CodeModeInstructions . make ( ( yield * codeMode . materialize ( ) ) . catalog ) , initialized )
2026-07-24 00:56:18 +00:00
} ) ,
)
expect ( reordered . changed ) . toBe ( false )
expect ( reordered . text ) . toBe ( "" )
2026-07-24 15:20:26 +00:00
} ) . pipe ( Effect . provide ( layer ) )
2026-07-21 19:46:18 +00:00
} )
} )