diff --git a/packages/cli/src/commands/agent/logs.ts b/packages/cli/src/commands/agent/logs.ts index 8e50c748a..1d5168aef 100644 --- a/packages/cli/src/commands/agent/logs.ts +++ b/packages/cli/src/commands/agent/logs.ts @@ -19,6 +19,27 @@ export interface AgentLogsOptions extends CommandOptions { // Logs command returns void - it outputs directly to console export type AgentLogsResult = void +export const NO_ACTIVITY_MESSAGE = 'No activity to display.' + +export async function fetchAgentTimelineItems( + client: DaemonClient, + agentId: string +): Promise { + return fetchProjectedTimelineItems({ client, agentId }) +} + +export function formatAgentActivityTranscript( + timelineItems: AgentTimelineItem[], + tailCount?: number +): string { + if (tailCount === 0) { + return '' + } + return curateAgentActivity( + timelineItems, + tailCount !== undefined ? { maxItems: tailCount } : undefined + ) +} function parseTailCount(raw: string | undefined): number | undefined { if (raw === undefined) return undefined @@ -100,10 +121,7 @@ export async function runLogsCommand( } // Fetch timeline directly via cursor RPC. - let timelineItems = await fetchProjectedTimelineItems({ - client, - agentId: resolvedId, - }) + let timelineItems = await fetchAgentTimelineItems(client, resolvedId) // Apply filter if (options.filter) { @@ -125,7 +143,7 @@ export async function runLogsCommand( return } - const transcript = curateAgentActivity(timelineItems, tailCount !== undefined ? { maxItems: tailCount } : undefined) + const transcript = formatAgentActivityTranscript(timelineItems, tailCount) console.log(transcript) } catch (err) { const message = err instanceof Error ? err.message : String(err) @@ -147,10 +165,7 @@ async function runFollowMode( const tailCount = parseTailCount(options.tail) ?? DEFAULT_FOLLOW_TAIL // First, get existing timeline. - let existingItems = await fetchProjectedTimelineItems({ - client, - agentId, - }) + let existingItems = await fetchAgentTimelineItems(client, agentId) // Apply filter to existing items if (options.filter) { @@ -159,8 +174,8 @@ async function runFollowMode( // Print existing transcript (tail-like behavior) if (tailCount > 0) { - const existingTranscript = curateAgentActivity(existingItems, { maxItems: tailCount }) - if (existingTranscript !== 'No activity to display.') { + const existingTranscript = formatAgentActivityTranscript(existingItems, tailCount) + if (existingTranscript !== NO_ACTIVITY_MESSAGE) { console.log(existingTranscript) } } @@ -181,8 +196,8 @@ async function runFollowMode( return } // Print each timeline item as it arrives using the curator format - const transcript = curateAgentActivity([item]) - if (transcript !== 'No activity to display.') { + const transcript = formatAgentActivityTranscript([item]) + if (transcript !== NO_ACTIVITY_MESSAGE) { console.log(transcript) } } diff --git a/packages/cli/src/commands/agent/wait.ts b/packages/cli/src/commands/agent/wait.ts index 4e49cd264..dfd799a4b 100644 --- a/packages/cli/src/commands/agent/wait.ts +++ b/packages/cli/src/commands/agent/wait.ts @@ -1,6 +1,7 @@ import type { Command } from 'commander' import { connectToDaemon, getDaemonHost } from '../../utils/client.js' import type { CommandOptions, SingleResult, OutputSchema, CommandError } from '../../output/index.js' +import { fetchAgentTimelineItems, formatAgentActivityTranscript } from './logs.js' /** Result type for agent wait command */ export interface AgentWaitResult { @@ -24,6 +25,28 @@ export interface AgentWaitOptions extends CommandOptions { host?: string } +const WAIT_ACTIVITY_PREVIEW_COUNT = 5 + +function appendRecentActivity(message: string, transcript: string | null): string { + if (!transcript || transcript.trim().length === 0) { + return message + } + + return `${message}\nLast ${WAIT_ACTIVITY_PREVIEW_COUNT} activity items:\n${transcript}` +} + +async function getRecentActivityTranscript( + client: Awaited>, + agentId: string +): Promise { + try { + const timelineItems = await fetchAgentTimelineItems(client, agentId) + return formatAgentActivityTranscript(timelineItems, WAIT_ACTIVITY_PREVIEW_COUNT) + } catch { + return null + } +} + /** * Parse duration string to milliseconds. * Supports formats like: 5m, 30s, 1h, 2h30m, 90, etc. @@ -106,6 +129,7 @@ export async function runWaitCommand( timeoutMs = 10 * 60 * 1000 // default 10 minutes } const timeoutSeconds = Math.floor(timeoutMs / 1000) + const timeoutLabel = `${timeoutSeconds} second${timeoutSeconds === 1 ? '' : 's'}` let client try { @@ -123,15 +147,24 @@ export async function runWaitCommand( try { try { const state = await client.waitForFinish(agentIdArg, timeoutMs) + const resolvedAgentId = state.final?.id ?? agentIdArg + const recentActivity = + state.status === 'timeout' || state.status === 'idle' + ? await getRecentActivityTranscript(client, resolvedAgentId) + : null + await client.close() if (state.status === 'timeout') { return { type: 'single', data: { - agentId: agentIdArg, + agentId: resolvedAgentId, status: 'timeout', - message: `Timed out waiting for agent after ${timeoutSeconds} seconds`, + message: appendRecentActivity( + `Agent did not finish within ${timeoutLabel}. Run \`paseo wait ${resolvedAgentId}\` again to keep waiting.`, + recentActivity + ), }, schema: agentWaitSchema, } @@ -142,7 +175,7 @@ export async function runWaitCommand( return { type: 'single', data: { - agentId: state.final?.id ?? agentIdArg, + agentId: resolvedAgentId, status: 'permission', message: permission ? `Agent is waiting for permission: ${permission.kind}` @@ -156,7 +189,7 @@ export async function runWaitCommand( return { type: 'single', data: { - agentId: state.final?.id ?? agentIdArg, + agentId: resolvedAgentId, status: 'error', message: state.error ?? 'Agent finished with error', }, @@ -168,9 +201,9 @@ export async function runWaitCommand( return { type: 'single', data: { - agentId: state.final?.id ?? agentIdArg, + agentId: resolvedAgentId, status: 'idle', - message: 'Agent is now idle', + message: appendRecentActivity('Agent is idle.', recentActivity), }, schema: agentWaitSchema, }