Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions apps/mcp-server/src/mcp/handlers/mode.handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,4 +927,91 @@ describe('ModeHandler', () => {
expect(parsed.parallelAgentsRecommendation).toBeUndefined();
});
});

describe('deep thinking instructions in PLAN mode', () => {
it('should include deepThinkingInstructions in PLAN mode response', async () => {
const result = await handler.handle('parse_mode', {
prompt: 'PLAN design auth feature',
});

expect(result?.isError).toBeFalsy();
const parsed = JSON.parse(result!.content[0].text as string);
expect(parsed.deepThinkingInstructions).toBeDefined();
});

it('should include deepThinkingInstructions in AUTO mode response', async () => {
mockKeywordService.parseMode = vi.fn().mockResolvedValue({
...mockParseModeResult,
mode: 'AUTO',
originalPrompt: 'implement dashboard',
});

const result = await handler.handle('parse_mode', {
prompt: 'AUTO implement dashboard',
});

expect(result?.isError).toBeFalsy();
const parsed = JSON.parse(result!.content[0].text as string);
expect(parsed.deepThinkingInstructions).toBeDefined();
});

it('should have correct structure with 3 reasoning steps', async () => {
const result = await handler.handle('parse_mode', {
prompt: 'PLAN design auth feature',
});

const parsed = JSON.parse(result!.content[0].text as string);
const dti = parsed.deepThinkingInstructions;

// reasoning array with 3 steps
expect(dti.reasoning).toHaveLength(3);
expect(dti.reasoning[0].step).toBe('decompose');
expect(dti.reasoning[1].step).toBe('alternatives');
expect(dti.reasoning[2].step).toBe('devils_advocate');

// Each step has an instruction string
for (const r of dti.reasoning) {
expect(typeof r.instruction).toBe('string');
expect(r.instruction.length).toBeGreaterThan(0);
}

// hallucinationPrevention and detailLevel
expect(typeof dti.hallucinationPrevention).toBe('string');
expect(dti.hallucinationPrevention.length).toBeGreaterThan(0);
expect(typeof dti.detailLevel).toBe('string');
expect(dti.detailLevel.length).toBeGreaterThan(0);
});

it('should NOT include deepThinkingInstructions in ACT mode', async () => {
mockKeywordService.parseMode = vi.fn().mockResolvedValue({
...mockParseModeResult,
mode: 'ACT',
originalPrompt: 'implement feature',
});

const result = await handler.handle('parse_mode', {
prompt: 'ACT implement feature',
});

expect(result?.isError).toBeFalsy();
const parsed = JSON.parse(result!.content[0].text as string);
expect(parsed.deepThinkingInstructions).toBeUndefined();
});

it('should NOT include deepThinkingInstructions in EVAL mode', async () => {
mockKeywordService.parseMode = vi.fn().mockResolvedValue({
...mockParseModeResult,
mode: 'EVAL',
originalPrompt: 'evaluate implementation',
});

const result = await handler.handle('parse_mode', {
prompt: 'EVAL evaluate implementation',
});

expect(result?.isError).toBeFalsy();
const parsed = JSON.parse(result!.content[0].text as string);
expect(parsed.deepThinkingInstructions).toBeUndefined();
});
});
});
57 changes: 57 additions & 0 deletions apps/mcp-server/src/mcp/handlers/mode.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ import { AgentService } from '../../agent/agent.service';
/** Maximum length for context title slug generation */
const CONTEXT_TITLE_MAX_LENGTH = 50;

/** Deep thinking reasoning step */
interface DeepThinkingStep {
/** Step identifier */
step: 'decompose' | 'alternatives' | 'devils_advocate';
/** Instruction for this reasoning step */
instruction: string;
}

/** Deep thinking instructions for structured reasoning in PLAN/AUTO modes */
interface DeepThinkingInstructions {
/** Structured reasoning steps */
reasoning: DeepThinkingStep[];
/** Instruction to prevent hallucinated references */
hallucinationPrevention: string;
/** Instruction for required detail level */
detailLevel: string;
}

/** Result type for context document handling */
interface ContextResult {
/** Path to the context file */
Expand Down Expand Up @@ -214,13 +232,18 @@ export class ModeHandler extends AbstractHandler {
result.originalPrompt,
);

// Build deep thinking instructions for PLAN/AUTO modes
const deepThinkingInstructions = this.buildDeepThinkingInstructions(result.mode as Mode);

return createJsonResponse({
...result,
language,
languageInstruction: languageInstructionResult.instruction,
resolvedModel,
// Include dispatch-ready data when available
...(dispatchReady && { dispatchReady }),
// Include deep thinking instructions for PLAN/AUTO modes
...(deepThinkingInstructions && { deepThinkingInstructions }),
// Include context document info (mandatory)
...contextResult,
// Include project root warning when auto-detected and config missing
Expand Down Expand Up @@ -386,6 +409,40 @@ export class ModeHandler extends AbstractHandler {
return dispatchReady;
}

/**
* Build deep thinking instructions for PLAN/AUTO modes.
* Returns undefined for ACT/EVAL modes (not applicable).
*/
private buildDeepThinkingInstructions(mode: Mode): DeepThinkingInstructions | undefined {
if (mode !== 'PLAN' && mode !== 'AUTO') {
return undefined;
}

return {
reasoning: [
{
step: 'decompose',
instruction:
'Break the problem into sub-problems. Identify each distinct concern, dependency, and boundary. List them explicitly before proposing a solution.',
},
{
step: 'alternatives',
instruction:
'For each non-trivial decision, consider at least 2 alternative approaches. Compare trade-offs (complexity, performance, maintainability) and justify your choice.',
},
{
step: 'devils_advocate',
instruction:
'Argue against your own plan. Identify weaknesses, edge cases, and assumptions that could fail. Address each weakness or document it as a known risk.',
},
],
hallucinationPrevention:
'Before including any file path, function name, class name, or API reference in your plan, verify it exists in the codebase. Do not assume or guess — use search tools to confirm.',
detailLevel:
'Every step must include exact file paths, code snippets or signatures, and runnable commands. Avoid vague references like "update the handler" — specify which handler, which method, and what change.',
};
}

/**
* Persist mode state for context recovery after compaction
*/
Expand Down
Loading