feat: show tool activity sub-lines in progress messages

Display subagent tool descriptions as sub-lines under the main
progress text, updating in-place via Discord message edit.
Shows up to 5 recent tool activities (e.g., "Reading index.ts").
This commit is contained in:
Eyejoker
2026-03-23 18:31:20 +09:00
parent bf1453541f
commit ac11cb7df1
3 changed files with 49 additions and 5 deletions

View File

@@ -32,7 +32,7 @@ interface ContainerInput {
interface ContainerOutput { interface ContainerOutput {
status: 'success' | 'error'; status: 'success' | 'error';
phase?: 'progress' | 'final'; phase?: 'progress' | 'final' | 'tool-activity';
result: string | null; result: string | null;
newSessionId?: string; newSessionId?: string;
error?: string; error?: string;
@@ -554,6 +554,7 @@ async function runQuery(
if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_progress') { if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_progress') {
const tp = message as Record<string, unknown>; const tp = message as Record<string, unknown>;
const summary = typeof tp.summary === 'string' ? tp.summary : ''; const summary = typeof tp.summary === 'string' ? tp.summary : '';
const description = typeof tp.description === 'string' ? tp.description : '';
if (summary) { if (summary) {
log(`Subagent progress: ${summary.slice(0, 200)}`); log(`Subagent progress: ${summary.slice(0, 200)}`);
writeOutput({ writeOutput({
@@ -563,6 +564,14 @@ async function runQuery(
newSessionId, newSessionId,
}); });
} }
if (description) {
writeOutput({
status: 'success',
phase: 'tool-activity',
result: description,
newSessionId,
});
}
} }
if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_started') { if (message.type === 'system' && (message as { subtype?: string }).subtype === 'task_started') {

View File

@@ -41,7 +41,7 @@ export interface AgentInput {
export interface AgentOutput { export interface AgentOutput {
status: 'success' | 'error'; status: 'success' | 'error';
result: string | null; result: string | null;
phase?: 'progress' | 'final'; phase?: 'progress' | 'final' | 'tool-activity';
newSessionId?: string; newSessionId?: string;
error?: string; error?: string;
} }
@@ -238,7 +238,11 @@ export async function runAgentProcess(
const lines = chunk.trim().split('\n'); const lines = chunk.trim().split('\n');
for (const line of lines) { for (const line of lines) {
if (!line) continue; 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( logger.info(
{ group: group.name, chatJid: input.chatJid, runId: input.runId }, { group: group.name, chatJid: input.chatJid, runId: input.runId },
line.replace(/^\[.*?\]\s*/, ''), line.replace(/^\[.*?\]\s*/, ''),

View File

@@ -28,6 +28,7 @@ export class MessageTurnController {
private latestProgressText: string | null = null; private latestProgressText: string | null = null;
private previousProgressText: string | null = null; private previousProgressText: string | null = null;
private pendingProgressText: string | null = null; private pendingProgressText: string | null = null;
private toolActivities: string[] = [];
private latestProgressRendered: string | null = null; private latestProgressRendered: string | null = null;
private progressMessageId: string | null = null; private progressMessageId: string | null = null;
private progressStartedAt: number | 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 (result.phase === 'progress') {
if (text) { if (text) {
this.bufferProgress(text); this.bufferProgress(text);
@@ -217,11 +228,14 @@ export class MessageTurnController {
if (minutes > 0) elapsedParts.push(`${minutes}`); if (minutes > 0) elapsedParts.push(`${minutes}`);
elapsedParts.push(`${seconds}`); elapsedParts.push(`${seconds}`);
const activityLines = this.toolActivities.length > 0
? '\n' + this.toolActivities.map((a) => `${a}`).join('\n')
: '';
const suffix = `\n\n${elapsedParts.join(' ')}`; 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 = const truncated =
text.length > maxText ? text.slice(0, maxText - 1) + '…' : text; 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 { private clearProgressTicker(): void {
@@ -233,6 +247,7 @@ export class MessageTurnController {
private resetProgressState(): void { private resetProgressState(): void {
this.clearProgressTicker(); this.clearProgressTicker();
this.pendingProgressText = null; this.pendingProgressText = null;
this.toolActivities = [];
this.latestProgressText = null; this.latestProgressText = null;
this.previousProgressText = null; this.previousProgressText = null;
this.latestProgressRendered = null; this.latestProgressRendered = null;
@@ -252,6 +267,22 @@ export class MessageTurnController {
void this.sendProgressMessage(this.pendingProgressText); void this.sendProgressMessage(this.pendingProgressText);
} }
this.pendingProgressText = text; 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();
}
} }
/** /**