@@ -138,241 +138,27 @@ describe('Core tool handlers', () => {
138138 // ---------------------------------------------------------------------------
139139
140140 describe ( 'comet_ask' , ( ) => {
141- it ( 'quick response — returns agent response ' , async ( ) => {
141+ it ( 'returns immediate submission message without polling ' , async ( ) => {
142142 let callCount = 0
143- const responseText =
144- 'The answer is 42, which is the meaning of life according to Douglas Adams'
145-
146143 mocks . safeEvaluate . mockImplementation ( async ( ) => {
147144 callCount ++
148- // First call: pre-send state
149- if ( callCount === 1 ) {
150- return { result : { value : '{"proseCount":0,"lastProseText":""}' } }
151- }
152- // Second call: type prompt
153- if ( callCount === 2 ) {
154- return { result : { value : 'typed' } }
155- }
156- // Third call: submit
157- if ( callCount === 3 ) {
158- return { result : { value : 'submitted' } }
159- }
160- // Fourth+ calls: status polling (completed)
161- return {
162- result : {
163- value : JSON . stringify ( {
164- status : 'completed' ,
165- steps : [ 'Searching web' ] ,
166- currentStep : 'Searching web' ,
167- response : responseText ,
168- hasStopButton : false ,
169- } ) ,
170- } ,
171- }
172- } )
173-
174- const handler = getHandler ( 'comet_ask' )
175- const result = await handler ( { prompt : 'What is 42?' } )
176-
177- expect ( result . content [ 0 ] . text ) . toContain ( responseText )
178- } )
179-
180- it ( 'timeout — returns still working message' , async ( ) => {
181- mocks . safeEvaluate . mockResolvedValue ( {
182- result : {
183- value : JSON . stringify ( {
184- status : 'working' ,
185- steps : [ ] ,
186- currentStep : '' ,
187- response : '' ,
188- hasStopButton : true ,
189- } ) ,
190- } ,
145+ return { result : { value : '{"proseCount":0,"lastProseText":""}' } }
191146 } )
192147
193148 const handler = getHandler ( 'comet_ask' )
194- const result = await handler ( { prompt : 'test' , timeout : 300 } )
149+ const result = await handler ( { prompt : 'test' } )
195150
196- expect ( result . content [ 0 ] . text ) . toContain ( 'Agent is still working' )
151+ expect ( result . content [ 0 ] . text ) . toContain ( 'Prompt submitted successfully' )
152+ expect ( result . content [ 0 ] . text ) . toContain ( 'comet_poll' )
197153 } )
198154
199155 it ( 'error handling — returns MCP error when safeEvaluate throws' , async ( ) => {
200156 mocks . safeEvaluate . mockRejectedValue ( new Error ( 'Script error' ) )
201-
202157 const handler = getHandler ( 'comet_ask' )
203158 const result = await handler ( { prompt : 'test' } )
204-
205159 expect ( result . isError ) . toBe ( true )
206160 expect ( result . content [ 0 ] . text ) . toContain ( 'Error' )
207161 } )
208-
209- it ( 'sequential queries — returns new response when proseCount increases' , async ( ) => {
210- // Simulates BUG-2: second query should detect new response via proseCount
211- // even when old response text is still on the page
212- let callCount = 0
213- const oldResponse = 'This is the old response from the first query that is still on the page.'
214- const newResponse = 'This is the new response from the second query with different content.'
215-
216- mocks . safeEvaluate . mockImplementation ( async ( ) => {
217- callCount ++
218- // First call: pre-send state (old response still on page, proseCount=1)
219- if ( callCount === 1 ) {
220- return {
221- result : { value : JSON . stringify ( { proseCount : 1 , lastProseText : oldResponse } ) } ,
222- }
223- }
224- // Second call: type prompt
225- if ( callCount === 2 ) return { result : { value : 'typed' } }
226- // Third call: submit
227- if ( callCount === 3 ) return { result : { value : 'submitted' } }
228- // Fourth+ calls: status polling — proseCount now 2 (new prose added)
229- return {
230- result : {
231- value : JSON . stringify ( {
232- status : 'completed' ,
233- steps : [ 'Searching web' ] ,
234- currentStep : 'Searching web' ,
235- response : newResponse ,
236- hasStopButton : false ,
237- proseCount : 2 ,
238- } ) ,
239- } ,
240- }
241- } )
242-
243- const handler = getHandler ( 'comet_ask' )
244- const result = await handler ( { prompt : 'What is 3+3?' } )
245-
246- expect ( result . content [ 0 ] . text ) . toContain ( newResponse )
247- expect ( result . content [ 0 ] . text ) . not . toContain ( oldResponse )
248- } )
249-
250- it ( 'comet_ask stops polling after timeout — no runaway polling' , async ( ) => {
251- let evalCalls = 0
252- mocks . safeEvaluate . mockImplementation ( async ( ) => {
253- evalCalls ++
254- if ( evalCalls === 1 ) return { result : { value : '{"proseCount":0,"lastProseText":""}' } }
255- if ( evalCalls === 2 ) return { result : { value : 'typed' } }
256- if ( evalCalls === 3 ) return { result : { value : 'submitted' } }
257- return {
258- result : {
259- value : JSON . stringify ( {
260- status : 'working' ,
261- steps : [ ] ,
262- currentStep : '' ,
263- response : '' ,
264- hasStopButton : true ,
265- } ) ,
266- } ,
267- }
268- } )
269-
270- const handler = getHandler ( 'comet_ask' )
271- const result = await handler ( { prompt : 'test' , timeout : 300 } )
272- expect ( result . content [ 0 ] . text ) . toContain ( 'Agent is still working' )
273-
274- // Verify no runaway polling after timeout
275- const callsAfterTimeout = evalCalls
276- await new Promise ( ( r ) => setTimeout ( r , 500 ) )
277- expect ( evalCalls ) . toBe ( callsAfterTimeout )
278- } )
279-
280- it ( 'smart polling — auto-extends when response is growing' , async ( ) => {
281- let callCount = 0
282- const growingResponses = [ 'A' . repeat ( 60 ) , 'A' . repeat ( 120 ) , 'A' . repeat ( 200 ) ]
283- mocks . safeEvaluate . mockImplementation ( async ( ) => {
284- callCount ++
285- if ( callCount === 1 ) return { result : { value : '{"proseCount":0,"lastProseText":""}' } }
286- if ( callCount === 2 ) return { result : { value : 'typed' } }
287- if ( callCount === 3 ) return { result : { value : 'submitted' } }
288- const responseIdx = Math . min ( callCount - 4 , growingResponses . length - 1 )
289- return {
290- result : {
291- value : JSON . stringify ( {
292- status : callCount > 6 ? 'completed' : 'working' ,
293- steps : [ ] ,
294- currentStep : '' ,
295- response : growingResponses [ responseIdx ] ,
296- hasStopButton : callCount <= 6 ,
297- proseCount : 1 ,
298- } ) ,
299- } ,
300- }
301- } )
302-
303- const handler = getHandler ( 'comet_ask' )
304- // 300ms timeout would normally be too short, but growing response should keep it alive
305- const result = await handler ( { prompt : 'test' , timeout : 300 } )
306- // Should have gotten the full response since it was growing
307- expect ( result . content [ 0 ] . text ) . toContain ( 'A' . repeat ( 200 ) )
308- } )
309-
310- it ( 'smart polling — gives up after stall' , async ( ) => {
311- let callCount = 0
312- const stalledResponse = 'B' . repeat ( 60 )
313- mocks . safeEvaluate . mockImplementation ( async ( ) => {
314- callCount ++
315- if ( callCount === 1 ) return { result : { value : '{"proseCount":0,"lastProseText":""}' } }
316- if ( callCount === 2 ) return { result : { value : 'typed' } }
317- if ( callCount === 3 ) return { result : { value : 'submitted' } }
318- return {
319- result : {
320- value : JSON . stringify ( {
321- status : 'working' ,
322- steps : [ ] ,
323- currentStep : '' ,
324- response : stalledResponse ,
325- hasStopButton : true ,
326- proseCount : 1 ,
327- } ) ,
328- } ,
329- }
330- } )
331-
332- const handler = getHandler ( 'comet_ask' )
333- const result = await handler ( { prompt : 'test' , timeout : 30000 } )
334- // Should time out because response stopped growing (stall detection)
335- expect ( result . content [ 0 ] . text ) . toContain ( 'still working' )
336- } )
337-
338- it ( 'ignores old substantial response — no false positive from hasSubstantialResponse' , async ( ) => {
339- // Regression: hasSubstantialResponse was OR'd into responseChanged,
340- // causing old responses to be treated as new when proseCount didn't increase.
341- const oldResponse =
342- 'This is an old response from a previous query that is still on the page and is quite long.'
343- let callCount = 0
344- mocks . safeEvaluate . mockImplementation ( async ( ) => {
345- callCount ++
346- // pre-send state: old response still on page
347- if ( callCount === 1 ) {
348- return {
349- result : { value : JSON . stringify ( { proseCount : 1 , lastProseText : oldResponse } ) } ,
350- }
351- }
352- if ( callCount === 2 ) return { result : { value : 'typed' } }
353- if ( callCount === 3 ) return { result : { value : 'submitted' } }
354- // Polling: agent hasn't started yet, old response still visible
355- return {
356- result : {
357- value : JSON . stringify ( {
358- status : 'working' ,
359- steps : [ ] ,
360- currentStep : '' ,
361- response : oldResponse ,
362- hasStopButton : true ,
363- proseCount : 1 ,
364- } ) ,
365- } ,
366- }
367- } )
368-
369- const handler = getHandler ( 'comet_ask' )
370- const result = await handler ( { prompt : 'New question?' , timeout : 1500 } )
371-
372- // Should NOT return the old response as if it were the new answer
373- // Instead should timeout since no new response detected
374- expect ( result . content [ 0 ] . text ) . toContain ( 'still working' )
375- } )
376162 } )
377163
378164 // ---------------------------------------------------------------------------
0 commit comments