diff --git a/runners/agent-runner/src/index.ts b/runners/agent-runner/src/index.ts index a7e8c32..8df9b46 100644 --- a/runners/agent-runner/src/index.ts +++ b/runners/agent-runner/src/index.ts @@ -32,7 +32,7 @@ interface ContainerInput { interface ContainerOutput { status: 'success' | 'error'; - phase?: 'progress' | 'final'; + phase?: 'progress' | 'final' | 'tool-activity'; result: string | null; newSessionId?: string; error?: string; @@ -554,6 +554,7 @@ async function runQuery( if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_progress') { const tp = message as Record; const summary = typeof tp.summary === 'string' ? tp.summary : ''; + const description = typeof tp.description === 'string' ? tp.description : ''; if (summary) { log(`Subagent progress: ${summary.slice(0, 200)}`); writeOutput({ @@ -563,6 +564,14 @@ async function runQuery( newSessionId, }); } + if (description) { + writeOutput({ + status: 'success', + phase: 'tool-activity', + result: description, + newSessionId, + }); + } } if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_started') { diff --git a/src/agent-runner.ts b/src/agent-runner.ts index c7da5e4..37bba21 100644 --- a/src/agent-runner.ts +++ b/src/agent-runner.ts @@ -41,7 +41,7 @@ export interface AgentInput { export interface AgentOutput { status: 'success' | 'error'; result: string | null; - phase?: 'progress' | 'final'; + phase?: 'progress' | 'final' | 'tool-activity'; newSessionId?: string; error?: string; } @@ -238,7 +238,11 @@ export async function runAgentProcess( const lines = chunk.trim().split('\n'); for (const line of lines) { if (!line) continue; - if (line.includes('Turn in progress') || line.includes('Subagent') || line.includes('Intermediate assistant')) { + if ( + line.includes('Turn in progress') || + line.includes('Subagent') || + line.includes('Intermediate assistant') + ) { logger.info( { group: group.name, chatJid: input.chatJid, runId: input.runId }, line.replace(/^\[.*?\]\s*/, ''), diff --git a/src/message-turn-controller.ts b/src/message-turn-controller.ts index bfb7e88..1303c62 100644 --- a/src/message-turn-controller.ts +++ b/src/message-turn-controller.ts @@ -28,6 +28,7 @@ export class MessageTurnController { private latestProgressText: string | null = null; private previousProgressText: string | null = null; private pendingProgressText: string | null = null; + private toolActivities: string[] = []; private latestProgressRendered: string | null = null; private progressMessageId: string | null = null; private progressStartedAt: number | null = null; @@ -103,6 +104,16 @@ export class MessageTurnController { ); } + if (result.phase === 'tool-activity') { + if (text) { + this.addToolActivity(text); + } + if (!this.poisonedSessionDetected) { + this.resetIdleTimer(); + } + return; + } + if (result.phase === 'progress') { if (text) { this.bufferProgress(text); @@ -217,11 +228,14 @@ export class MessageTurnController { if (minutes > 0) elapsedParts.push(`${minutes}분`); elapsedParts.push(`${seconds}초`); + const activityLines = this.toolActivities.length > 0 + ? '\n' + this.toolActivities.map((a) => `├ ${a}`).join('\n') + : ''; const suffix = `\n\n${elapsedParts.join(' ')}`; - const maxText = 2000 - TASK_STATUS_MESSAGE_PREFIX.length - suffix.length; + const maxText = 2000 - TASK_STATUS_MESSAGE_PREFIX.length - activityLines.length - suffix.length; const truncated = text.length > maxText ? text.slice(0, maxText - 1) + '…' : text; - return `${TASK_STATUS_MESSAGE_PREFIX}${truncated}${suffix}`; + return `${TASK_STATUS_MESSAGE_PREFIX}${truncated}${activityLines}${suffix}`; } private clearProgressTicker(): void { @@ -233,6 +247,7 @@ export class MessageTurnController { private resetProgressState(): void { this.clearProgressTicker(); this.pendingProgressText = null; + this.toolActivities = []; this.latestProgressText = null; this.previousProgressText = null; this.latestProgressRendered = null; @@ -252,6 +267,22 @@ export class MessageTurnController { void this.sendProgressMessage(this.pendingProgressText); } this.pendingProgressText = text; + this.toolActivities = []; + } + + /** + * Append a tool activity line and update the progress message in-place. + */ + private addToolActivity(description: string): void { + const MAX_ACTIVITIES = 5; + this.toolActivities.push(description); + if (this.toolActivities.length > MAX_ACTIVITIES) { + this.toolActivities = this.toolActivities.slice(-MAX_ACTIVITIES); + } + // Update the displayed progress message with tool activity sub-lines + if (this.latestProgressText && this.progressMessageId) { + void this.syncTrackedProgressMessage(); + } } /**