mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-06 09:53:07 +00:00
- Changed model identifier from `claude-opus-4-5-20251101` to `claude-opus-4-6` across various files, including documentation and code references. - Updated the SDK to support adaptive thinking for Opus 4.6, allowing the model to determine its own reasoning depth. - Enhanced the thinking level options to include 'adaptive' and adjusted related components to reflect this change. - Updated tests to ensure compatibility with the new model and its features. These changes improve the model's capabilities and user experience by leveraging adaptive reasoning.
520 lines
18 KiB
TypeScript
520 lines
18 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
|
|
describe('sdk-options.ts', () => {
|
|
let originalEnv: NodeJS.ProcessEnv;
|
|
|
|
beforeEach(() => {
|
|
originalEnv = { ...process.env };
|
|
vi.resetModules();
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = originalEnv;
|
|
});
|
|
|
|
describe('TOOL_PRESETS', () => {
|
|
it('should export readOnly tools', async () => {
|
|
const { TOOL_PRESETS } = await import('@/lib/sdk-options.js');
|
|
expect(TOOL_PRESETS.readOnly).toEqual(['Read', 'Glob', 'Grep']);
|
|
});
|
|
|
|
it('should export specGeneration tools', async () => {
|
|
const { TOOL_PRESETS } = await import('@/lib/sdk-options.js');
|
|
expect(TOOL_PRESETS.specGeneration).toEqual(['Read', 'Glob', 'Grep']);
|
|
});
|
|
|
|
it('should export fullAccess tools', async () => {
|
|
const { TOOL_PRESETS } = await import('@/lib/sdk-options.js');
|
|
expect(TOOL_PRESETS.fullAccess).toContain('Read');
|
|
expect(TOOL_PRESETS.fullAccess).toContain('Write');
|
|
expect(TOOL_PRESETS.fullAccess).toContain('Edit');
|
|
expect(TOOL_PRESETS.fullAccess).toContain('Bash');
|
|
});
|
|
|
|
it('should export chat tools matching fullAccess', async () => {
|
|
const { TOOL_PRESETS } = await import('@/lib/sdk-options.js');
|
|
expect(TOOL_PRESETS.chat).toEqual(TOOL_PRESETS.fullAccess);
|
|
});
|
|
});
|
|
|
|
describe('MAX_TURNS', () => {
|
|
it('should export turn presets', async () => {
|
|
const { MAX_TURNS } = await import('@/lib/sdk-options.js');
|
|
expect(MAX_TURNS.quick).toBe(50);
|
|
expect(MAX_TURNS.standard).toBe(100);
|
|
expect(MAX_TURNS.extended).toBe(250);
|
|
expect(MAX_TURNS.maximum).toBe(1000);
|
|
});
|
|
});
|
|
|
|
describe('getModelForUseCase', () => {
|
|
it('should return explicit model when provided', async () => {
|
|
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
|
|
const result = getModelForUseCase('spec', 'claude-sonnet-4-20250514');
|
|
expect(result).toBe('claude-sonnet-4-20250514');
|
|
});
|
|
|
|
it('should use environment variable for spec model', async () => {
|
|
process.env.AUTOMAKER_MODEL_SPEC = 'claude-sonnet-4-20250514';
|
|
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
|
|
const result = getModelForUseCase('spec');
|
|
expect(result).toBe('claude-sonnet-4-20250514');
|
|
});
|
|
|
|
it('should use default model for spec when no override', async () => {
|
|
delete process.env.AUTOMAKER_MODEL_SPEC;
|
|
delete process.env.AUTOMAKER_MODEL_DEFAULT;
|
|
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
|
|
const result = getModelForUseCase('spec');
|
|
expect(result).toContain('claude');
|
|
});
|
|
|
|
it('should fall back to AUTOMAKER_MODEL_DEFAULT', async () => {
|
|
delete process.env.AUTOMAKER_MODEL_SPEC;
|
|
process.env.AUTOMAKER_MODEL_DEFAULT = 'claude-sonnet-4-20250514';
|
|
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
|
|
const result = getModelForUseCase('spec');
|
|
expect(result).toBe('claude-sonnet-4-20250514');
|
|
});
|
|
});
|
|
|
|
describe('createSpecGenerationOptions', () => {
|
|
it('should create options with spec generation settings', async () => {
|
|
const { createSpecGenerationOptions, TOOL_PRESETS, MAX_TURNS } =
|
|
await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(MAX_TURNS.maximum);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.specGeneration]);
|
|
expect(options.permissionMode).toBe('default');
|
|
});
|
|
|
|
it('should include system prompt when provided', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
systemPrompt: 'Custom prompt',
|
|
});
|
|
|
|
expect(options.systemPrompt).toBe('Custom prompt');
|
|
});
|
|
|
|
it('should include abort controller when provided', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const abortController = new AbortController();
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
abortController,
|
|
});
|
|
|
|
expect(options.abortController).toBe(abortController);
|
|
});
|
|
});
|
|
|
|
describe('createFeatureGenerationOptions', () => {
|
|
it('should create options with feature generation settings', async () => {
|
|
const { createFeatureGenerationOptions, TOOL_PRESETS, MAX_TURNS } =
|
|
await import('@/lib/sdk-options.js');
|
|
|
|
const options = createFeatureGenerationOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(MAX_TURNS.quick);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.readOnly]);
|
|
});
|
|
});
|
|
|
|
describe('createSuggestionsOptions', () => {
|
|
it('should create options with suggestions settings', async () => {
|
|
const { createSuggestionsOptions, TOOL_PRESETS, MAX_TURNS } =
|
|
await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSuggestionsOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(MAX_TURNS.extended);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.readOnly]);
|
|
});
|
|
|
|
it('should include systemPrompt when provided', async () => {
|
|
const { createSuggestionsOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSuggestionsOptions({
|
|
cwd: '/test/path',
|
|
systemPrompt: 'Custom prompt',
|
|
});
|
|
|
|
expect(options.systemPrompt).toBe('Custom prompt');
|
|
});
|
|
|
|
it('should include abortController when provided', async () => {
|
|
const { createSuggestionsOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const abortController = new AbortController();
|
|
const options = createSuggestionsOptions({
|
|
cwd: '/test/path',
|
|
abortController,
|
|
});
|
|
|
|
expect(options.abortController).toBe(abortController);
|
|
});
|
|
|
|
it('should include outputFormat when provided', async () => {
|
|
const { createSuggestionsOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSuggestionsOptions({
|
|
cwd: '/test/path',
|
|
outputFormat: { type: 'json' },
|
|
});
|
|
|
|
expect(options.outputFormat).toEqual({ type: 'json' });
|
|
});
|
|
});
|
|
|
|
describe('createChatOptions', () => {
|
|
it('should create options with chat settings', async () => {
|
|
const { createChatOptions, TOOL_PRESETS, MAX_TURNS } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createChatOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(MAX_TURNS.standard);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.chat]);
|
|
});
|
|
|
|
it('should prefer explicit model over session model', async () => {
|
|
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createChatOptions({
|
|
cwd: '/test/path',
|
|
model: 'claude-opus-4-20250514',
|
|
sessionModel: 'claude-haiku-3-5-20241022',
|
|
});
|
|
|
|
expect(options.model).toBe('claude-opus-4-20250514');
|
|
});
|
|
|
|
it('should use session model when explicit model not provided', async () => {
|
|
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createChatOptions({
|
|
cwd: '/test/path',
|
|
sessionModel: 'claude-sonnet-4-20250514',
|
|
});
|
|
|
|
expect(options.model).toBe('claude-sonnet-4-20250514');
|
|
});
|
|
});
|
|
|
|
describe('createAutoModeOptions', () => {
|
|
it('should create options with auto mode settings', async () => {
|
|
const { createAutoModeOptions, TOOL_PRESETS, MAX_TURNS } =
|
|
await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(MAX_TURNS.maximum);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.fullAccess]);
|
|
});
|
|
|
|
it('should include systemPrompt when provided', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
systemPrompt: 'Custom prompt',
|
|
});
|
|
|
|
expect(options.systemPrompt).toBe('Custom prompt');
|
|
});
|
|
|
|
it('should include abortController when provided', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const abortController = new AbortController();
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
abortController,
|
|
});
|
|
|
|
expect(options.abortController).toBe(abortController);
|
|
});
|
|
});
|
|
|
|
describe('createCustomOptions', () => {
|
|
it('should create options with custom settings', async () => {
|
|
const { createCustomOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createCustomOptions({
|
|
cwd: '/test/path',
|
|
maxTurns: 10,
|
|
allowedTools: ['Read', 'Write'],
|
|
});
|
|
|
|
expect(options.cwd).toBe('/test/path');
|
|
expect(options.maxTurns).toBe(10);
|
|
expect(options.allowedTools).toEqual(['Read', 'Write']);
|
|
});
|
|
|
|
it('should use defaults when optional params not provided', async () => {
|
|
const { createCustomOptions, TOOL_PRESETS, MAX_TURNS } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createCustomOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.maxTurns).toBe(MAX_TURNS.maximum);
|
|
expect(options.allowedTools).toEqual([...TOOL_PRESETS.readOnly]);
|
|
});
|
|
|
|
it('should include systemPrompt when provided', async () => {
|
|
const { createCustomOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createCustomOptions({
|
|
cwd: '/test/path',
|
|
systemPrompt: 'Custom prompt',
|
|
});
|
|
|
|
expect(options.systemPrompt).toBe('Custom prompt');
|
|
});
|
|
|
|
it('should include abortController when provided', async () => {
|
|
const { createCustomOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const abortController = new AbortController();
|
|
const options = createCustomOptions({
|
|
cwd: '/test/path',
|
|
abortController,
|
|
});
|
|
|
|
expect(options.abortController).toBe(abortController);
|
|
});
|
|
});
|
|
|
|
describe('getThinkingTokenBudget (from @automaker/types)', () => {
|
|
it('should return undefined for "none" thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget('none')).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined for undefined thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget(undefined)).toBeUndefined();
|
|
});
|
|
|
|
it('should return 1024 for "low" thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget('low')).toBe(1024);
|
|
});
|
|
|
|
it('should return 10000 for "medium" thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget('medium')).toBe(10000);
|
|
});
|
|
|
|
it('should return 16000 for "high" thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget('high')).toBe(16000);
|
|
});
|
|
|
|
it('should return 32000 for "ultrathink" thinking level', async () => {
|
|
const { getThinkingTokenBudget } = await import('@automaker/types');
|
|
expect(getThinkingTokenBudget('ultrathink')).toBe(32000);
|
|
});
|
|
});
|
|
|
|
describe('THINKING_TOKEN_BUDGET constant', () => {
|
|
it('should have correct values for all thinking levels', async () => {
|
|
const { THINKING_TOKEN_BUDGET } = await import('@automaker/types');
|
|
|
|
expect(THINKING_TOKEN_BUDGET.none).toBeUndefined();
|
|
expect(THINKING_TOKEN_BUDGET.low).toBe(1024);
|
|
expect(THINKING_TOKEN_BUDGET.medium).toBe(10000);
|
|
expect(THINKING_TOKEN_BUDGET.high).toBe(16000);
|
|
expect(THINKING_TOKEN_BUDGET.ultrathink).toBe(32000);
|
|
});
|
|
|
|
it('should have minimum of 1024 for enabled thinking levels', async () => {
|
|
const { THINKING_TOKEN_BUDGET } = await import('@automaker/types');
|
|
|
|
// Per Claude SDK docs: minimum is 1024 tokens
|
|
expect(THINKING_TOKEN_BUDGET.low).toBeGreaterThanOrEqual(1024);
|
|
expect(THINKING_TOKEN_BUDGET.medium).toBeGreaterThanOrEqual(1024);
|
|
expect(THINKING_TOKEN_BUDGET.high).toBeGreaterThanOrEqual(1024);
|
|
expect(THINKING_TOKEN_BUDGET.ultrathink).toBeGreaterThanOrEqual(1024);
|
|
});
|
|
|
|
it('should have ultrathink at or below 32000 to avoid timeouts', async () => {
|
|
const { THINKING_TOKEN_BUDGET } = await import('@automaker/types');
|
|
|
|
// Per Claude SDK docs: above 32000 risks timeouts
|
|
expect(THINKING_TOKEN_BUDGET.ultrathink).toBeLessThanOrEqual(32000);
|
|
});
|
|
});
|
|
|
|
describe('thinking level integration with SDK options', () => {
|
|
describe('createSpecGenerationOptions with thinkingLevel', () => {
|
|
it('should not include maxThinkingTokens when thinkingLevel is undefined', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
|
|
it('should not include maxThinkingTokens when thinkingLevel is "none"', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'none',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
|
|
it('should include maxThinkingTokens for "low" thinkingLevel', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'low',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(1024);
|
|
});
|
|
|
|
it('should include maxThinkingTokens for "high" thinkingLevel', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'high',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(16000);
|
|
});
|
|
|
|
it('should include maxThinkingTokens for "ultrathink" thinkingLevel', async () => {
|
|
const { createSpecGenerationOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSpecGenerationOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'ultrathink',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(32000);
|
|
});
|
|
});
|
|
|
|
describe('createAutoModeOptions with thinkingLevel', () => {
|
|
it('should not include maxThinkingTokens when thinkingLevel is undefined', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({ cwd: '/test/path' });
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
|
|
it('should include maxThinkingTokens for "medium" thinkingLevel', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'medium',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(10000);
|
|
});
|
|
|
|
it('should include maxThinkingTokens for "ultrathink" thinkingLevel', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'ultrathink',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(32000);
|
|
});
|
|
});
|
|
|
|
describe('createChatOptions with thinkingLevel', () => {
|
|
it('should include maxThinkingTokens for enabled thinkingLevel', async () => {
|
|
const { createChatOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createChatOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'high',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(16000);
|
|
});
|
|
});
|
|
|
|
describe('createSuggestionsOptions with thinkingLevel', () => {
|
|
it('should include maxThinkingTokens for enabled thinkingLevel', async () => {
|
|
const { createSuggestionsOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createSuggestionsOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'low',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(1024);
|
|
});
|
|
});
|
|
|
|
describe('createCustomOptions with thinkingLevel', () => {
|
|
it('should include maxThinkingTokens for enabled thinkingLevel', async () => {
|
|
const { createCustomOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createCustomOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'medium',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBe(10000);
|
|
});
|
|
|
|
it('should not include maxThinkingTokens when thinkingLevel is "none"', async () => {
|
|
const { createCustomOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createCustomOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'none',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('adaptive thinking for Opus 4.6', () => {
|
|
it('should not set maxThinkingTokens for adaptive thinking (model decides)', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'adaptive',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
|
|
it('should not include maxThinkingTokens when thinkingLevel is "none"', async () => {
|
|
const { createAutoModeOptions } = await import('@/lib/sdk-options.js');
|
|
|
|
const options = createAutoModeOptions({
|
|
cwd: '/test/path',
|
|
thinkingLevel: 'none',
|
|
});
|
|
|
|
expect(options.maxThinkingTokens).toBeUndefined();
|
|
});
|
|
});
|
|
});
|
|
});
|