1- import path from "path" ;
2- import { describe , expect , test } from "bun:test" ;
3- import { fileURLToPath } from "url" ;
4- import { Instance } from "../../src/project/instance" ;
5- import { ModelID , ProviderID } from "../../src/provider/schema" ;
6- import { Session } from "../../src/session" ;
7- import { MessageV2 } from "../../src/session/message-v2" ;
8- import { SessionPrompt } from "../../src/session/prompt" ;
9- import { Log } from "../../src/util/log" ;
10- import { tmpdir } from "../fixture/fixture" ;
11-
12- Log . init ( { print : false } ) ;
1+ import path from "path"
2+ import { describe , expect , test } from "bun:test"
3+ import { fileURLToPath } from "url"
4+ import { Instance } from "../../src/project/instance"
5+ import { ModelID , ProviderID } from "../../src/provider/schema"
6+ import { Session } from "../../src/session"
7+ import { MessageV2 } from "../../src/session/message-v2"
8+ import { SessionPrompt } from "../../src/session/prompt"
9+ import { Log } from "../../src/util/log"
10+ import { tmpdir } from "../fixture/fixture"
11+
12+ Log . init ( { print : false } )
1313
1414describe ( "session.prompt missing file" , ( ) => {
1515 test ( "does not fail the prompt when a file part is missing" , async ( ) => {
@@ -22,14 +22,14 @@ describe("session.prompt missing file", () => {
2222 } ,
2323 } ,
2424 } ,
25- } ) ;
25+ } )
2626
2727 await Instance . provide ( {
2828 directory : tmp . path ,
2929 fn : async ( ) => {
30- const session = await Session . create ( { } ) ;
30+ const session = await Session . create ( { } )
3131
32- const missing = path . join ( tmp . path , "does-not-exist.ts" ) ;
32+ const missing = path . join ( tmp . path , "does-not-exist.ts" )
3333 const msg = await SessionPrompt . prompt ( {
3434 sessionID : session . id ,
3535 agent : "build" ,
@@ -43,21 +43,19 @@ describe("session.prompt missing file", () => {
4343 filename : "does-not-exist.ts" ,
4444 } ,
4545 ] ,
46- } ) ;
46+ } )
4747
48- if ( msg . info . role !== "user" ) throw new Error ( "expected user message" ) ;
48+ if ( msg . info . role !== "user" ) throw new Error ( "expected user message" )
4949
5050 const hasFailure = msg . parts . some (
51- ( part ) =>
52- part . type === "text" && part . synthetic &&
53- part . text . includes ( "Read tool failed to read" ) ,
54- ) ;
55- expect ( hasFailure ) . toBe ( true ) ;
51+ ( part ) => part . type === "text" && part . synthetic && part . text . includes ( "Read tool failed to read" ) ,
52+ )
53+ expect ( hasFailure ) . toBe ( true )
5654
57- await Session . remove ( session . id ) ;
55+ await Session . remove ( session . id )
5856 } ,
59- } ) ;
60- } ) ;
57+ } )
58+ } )
6159
6260 test ( "keeps stored part order stable when file resolution is async" , async ( ) => {
6361 await using tmp = await tmpdir ( {
@@ -69,14 +67,14 @@ describe("session.prompt missing file", () => {
6967 } ,
7068 } ,
7169 } ,
72- } ) ;
70+ } )
7371
7472 await Instance . provide ( {
7573 directory : tmp . path ,
7674 fn : async ( ) => {
77- const session = await Session . create ( { } ) ;
75+ const session = await Session . create ( { } )
7876
79- const missing = path . join ( tmp . path , "still-missing.ts" ) ;
77+ const missing = path . join ( tmp . path , "still-missing.ts" )
8078 const msg = await SessionPrompt . prompt ( {
8179 sessionID : session . id ,
8280 agent : "build" ,
@@ -90,76 +88,71 @@ describe("session.prompt missing file", () => {
9088 } ,
9189 { type : "text" , text : "after-file" } ,
9290 ] ,
93- } ) ;
91+ } )
9492
95- if ( msg . info . role !== "user" ) throw new Error ( "expected user message" ) ;
93+ if ( msg . info . role !== "user" ) throw new Error ( "expected user message" )
9694
9795 const stored = await MessageV2 . get ( {
9896 sessionID : session . id ,
9997 messageID : msg . info . id ,
100- } ) ;
101- const text = stored . parts . filter ( ( part ) => part . type === "text" ) . map ( (
102- part ,
103- ) => part . text ) ;
98+ } )
99+ const text = stored . parts . filter ( ( part ) => part . type === "text" ) . map ( ( part ) => part . text )
104100
105- expect (
106- text [ 0 ] ?. startsWith ( "Called the Read tool with the following input:" ) ,
107- ) . toBe ( true ) ;
108- expect ( text [ 1 ] ?. includes ( "Read tool failed to read" ) ) . toBe ( true ) ;
109- expect ( text [ 2 ] ) . toBe ( "after-file" ) ;
101+ expect ( text [ 0 ] ?. startsWith ( "Called the Read tool with the following input:" ) ) . toBe ( true )
102+ expect ( text [ 1 ] ?. includes ( "Read tool failed to read" ) ) . toBe ( true )
103+ expect ( text [ 2 ] ) . toBe ( "after-file" )
110104
111- await Session . remove ( session . id ) ;
105+ await Session . remove ( session . id )
112106 } ,
113- } ) ;
114- } ) ;
115- } ) ;
107+ } )
108+ } )
109+ } )
116110
117111describe ( "session.prompt special characters" , ( ) => {
118112 test ( "handles filenames with # character" , async ( ) => {
119113 await using tmp = await tmpdir ( {
120114 git : true ,
121115 init : async ( dir ) => {
122- await Bun . write ( path . join ( dir , "file#name.txt" ) , "special content\n" ) ;
116+ await Bun . write ( path . join ( dir , "file#name.txt" ) , "special content\n" )
123117 } ,
124- } ) ;
118+ } )
125119
126120 await Instance . provide ( {
127121 directory : tmp . path ,
128122 fn : async ( ) => {
129- const session = await Session . create ( { } ) ;
130- const template = "Read @file#name.txt" ;
131- const parts = await SessionPrompt . resolvePromptParts ( template ) ;
132- const fileParts = parts . filter ( ( part ) => part . type === "file" ) ;
123+ const session = await Session . create ( { } )
124+ const template = "Read @file#name.txt"
125+ const parts = await SessionPrompt . resolvePromptParts ( template )
126+ const fileParts = parts . filter ( ( part ) => part . type === "file" )
133127
134- expect ( fileParts . length ) . toBe ( 1 ) ;
135- expect ( fileParts [ 0 ] . filename ) . toBe ( "file#name.txt" ) ;
136- expect ( fileParts [ 0 ] . url ) . toContain ( "%23" ) ;
128+ expect ( fileParts . length ) . toBe ( 1 )
129+ expect ( fileParts [ 0 ] . filename ) . toBe ( "file#name.txt" )
130+ expect ( fileParts [ 0 ] . url ) . toContain ( "%23" )
137131
138- const decodedPath = fileURLToPath ( fileParts [ 0 ] . url ) ;
139- expect ( decodedPath ) . toBe ( path . join ( tmp . path , "file#name.txt" ) ) ;
132+ const decodedPath = fileURLToPath ( fileParts [ 0 ] . url )
133+ expect ( decodedPath ) . toBe ( path . join ( tmp . path , "file#name.txt" ) )
140134
141135 const message = await SessionPrompt . prompt ( {
142136 sessionID : session . id ,
143137 parts,
144138 noReply : true ,
145- } ) ;
146- const stored = await MessageV2 . get ( {
147- sessionID : session . id ,
148- messageID : message . info . id ,
149- } ) ;
150- const textParts = stored . parts . filter ( ( part ) => part . type === "text" ) ;
151- const hasContent = textParts . some ( ( part ) =>
152- part . text . includes ( "special content" )
153- ) ;
154- expect ( hasContent ) . toBe ( true ) ;
139+ } )
140+ const stored = await MessageV2 . get ( { sessionID : session . id , messageID : message . info . id } )
141+ const textParts = stored . parts . filter ( ( part ) => part . type === "text" )
142+ const hasContent = textParts . some ( ( part ) => part . text . includes ( "special content" ) )
143+ expect ( hasContent ) . toBe ( true )
155144
156- await Session . remove ( session . id ) ;
145+ await Session . remove ( session . id )
157146 } ,
158- } ) ;
159- } ) ;
160- } ) ;
147+ } )
148+ } )
149+ } )
150+
161151
162152describe ( "session.prompt agent part" , ( ) => {
153+ const delegationNudge =
154+ "Use the above message and context to generate a prompt and call the task tool with subagent:" ;
155+
163156 test ( "injects synthetic delegation nudge in root sessions" , async ( ) => {
164157 await using tmp = await tmpdir ( { git : true } ) ;
165158
@@ -185,11 +178,8 @@ describe("session.prompt agent part", () => {
185178 } ) ;
186179 const syntheticDelegation = stored . parts . find (
187180 ( part ) =>
188- part . type === "text" &&
189- part . synthetic &&
190- part . text . includes (
191- "Use the above message and context to generate a prompt and call the task tool with subagent:" ,
192- ) ,
181+ part . type === "text" && part . synthetic &&
182+ part . text . includes ( delegationNudge ) ,
193183 ) ;
194184 expect ( syntheticDelegation ) . toBeDefined ( ) ;
195185
@@ -224,15 +214,11 @@ describe("session.prompt agent part", () => {
224214 } ) ;
225215 const syntheticDelegation = stored . parts . find (
226216 ( part ) =>
227- part . type === "text" &&
228- part . synthetic &&
229- part . text . includes (
230- "Use the above message and context to generate a prompt and call the task tool with subagent:" ,
231- ) ,
217+ part . type === "text" && part . synthetic &&
218+ part . text . includes ( delegationNudge ) ,
232219 ) ;
233220 expect ( syntheticDelegation ) . toBeUndefined ( ) ;
234221
235- // The agent part itself should still be present
236222 const agentPart = stored . parts . find ( ( part ) => part . type === "agent" ) ;
237223 expect ( agentPart ) . toBeDefined ( ) ;
238224
@@ -245,8 +231,8 @@ describe("session.prompt agent part", () => {
245231
246232describe ( "session.prompt agent variant" , ( ) => {
247233 test ( "applies agent variant only when using agent model" , async ( ) => {
248- const prev = process . env . OPENAI_API_KEY ;
249- process . env . OPENAI_API_KEY = "test-openai-key" ;
234+ const prev = process . env . OPENAI_API_KEY
235+ process . env . OPENAI_API_KEY = "test-openai-key"
250236
251237 try {
252238 await using tmp = await tmpdir ( {
@@ -259,61 +245,49 @@ describe("session.prompt agent variant", () => {
259245 } ,
260246 } ,
261247 } ,
262- } ) ;
248+ } )
263249
264250 await Instance . provide ( {
265251 directory : tmp . path ,
266252 fn : async ( ) => {
267- const session = await Session . create ( { } ) ;
253+ const session = await Session . create ( { } )
268254
269255 const other = await SessionPrompt . prompt ( {
270256 sessionID : session . id ,
271257 agent : "build" ,
272- model : {
273- providerID : ProviderID . make ( "opencode" ) ,
274- modelID : ModelID . make ( "kimi-k2.5-free" ) ,
275- } ,
258+ model : { providerID : ProviderID . make ( "opencode" ) , modelID : ModelID . make ( "kimi-k2.5-free" ) } ,
276259 noReply : true ,
277260 parts : [ { type : "text" , text : "hello" } ] ,
278- } ) ;
279- if ( other . info . role !== "user" ) {
280- throw new Error ( "expected user message" ) ;
281- }
282- expect ( other . info . variant ) . toBeUndefined ( ) ;
261+ } )
262+ if ( other . info . role !== "user" ) throw new Error ( "expected user message" )
263+ expect ( other . info . variant ) . toBeUndefined ( )
283264
284265 const match = await SessionPrompt . prompt ( {
285266 sessionID : session . id ,
286267 agent : "build" ,
287268 noReply : true ,
288269 parts : [ { type : "text" , text : "hello again" } ] ,
289- } ) ;
290- if ( match . info . role !== "user" ) {
291- throw new Error ( "expected user message" ) ;
292- }
293- expect ( match . info . model ) . toEqual ( {
294- providerID : ProviderID . make ( "openai" ) ,
295- modelID : ModelID . make ( "gpt-5.2" ) ,
296- } ) ;
297- expect ( match . info . variant ) . toBe ( "xhigh" ) ;
270+ } )
271+ if ( match . info . role !== "user" ) throw new Error ( "expected user message" )
272+ expect ( match . info . model ) . toEqual ( { providerID : ProviderID . make ( "openai" ) , modelID : ModelID . make ( "gpt-5.2" ) } )
273+ expect ( match . info . variant ) . toBe ( "xhigh" )
298274
299275 const override = await SessionPrompt . prompt ( {
300276 sessionID : session . id ,
301277 agent : "build" ,
302278 noReply : true ,
303279 variant : "high" ,
304280 parts : [ { type : "text" , text : "hello third" } ] ,
305- } ) ;
306- if ( override . info . role !== "user" ) {
307- throw new Error ( "expected user message" ) ;
308- }
309- expect ( override . info . variant ) . toBe ( "high" ) ;
281+ } )
282+ if ( override . info . role !== "user" ) throw new Error ( "expected user message" )
283+ expect ( override . info . variant ) . toBe ( "high" )
310284
311- await Session . remove ( session . id ) ;
285+ await Session . remove ( session . id )
312286 } ,
313- } ) ;
287+ } )
314288 } finally {
315- if ( prev === undefined ) delete process . env . OPENAI_API_KEY ;
316- else process . env . OPENAI_API_KEY = prev ;
289+ if ( prev === undefined ) delete process . env . OPENAI_API_KEY
290+ else process . env . OPENAI_API_KEY = prev
317291 }
318- } ) ;
319- } ) ;
292+ } )
293+ } )
0 commit comments