| import { describe, it } from 'node:test'; |
| import assert from 'node:assert/strict'; |
| import { isThinkingRequested, shouldUseCascadeReuse, shouldUseStrictCascadeReuse } from '../src/handlers/chat.js'; |
|
|
| describe('shouldUseCascadeReuse', () => { |
| it('allows reuse for normal Cascade chat turns', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: false, modelKey: 'claude-4.5-haiku' }), true); |
| }); |
|
|
| it('keeps most tool-emulated turns out of reuse', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-4.5-haiku' }), false); |
| }); |
|
|
| it('allows reuse for tool-emulated Opus 4.7 turns', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4-7-medium' }), true); |
| }); |
|
|
| it('can disable the Opus 4.7 tool reuse override', () => { |
| assert.equal(shouldUseCascadeReuse({ |
| useCascade: true, |
| emulateTools: true, |
| modelKey: 'claude-opus-4-7-medium', |
| allowToolReuse: false, |
| }), false); |
| }); |
|
|
| it('disables reuse outside Cascade', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: false, emulateTools: false, modelKey: 'claude-opus-4-7-medium' }), false); |
| }); |
|
|
| |
| |
| |
| |
| it('allows tool-emulated reuse for Opus 4.6 (dotted)', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4.6' }), true); |
| }); |
|
|
| it('allows tool-emulated reuse for Opus 4.6 (dashed variant)', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4-6-medium' }), true); |
| }); |
|
|
| it('allows tool-emulated reuse for Opus 4.6 thinking', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4.6-thinking' }), true); |
| }); |
|
|
| it('allows tool-emulated reuse for Opus 4.7 1m', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4-7-1m' }), true); |
| }); |
|
|
| it('rejects Opus 4.5 (outside the widening)', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-opus-4.5' }), false); |
| }); |
|
|
| it('allows tool-emulated reuse for Sonnet 4.6 thinking (#93)', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-sonnet-4-6-thinking' }), true); |
| }); |
|
|
| it('can disable Sonnet 4.6 tool reuse with WINDSURFAPI_DISABLE_SONNET_TOOL_REUSE=1', () => { |
| const previous = process.env.WINDSURFAPI_DISABLE_SONNET_TOOL_REUSE; |
| process.env.WINDSURFAPI_DISABLE_SONNET_TOOL_REUSE = '1'; |
| try { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'claude-sonnet-4-6-thinking' }), false); |
| } finally { |
| if (previous === undefined) { |
| delete process.env.WINDSURFAPI_DISABLE_SONNET_TOOL_REUSE; |
| } else { |
| process.env.WINDSURFAPI_DISABLE_SONNET_TOOL_REUSE = previous; |
| } |
| } |
| }); |
|
|
| it('rejects GPT-5 tool-emulated reuse', () => { |
| assert.equal(shouldUseCascadeReuse({ useCascade: true, emulateTools: true, modelKey: 'gpt-5' }), false); |
| }); |
| }); |
|
|
| describe('shouldUseStrictCascadeReuse', () => { |
| it('strictly binds tool-emulated Opus 4.7 reuse by default', () => { |
| assert.equal(shouldUseStrictCascadeReuse({ |
| emulateTools: true, |
| modelKey: 'claude-opus-4-7-medium', |
| strict: false, |
| allowOpus47Strict: true, |
| }), true); |
| }); |
|
|
| it('strictly binds tool-emulated Opus 4.6 reuse (#59 widening)', () => { |
| assert.equal(shouldUseStrictCascadeReuse({ |
| emulateTools: true, |
| modelKey: 'claude-opus-4.6', |
| strict: false, |
| allowOpus47Strict: true, |
| }), true); |
| }); |
|
|
| it('does not strictly bind other models unless the global flag is on', () => { |
| assert.equal(shouldUseStrictCascadeReuse({ |
| emulateTools: true, |
| modelKey: 'claude-4.5-haiku', |
| strict: false, |
| allowOpus47Strict: true, |
| }), false); |
| }); |
|
|
| it('keeps Sonnet 4.6 out of Opus-only strict reuse', () => { |
| assert.equal(shouldUseStrictCascadeReuse({ |
| emulateTools: true, |
| modelKey: 'claude-sonnet-4-6-thinking', |
| strict: false, |
| allowOpus47Strict: true, |
| }), false); |
| }); |
| }); |
|
|
| describe('isThinkingRequested', () => { |
| it('treats explicit enabled type as a thinking request', () => { |
| assert.equal(isThinkingRequested({ thinking: { type: 'enabled' } }), true); |
| }); |
|
|
| |
| |
| |
| it('treats adaptive type as a thinking request', () => { |
| assert.equal(isThinkingRequested({ thinking: { type: 'adaptive' } }), true); |
| }); |
|
|
| it('treats unknown future thinking types as enabled (forward-compatible)', () => { |
| assert.equal(isThinkingRequested({ thinking: { type: 'whatever_2027' } }), true); |
| }); |
|
|
| it('respects an explicit disabled type', () => { |
| assert.equal(isThinkingRequested({ thinking: { type: 'disabled' } }), false); |
| }); |
|
|
| it('returns false when thinking object lacks a type', () => { |
| assert.equal(isThinkingRequested({ thinking: {} }), false); |
| assert.equal(isThinkingRequested({ thinking: { budget_tokens: 1024 } }), false); |
| }); |
|
|
| it('treats reasoning_effort as a thinking request even without thinking object', () => { |
| assert.equal(isThinkingRequested({ reasoning_effort: 'high' }), true); |
| assert.equal(isThinkingRequested({ reasoning_effort: 'low' }), true); |
| }); |
|
|
| it('returns false for empty or missing body', () => { |
| assert.equal(isThinkingRequested({}), false); |
| assert.equal(isThinkingRequested(null), false); |
| assert.equal(isThinkingRequested(undefined), false); |
| }); |
| }); |
|
|