import { ApprovalRequestId, isToolLifecycleItemType, ProviderApprovalOption, ProviderRequestKind,} from "@t3tools/contracts";import type { OrchestrationLatestTurn, OrchestrationThread, OrchestrationThreadActivity, ToolLifecycleItemType, TurnId, UserInputQuestion,} from "@t3tools/contracts";import { formatDuration } from "@t3tools/shared/orchestrationTiming"; import * as Arr from "effect/Array";import * as Order from "effect/Order";import * as Schema from "effect/Schema"; export interface PendingApproval { readonly requestId: ApprovalRequestId; readonly requestKind: ProviderRequestKind; readonly createdAt: string; readonly detail?: string; readonly appName?: string; readonly options?: ReadonlyArray<ProviderApprovalOption>;} const isProviderRequestKind = Schema.is(ProviderRequestKind);const isProviderApprovalOption = Schema.is(ProviderApprovalOption); export interface PendingUserInput { readonly requestId: ApprovalRequestId; readonly createdAt: string; readonly questions: ReadonlyArray<UserInputQuestion>;} export interface PendingUserInputDraftAnswer { readonly selectedOptionLabels?: ReadonlyArray<string>; readonly customAnswer?: string;} export interface ThreadFeedActivity { readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly summary: string; readonly detail: string | null; readonly canExpand: boolean; readonly getFullDetail: () => string | null; readonly getCopyText: () => string; readonly icon: | "agent" | "alert" | "check" | "command" | "edit" | "eye" | "globe" | "hammer" | "message" | "warning" | "wrench" | "zap"; readonly toolLike: boolean; readonly status: "success" | "failure" | "neutral" | null;} const MAX_VISIBLE_WORK_LOG_ENTRIES = 1; type WorkLogToolLifecycleStatus = "inProgress" | "completed" | "failed" | "declined" | "stopped"; interface WorkLogEntry { id: string; createdAt: string; turnId: TurnId | null; label: string; detail?: string; command?: string; rawCommand?: string; changedFiles?: ReadonlyArray<string>; tone: "thinking" | "tool" | "info" | "error"; toolTitle?: string; itemType?: ToolLifecycleItemType; requestKind?: PendingApproval["requestKind"]; toolLifecycleStatus?: WorkLogToolLifecycleStatus; toolData?: unknown;} interface DerivedWorkLogEntry extends WorkLogEntry { activityKind: OrchestrationThreadActivity["kind"]; collapseKey?: string; /** Grouping key for subagent lifecycle rows (one row per agent). */ taskId?: string;} type RawThreadFeedEntry = | { readonly type: "message"; readonly id: string; readonly createdAt: string; readonly message: OrchestrationThread["messages"][number]; } | { readonly type: "activity"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly activity: ThreadFeedActivity; }; export type ThreadFeedEntry = | Extract<RawThreadFeedEntry, { type: "message" }> | { readonly type: "working"; readonly id: string; readonly createdAt: string; } | { readonly type: "activity-group"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly activities: ReadonlyArray<ThreadFeedActivity>; } | { readonly type: "work-toggle"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly groupId: string; readonly hiddenCount: number; readonly expanded: boolean; readonly onlyToolActivities: boolean; } | { readonly type: "turn-fold"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId; readonly label: string; readonly expanded: boolean; }; export type ThreadFeedLatestTurn = Pick< OrchestrationLatestTurn, "turnId" | "state" | "startedAt" | "completedAt">; function requestKindFromRequestType(requestType: unknown): PendingApproval["requestKind"] | null { switch (requestType) { case "command_execution_approval": case "exec_command_approval": return "command"; case "file_read_approval": return "file-read"; case "file_change_approval": case "apply_patch_approval": return "file-change"; case "mcp_elicitation_approval": return "mcp-elicitation"; default: return null; }} function isStalePendingRequestFailureDetail(detail: string | undefined): boolean { const normalized = detail?.toLowerCase(); if (!normalized) { return false; } return ( normalized.includes("stale pending approval request") || normalized.includes("stale pending user-input request") || normalized.includes("unknown pending approval request") || normalized.includes("unknown pending permission request") || normalized.includes("unknown pending user-input request") );} function parseApprovalRequestId(value: unknown): ApprovalRequestId | null { return typeof value === "string" && value.length > 0 ? ApprovalRequestId.make(value) : null;} function parseUserInputQuestions( payload: Record<string, unknown> | null,): ReadonlyArray<UserInputQuestion> | null { const questions = payload?.questions; if (!Array.isArray(questions)) { return null; } const parsed = questions .map<UserInputQuestion | null>((entry) => { if (!entry || typeof entry !== "object") return null; const question = entry as Record<string, unknown>; if ( typeof question.id !== "string" || typeof question.header !== "string" || typeof question.question !== "string" || !Array.isArray(question.options) ) { return null; } const options = question.options .map<UserInputQuestion["options"][number] | null>((option) => { if (!option || typeof option !== "object") return null; const record = option as Record<string, unknown>; if (typeof record.label !== "string" || typeof record.description !== "string") { return null; } return { label: record.label, description: record.description, }; }) .filter((option): option is UserInputQuestion["options"][number] => option !== null); if (options.length === 0) { return null; } return { id: question.id, header: question.header, question: question.question, options, multiSelect: question.multiSelect === true, }; }) .filter((question): question is UserInputQuestion => question !== null); return parsed.length > 0 ? parsed : null;} function normalizeDraftAnswer(value: string | undefined): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null;} function normalizeSelectedOptionLabels( value: ReadonlyArray<string> | undefined,): ReadonlyArray<string> { if (!Array.isArray(value)) { return []; } return Array.from( new Set(value.map((entry) => entry.trim()).filter((entry) => entry.length > 0)), );} function resolvePendingUserInputAnswer( question: UserInputQuestion, draft: PendingUserInputDraftAnswer | undefined,): string | ReadonlyArray<string> | null { const customAnswer = normalizeDraftAnswer(draft?.customAnswer); if (customAnswer) { return customAnswer; } const selectedOptionLabels = normalizeSelectedOptionLabels(draft?.selectedOptionLabels); if (question.multiSelect) { return selectedOptionLabels.length > 0 ? selectedOptionLabels : null; } return selectedOptionLabels[0] ?? null;} /** Codex children settle via task.updated (idle/failed/interrupted), never * task.completed — these rows are mobile's only terminal signal for them. */const MOBILE_TERMINAL_UPDATE_STATUSES: ReadonlySet<string> = new Set([ "idle", "completed", "failed", "cancelled", "interrupted",]); function isTerminalBypassUpdate(activity: OrchestrationThreadActivity): boolean { if (activity.kind !== "task.updated") { return false; } const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; return ( payload?.timelineBypass === true && typeof payload.status === "string" && MOBILE_TERMINAL_UPDATE_STATUSES.has(payload.status) );} /** * Quiet-timeline guarantee (mirrors web's session-logic): agent-internal * activity lives in the Agents sheet, not the work log. Terminal rows are * kept — with no Agents surface on mobile they are the terminal signal * (a surface that hides rows must keep its own terminal signal). That means * task.completed (Claude) AND terminal bypassed task.updated (Codex, whose * children never emit task.completed — review finding). */function isAgentInternalActivity(activity: OrchestrationThreadActivity): boolean { const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; if (!payload) { return false; } const isTerminalTaskRow = activity.kind === "task.completed" || isTerminalBypassUpdate(activity); if (payload.timelineBypass === true && !isTerminalTaskRow) { return true; } // agentId marks ownership, not "hide me": a NESTED AGENT's terminal row is // the only signal mobile gets (no Agents sheet), so it stays. Only an // agent's own background work (stamped "background") is internal — same // rule as web (review finding: hiding on agentId alone dropped nested // completions with no replacement UI). const ownedByAgent = typeof payload.agentId === "string" && payload.agentId.trim().length > 0; if (!ownedByAgent) { return false; } return !(isTerminalTaskRow && payload.agentKind === "agent");} function deriveWorkLogEntries( activities: ReadonlyArray<OrchestrationThreadActivity>,): DerivedWorkLogEntry[] { const ordered = Arr.sort(activities, activityOrder); const entries: DerivedWorkLogEntry[] = []; for (const activity of ordered) { if (activity.kind === "tool.started") continue; if (activity.kind === "task.started") continue; // Terminal bypassed updates pass: Codex children's only terminal signal. if (activity.kind === "task.updated" && !isTerminalBypassUpdate(activity)) continue; if (activity.kind === "tool.progress") continue; if (activity.kind === "context-window.updated") continue; if (activity.summary === "Checkpoint captured") continue; if (isPlanBoundaryToolActivity(activity)) continue; if (isAgentInternalActivity(activity)) continue; entries.push(toDerivedWorkLogEntry(activity)); } return collapseDerivedWorkLogEntries(entries);} function isPlanBoundaryToolActivity(activity: OrchestrationThreadActivity): boolean { if (activity.kind !== "tool.updated" && activity.kind !== "tool.completed") { return false; } const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; return typeof payload?.detail === "string" && payload.detail.startsWith("ExitPlanMode:");} function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWorkLogEntry { const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; const commandPreview = extractToolCommand(payload); const changedFiles = extractChangedFiles(payload); const title = extractToolTitle(payload); // task.updated included: terminal bypassed updates (Codex children's only // terminal signal) must carry task identity so they collapse per child // instead of stacking anonymous "Task idle" rows. const isTaskActivity = activity.kind === "task.progress" || activity.kind === "task.completed" || activity.kind === "task.updated"; const taskSummary = isTaskActivity && typeof payload?.summary === "string" && payload.summary.length > 0 ? payload.summary : null; const taskDetailAsLabel = isTaskActivity && !taskSummary && typeof payload?.detail === "string" && payload.detail.length > 0 ? payload.detail : null; const taskLabel = taskSummary || taskDetailAsLabel; const taskId = isTaskActivity && typeof payload?.taskId === "string" && payload.taskId.length > 0 ? payload.taskId : undefined; const entry: DerivedWorkLogEntry = { id: activity.id, createdAt: activity.createdAt, turnId: activity.turnId, ...(taskId ? { taskId } : {}), label: taskLabel || activity.summary, tone: activity.kind === "task.progress" ? "thinking" : activity.tone === "approval" ? "info" : activity.tone, activityKind: activity.kind, }; const itemType = extractWorkLogItemType(payload); const requestKind = extractWorkLogRequestKind(payload); if ( !taskDetailAsLabel && payload && typeof payload.detail === "string" && payload.detail.length > 0 ) { const detail = stripTrailingExitCode(payload.detail).output; if (detail) { entry.detail = detail; } } if (commandPreview.command) { entry.command = commandPreview.command; } if (commandPreview.rawCommand) { entry.rawCommand = commandPreview.rawCommand; } if (changedFiles.length > 0) { entry.changedFiles = changedFiles; } if (title) { entry.toolTitle = title; } if (itemType === "mcp_tool_call") { const data = asRecord(payload?.data); if (data?.item !== undefined) { entry.toolData = data.item; } } if (itemType) { entry.itemType = itemType; } if (requestKind) { entry.requestKind = requestKind; } let toolLifecycleStatus = extractWorkLogToolLifecycleStatus(payload); if (!toolLifecycleStatus && activity.kind === "tool.completed") { toolLifecycleStatus = "completed"; } if (toolLifecycleStatus) { entry.toolLifecycleStatus = toolLifecycleStatus; } const collapseKey = deriveToolLifecycleCollapseKey(entry); if (collapseKey) { entry.collapseKey = collapseKey; } return entry;} function collapseDerivedWorkLogEntries( entries: ReadonlyArray<DerivedWorkLogEntry>,): DerivedWorkLogEntry[] { const collapsed: DerivedWorkLogEntry[] = []; // Subagent rows collapse by identity, not adjacency (quiet-timeline // guarantee; mirrors web's session-logic). const taskRowIndex = new Map<string, number>(); for (const entry of entries) { const isTaskRow = entry.taskId !== undefined && (entry.activityKind === "task.progress" || entry.activityKind === "task.completed" || entry.activityKind === "task.updated"); if (isTaskRow && entry.taskId !== undefined) { const existingIndex = taskRowIndex.get(entry.taskId); if (existingIndex !== undefined) { collapsed[existingIndex] = mergeDerivedWorkLogEntries(collapsed[existingIndex]!, entry); continue; } taskRowIndex.set(entry.taskId, collapsed.length); collapsed.push(entry); continue; } const previous = collapsed.at(-1); if (previous && shouldCollapseToolLifecycleEntries(previous, entry)) { collapsed[collapsed.length - 1] = mergeDerivedWorkLogEntries(previous, entry); continue; } collapsed.push(entry); } return collapsed;} function shouldCollapseToolLifecycleEntries( previous: DerivedWorkLogEntry, next: DerivedWorkLogEntry,): boolean { if (previous.activityKind !== "tool.updated" && previous.activityKind !== "tool.completed") { return false; } if (next.activityKind !== "tool.updated" && next.activityKind !== "tool.completed") { return false; } if (previous.activityKind === "tool.completed") { return false;Lines 1–500 of 1609. Load more
import { ApprovalRequestId, isToolLifecycleItemType, ProviderApprovalOption, ProviderRequestKind, } from "@t3tools/contracts"; import type { OrchestrationLatestTurn, OrchestrationThread, OrchestrationThreadActivity, ToolLifecycleItemType, TurnId, UserInputQuestion, } from "@t3tools/contracts"; import { formatDuration } from "@t3tools/shared/orchestrationTiming"; import * as Arr from "effect/Array"; import * as Order from "effect/Order"; import * as Schema from "effect/Schema"; export interface PendingApproval { readonly requestId: ApprovalRequestId; readonly requestKind: ProviderRequestKind; readonly createdAt: string; readonly detail?: string; readonly appName?: string; readonly options?: ReadonlyArray<ProviderApprovalOption>; } const isProviderRequestKind = Schema.is(ProviderRequestKind); const isProviderApprovalOption = Schema.is(ProviderApprovalOption); export interface PendingUserInput { readonly requestId: ApprovalRequestId; readonly createdAt: string; readonly questions: ReadonlyArray<UserInputQuestion>; } export interface PendingUserInputDraftAnswer { readonly selectedOptionLabels?: ReadonlyArray<string>; readonly customAnswer?: string; } export interface ThreadFeedActivity { readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly summary: string; readonly detail: string | null; readonly canExpand: boolean; readonly getFullDetail: () => string | null; readonly getCopyText: () => string; readonly icon: | "agent" | "alert" | "check" | "command" | "edit" | "eye" | "globe" | "hammer" | "message" | "warning" | "wrench" | "zap"; readonly toolLike: boolean; readonly status: "success" | "failure" | "neutral" | null; } const MAX_VISIBLE_WORK_LOG_ENTRIES = 1; type WorkLogToolLifecycleStatus = "inProgress" | "completed" | "failed" | "declined" | "stopped"; interface WorkLogEntry { id: string; createdAt: string; turnId: TurnId | null; label: string; detail?: string; command?: string; rawCommand?: string; changedFiles?: ReadonlyArray<string>; tone: "thinking" | "tool" | "info" | "error"; toolTitle?: string; itemType?: ToolLifecycleItemType; requestKind?: PendingApproval["requestKind"]; toolLifecycleStatus?: WorkLogToolLifecycleStatus; toolData?: unknown; } interface DerivedWorkLogEntry extends WorkLogEntry { activityKind: OrchestrationThreadActivity["kind"]; collapseKey?: string; /** Grouping key for subagent lifecycle rows (one row per agent). */ taskId?: string; } type RawThreadFeedEntry = | { readonly type: "message"; readonly id: string; readonly createdAt: string; readonly message: OrchestrationThread["messages"][number]; } | { readonly type: "activity"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly activity: ThreadFeedActivity; }; export type ThreadFeedEntry = | Extract<RawThreadFeedEntry, { type: "message" }> | { readonly type: "working"; readonly id: string; readonly createdAt: string; } | { readonly type: "activity-group"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly activities: ReadonlyArray<ThreadFeedActivity>; } | { readonly type: "work-toggle"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId | null; readonly groupId: string; readonly hiddenCount: number; readonly expanded: boolean; readonly onlyToolActivities: boolean; } | { readonly type: "turn-fold"; readonly id: string; readonly createdAt: string; readonly turnId: TurnId; readonly label: string; readonly expanded: boolean; }; export type ThreadFeedLatestTurn = Pick< OrchestrationLatestTurn, "turnId" | "state" | "startedAt" | "completedAt" >; function requestKindFromRequestType(requestType: unknown): PendingApproval["requestKind"] | null { switch (requestType) { case "command_execution_approval": case "exec_command_approval": return "command"; case "file_read_approval": return "file-read"; case "file_change_approval": case "apply_patch_approval": return "file-change"; case "mcp_elicitation_approval": return "mcp-elicitation"; default: return null; } } function isStalePendingRequestFailureDetail(detail: string | undefined): boolean { const normalized = detail?.toLowerCase(); if (!normalized) { return false; } return ( normalized.includes("stale pending approval request") || normalized.includes("stale pending user-input request") || normalized.includes("unknown pending approval request") || normalized.includes("unknown pending permission request") || normalized.includes("unknown pending user-input request") ); } function parseApprovalRequestId(value: unknown): ApprovalRequestId | null { return typeof value === "string" && value.length > 0 ? ApprovalRequestId.make(value) : null; } function parseUserInputQuestions( payload: Record<string, unknown> | null, ): ReadonlyArray<UserInputQuestion> | null { const questions = payload?.questions; if (!Array.isArray(questions)) { return null; } const parsed = questions .map<UserInputQuestion | null>((entry) => { if (!entry || typeof entry !== "object") return null; const question = entry as Record<string, unknown>; if ( typeof question.id !== "string" || typeof question.header !== "string" || typeof question.question !== "string" || !Array.isArray(question.options) ) { return null; } const options = question.options .map<UserInputQuestion["options"][number] | null>((option) => { if (!option || typeof option !== "object") return null; const record = option as Record<string, unknown>; if (typeof record.label !== "string" || typeof record.description !== "string") { return null; } return { label: record.label, description: record.description, }; }) .filter((option): option is UserInputQuestion["options"][number] => option !== null); if (options.length === 0) { return null; } return { id: question.id, header: question.header, question: question.question, options, multiSelect: question.multiSelect === true, }; }) .filter((question): question is UserInputQuestion => question !== null); return parsed.length > 0 ? parsed : null; } function normalizeDraftAnswer(value: string | undefined): string | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; } function normalizeSelectedOptionLabels( value: ReadonlyArray<string> | undefined, ): ReadonlyArray<string> { if (!Array.isArray(value)) { return []; } return Array.from( new Set(value.map((entry) => entry.trim()).filter((entry) => entry.length > 0)), ); } function resolvePendingUserInputAnswer( question: UserInputQuestion, draft: PendingUserInputDraftAnswer | undefined, ): string | ReadonlyArray<string> | null { const customAnswer = normalizeDraftAnswer(draft?.customAnswer); if (customAnswer) { return customAnswer; } const selectedOptionLabels = normalizeSelectedOptionLabels(draft?.selectedOptionLabels); if (question.multiSelect) { return selectedOptionLabels.length > 0 ? selectedOptionLabels : null; } return selectedOptionLabels[0] ?? null; } /** Codex children settle via task.updated (idle/failed/interrupted), never * task.completed — these rows are mobile's only terminal signal for them. */ const MOBILE_TERMINAL_UPDATE_STATUSES: ReadonlySet<string> = new Set([ "idle", "completed", "failed", "cancelled", "interrupted", ]); function isTerminalBypassUpdate(activity: OrchestrationThreadActivity): boolean { if (activity.kind !== "task.updated") { return false; } const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; return ( payload?.timelineBypass === true && typeof payload.status === "string" && MOBILE_TERMINAL_UPDATE_STATUSES.has(payload.status) ); } /** * Quiet-timeline guarantee (mirrors web's session-logic): agent-internal * activity lives in the Agents sheet, not the work log. Terminal rows are * kept — with no Agents surface on mobile they are the terminal signal * (a surface that hides rows must keep its own terminal signal). That means * task.completed (Claude) AND terminal bypassed task.updated (Codex, whose * children never emit task.completed — review finding). */ function isAgentInternalActivity(activity: OrchestrationThreadActivity): boolean { const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; if (!payload) { return false; } const isTerminalTaskRow = activity.kind === "task.completed" || isTerminalBypassUpdate(activity); if (payload.timelineBypass === true && !isTerminalTaskRow) { return true; } // agentId marks ownership, not "hide me": a NESTED AGENT's terminal row is // the only signal mobile gets (no Agents sheet), so it stays. Only an // agent's own background work (stamped "background") is internal — same // rule as web (review finding: hiding on agentId alone dropped nested // completions with no replacement UI). const ownedByAgent = typeof payload.agentId === "string" && payload.agentId.trim().length > 0; if (!ownedByAgent) { return false; } return !(isTerminalTaskRow && payload.agentKind === "agent"); } function deriveWorkLogEntries( activities: ReadonlyArray<OrchestrationThreadActivity>, ): DerivedWorkLogEntry[] { const ordered = Arr.sort(activities, activityOrder); const entries: DerivedWorkLogEntry[] = []; for (const activity of ordered) { if (activity.kind === "tool.started") continue; if (activity.kind === "task.started") continue; // Terminal bypassed updates pass: Codex children's only terminal signal. if (activity.kind === "task.updated" && !isTerminalBypassUpdate(activity)) continue; if (activity.kind === "tool.progress") continue; if (activity.kind === "context-window.updated") continue; if (activity.summary === "Checkpoint captured") continue; if (isPlanBoundaryToolActivity(activity)) continue; if (isAgentInternalActivity(activity)) continue; entries.push(toDerivedWorkLogEntry(activity)); } return collapseDerivedWorkLogEntries(entries); } function isPlanBoundaryToolActivity(activity: OrchestrationThreadActivity): boolean { if (activity.kind !== "tool.updated" && activity.kind !== "tool.completed") { return false; } const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; return typeof payload?.detail === "string" && payload.detail.startsWith("ExitPlanMode:"); } function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWorkLogEntry { const payload = activity.payload && typeof activity.payload === "object" ? (activity.payload as Record<string, unknown>) : null; const commandPreview = extractToolCommand(payload); const changedFiles = extractChangedFiles(payload); const title = extractToolTitle(payload); // task.updated included: terminal bypassed updates (Codex children's only // terminal signal) must carry task identity so they collapse per child // instead of stacking anonymous "Task idle" rows. const isTaskActivity = activity.kind === "task.progress" || activity.kind === "task.completed" || activity.kind === "task.updated"; const taskSummary = isTaskActivity && typeof payload?.summary === "string" && payload.summary.length > 0 ? payload.summary : null; const taskDetailAsLabel = isTaskActivity && !taskSummary && typeof payload?.detail === "string" && payload.detail.length > 0 ? payload.detail : null; const taskLabel = taskSummary || taskDetailAsLabel; const taskId = isTaskActivity && typeof payload?.taskId === "string" && payload.taskId.length > 0 ? payload.taskId : undefined; const entry: DerivedWorkLogEntry = { id: activity.id, createdAt: activity.createdAt, turnId: activity.turnId, ...(taskId ? { taskId } : {}), label: taskLabel || activity.summary, tone: activity.kind === "task.progress" ? "thinking" : activity.tone === "approval" ? "info" : activity.tone, activityKind: activity.kind, }; const itemType = extractWorkLogItemType(payload); const requestKind = extractWorkLogRequestKind(payload); if ( !taskDetailAsLabel && payload && typeof payload.detail === "string" && payload.detail.length > 0 ) { const detail = stripTrailingExitCode(payload.detail).output; if (detail) { entry.detail = detail; } } if (commandPreview.command) { entry.command = commandPreview.command; } if (commandPreview.rawCommand) { entry.rawCommand = commandPreview.rawCommand; } if (changedFiles.length > 0) { entry.changedFiles = changedFiles; } if (title) { entry.toolTitle = title; } if (itemType === "mcp_tool_call") { const data = asRecord(payload?.data); if (data?.item !== undefined) { entry.toolData = data.item; } } if (itemType) { entry.itemType = itemType; } if (requestKind) { entry.requestKind = requestKind; } let toolLifecycleStatus = extractWorkLogToolLifecycleStatus(payload); if (!toolLifecycleStatus && activity.kind === "tool.completed") { toolLifecycleStatus = "completed"; } if (toolLifecycleStatus) { entry.toolLifecycleStatus = toolLifecycleStatus; } const collapseKey = deriveToolLifecycleCollapseKey(entry); if (collapseKey) { entry.collapseKey = collapseKey; } return entry; } function collapseDerivedWorkLogEntries( entries: ReadonlyArray<DerivedWorkLogEntry>, ): DerivedWorkLogEntry[] { const collapsed: DerivedWorkLogEntry[] = []; // Subagent rows collapse by identity, not adjacency (quiet-timeline // guarantee; mirrors web's session-logic). const taskRowIndex = new Map<string, number>(); for (const entry of entries) { const isTaskRow = entry.taskId !== undefined && (entry.activityKind === "task.progress" || entry.activityKind === "task.completed" || entry.activityKind === "task.updated"); if (isTaskRow && entry.taskId !== undefined) { const existingIndex = taskRowIndex.get(entry.taskId); if (existingIndex !== undefined) { collapsed[existingIndex] = mergeDerivedWorkLogEntries(collapsed[existingIndex]!, entry); continue; } taskRowIndex.set(entry.taskId, collapsed.length); collapsed.push(entry); continue; } const previous = collapsed.at(-1); if (previous && shouldCollapseToolLifecycleEntries(previous, entry)) { collapsed[collapsed.length - 1] = mergeDerivedWorkLogEntries(previous, entry); continue; } collapsed.push(entry); } return collapsed; } function shouldCollapseToolLifecycleEntries( previous: DerivedWorkLogEntry, next: DerivedWorkLogEntry, ): boolean { if (previous.activityKind !== "tool.updated" && previous.activityKind !== "tool.completed") { return false; } if (next.activityKind !== "tool.updated" && next.activityKind !== "tool.completed") { return false; } if (previous.activityKind === "tool.completed") { return false;