diff --git a/src/__tests__/mcp-server.test.ts b/src/__tests__/mcp-server.test.ts index cfec208b..021c1497 100644 --- a/src/__tests__/mcp-server.test.ts +++ b/src/__tests__/mcp-server.test.ts @@ -659,6 +659,42 @@ describe('MCP Tool Handlers', () => { expect(result.content[0].text).toContain('Session not found'); }); + it('get_status handler includes pendingQuestion when session is ask_question', async () => { + let callCount = 0; + const ts = Date.now(); + (fetch as any).mockImplementation(() => { + callCount++; + if (callCount === 1) { + return { + ok: true, + json: () => Promise.resolve({ + id: UUID, + status: 'ask_question', + pendingQuestion: { + toolUseId: 'toolu_abc123', + content: 'Which strategy? 1) Clean up 2) Auto-restart 3) Both', + options: ['Clean up', 'Auto-restart', 'Both'], + since: ts, + }, + }), + }; + } + return { ok: true, json: () => Promise.resolve({ alive: true, status: 'ask_question' }) }; + }); + + const handler = getToolHandler('get_status'); + const result = await handler({ sessionId: UUID }); + expect(result.isError).toBeFalsy(); + const data = parseResult(result); + expect(data.status).toBe('ask_question'); + expect(data.pendingQuestion).toEqual({ + toolUseId: 'toolu_abc123', + content: 'Which strategy? 1) Clean up 2) Auto-restart 3) Both', + options: ['Clean up', 'Auto-restart', 'Both'], + since: ts, + }); + }); + // ── get_transcript handler ── it('get_transcript handler returns transcript', async () => { diff --git a/src/server.ts b/src/server.ts index dd0e3c9d..aeed354b 100644 --- a/src/server.ts +++ b/src/server.ts @@ -577,12 +577,12 @@ app.post('/sessions', async (req, reply) => { app.get<{ Params: { id: string } }>('/v1/sessions/:id', async (req, reply) => { const session = sessions.getSession(req.params.id); if (!session) return reply.status(404).send({ error: 'Session not found' }); - return addActionHints(session); + return addActionHints(session, sessions); }); app.get<{ Params: { id: string } }>('/sessions/:id', async (req, reply) => { const session = sessions.getSession(req.params.id); if (!session) return reply.status(404).send({ error: 'Session not found' }); - return addActionHints(session); + return addActionHints(session, sessions); }); // #128: Bulk health check — returns health for all sessions in one request @@ -1276,7 +1276,10 @@ async function reapZombieSessions(): Promise { // ── Helpers ────────────────────────────────────────────────────────── /** Issue #20: Add actionHints to session response for interactive states. */ -function addActionHints(session: import('./session.js').SessionInfo): Record { +function addActionHints( + session: import('./session.js').SessionInfo, + sessions?: SessionManager, +): Record { // #357: Convert Set to array for JSON serialization const result: Record = { ...session, @@ -1288,9 +1291,34 @@ function addActionHints(session: import('./session.js').SessionInfo): Record= 2) return options.slice(0, 4); + return null; +} + function makePayload( event: SessionEvent, sessionId: string, diff --git a/src/session.ts b/src/session.ts index 4b5b06ff..881cc2c3 100644 --- a/src/session.ts +++ b/src/session.ts @@ -85,6 +85,7 @@ interface PendingQuestion { timer: NodeJS.Timeout; toolUseId: string; question: string; + timestamp: number; } export class SessionManager { @@ -955,7 +956,7 @@ export class SessionManager { resolve(null); }, timeoutMs); - this.pendingQuestions.set(sessionId, { resolve, timer, toolUseId, question }); + this.pendingQuestions.set(sessionId, { resolve, timer, toolUseId, question, timestamp: Date.now() }); }); } @@ -976,9 +977,9 @@ export class SessionManager { } /** Issue #336: Get info about a pending question. */ - getPendingQuestionInfo(sessionId: string): { toolUseId: string; question: string } | null { + getPendingQuestionInfo(sessionId: string): { toolUseId: string; question: string; timestamp: number } | null { const pending = this.pendingQuestions.get(sessionId); - return pending ? { toolUseId: pending.toolUseId, question: pending.question } : null; + return pending ? { toolUseId: pending.toolUseId, question: pending.question, timestamp: pending.timestamp } : null; } /** Issue #336: Clean up any pending question for a session. */