-
-
-
-
- {rawCommand ? (
-
-
+
+
+
+
+ {runtimeWarningMessage ? (
+
+ {runtimeWarningMessage}
+
+ ) : null}
+ {runtimeWarningDetail ? (
+
+ {runtimeWarningDetail}
+
+ ) : null}
- ) : (
-
-
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {rawCommand ? (
+
{heading}
- {preview && - {preview}}
-
-
-
-
- {displayText}
+ {preview && (
+
+
+ {" "}
+ - {preview}
+
+ }
+ />
+
+
+ {rawCommand}
+
+
+
+ )}
-
-
- )}
+
+ ) : (
+
+
+
+
+ {heading}
+
+ {preview && - {preview}}
+
+
+
+
+ {displayText}
+
+
+
+ )}
+
-
- {hasChangedFiles && !previewIsChangedFiles && (
-
- {workEntry.changedFiles?.slice(0, 4).map((filePath) => {
- const displayPath = formatWorkspaceRelativePath(filePath, workspaceRoot);
- return (
-
- {displayPath}
+ {hasChangedFiles && !previewIsChangedFiles && (
+
+ {workEntry.changedFiles?.slice(0, 4).map((filePath) => {
+ const displayPath = formatWorkspaceRelativePath(filePath, workspaceRoot);
+ return (
+
+ {displayPath}
+
+ );
+ })}
+ {(workEntry.changedFiles?.length ?? 0) > 4 && (
+
+ +{(workEntry.changedFiles?.length ?? 0) - 4}
- );
- })}
- {(workEntry.changedFiles?.length ?? 0) > 4 && (
-
- +{(workEntry.changedFiles?.length ?? 0) - 4}
-
- )}
-
- )}
-
+ )}
+
+ )}
+
+
);
});
diff --git a/apps/web/src/session-logic.test.ts b/apps/web/src/session-logic.test.ts
index baf384d6af2..02dbd32b2d2 100644
--- a/apps/web/src/session-logic.test.ts
+++ b/apps/web/src/session-logic.test.ts
@@ -653,6 +653,29 @@ describe("deriveWorkLogEntries", () => {
expect(entries[0]?.tone).toBe("error");
});
+ it("keeps runtime warning rows generic while preserving message and structured details", () => {
+ const detail = { code: "slow_provider", retryInSeconds: 5 };
+ const activities: OrchestrationThreadActivity[] = [
+ makeActivity({
+ id: "runtime-warning",
+ createdAt: "2026-02-23T00:00:03.000Z",
+ kind: "runtime.warning",
+ summary: "Runtime Warning",
+ tone: "info",
+ payload: {
+ message: "Provider got slow",
+ detail,
+ },
+ }),
+ ];
+
+ const [entry] = deriveWorkLogEntries(activities, undefined);
+ expect(entry?.label).toBe("Runtime Warning");
+ expect(entry?.detail).toBeUndefined();
+ expect(entry?.runtimeWarningMessage).toBe("Provider got slow");
+ expect(entry?.runtimeWarningDetail).toEqual(detail);
+ });
+
it("filters by turn id when provided", () => {
const activities: OrchestrationThreadActivity[] = [
makeActivity({ id: "turn-1", turnId: "turn-1", summary: "Tool call", kind: "tool.started" }),
diff --git a/apps/web/src/session-logic.ts b/apps/web/src/session-logic.ts
index a7767672fa1..15d7ded33db 100644
--- a/apps/web/src/session-logic.ts
+++ b/apps/web/src/session-logic.ts
@@ -52,6 +52,8 @@ export interface WorkLogEntry {
createdAt: string;
label: string;
detail?: string;
+ runtimeWarningMessage?: string;
+ runtimeWarningDetail?: unknown;
command?: string;
rawCommand?: string;
changedFiles?: ReadonlyArray
;
@@ -530,6 +532,12 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
payload.detail.length > 0
? payload.detail
: null;
+ const runtimeWarningMessage =
+ activity.kind === "runtime.warning" &&
+ typeof payload?.message === "string" &&
+ payload.message.length > 0
+ ? payload.message
+ : null;
const taskLabel = taskSummary || taskDetailAsLabel;
const detail = isTaskActivity
? !taskDetailAsLabel &&
@@ -538,12 +546,14 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
payload.detail.length > 0
? stripTrailingExitCode(payload.detail).output
: null
- : extractToolDetail(payload, title ?? activity.summary);
+ : activity.kind === "runtime.warning"
+ ? null
+ : extractToolDetail(payload, title ?? activity.summary);
const toolCallId = isTaskActivity ? null : extractToolCallId(payload);
const entry: DerivedWorkLogEntry = {
id: activity.id,
createdAt: activity.createdAt,
- label: taskLabel || activity.summary,
+ label: activity.kind === "runtime.warning" ? activity.summary : taskLabel || activity.summary,
tone:
activity.kind === "task.progress"
? "thinking"
@@ -557,6 +567,12 @@ function toDerivedWorkLogEntry(activity: OrchestrationThreadActivity): DerivedWo
if (detail) {
entry.detail = detail;
}
+ if (runtimeWarningMessage) {
+ entry.runtimeWarningMessage = runtimeWarningMessage;
+ }
+ if (activity.kind === "runtime.warning" && payload?.detail !== undefined) {
+ entry.runtimeWarningDetail = payload.detail;
+ }
if (commandPreview.command) {
entry.command = commandPreview.command;
}
From 50fe1e2f78ca50322020c0accf7e10aee9afa2b0 Mon Sep 17 00:00:00 2001
From: justsomelegs <145564979+justsomelegs@users.noreply.github.com>
Date: Wed, 13 May 2026 14:58:06 +0100
Subject: [PATCH 2/2] Align runtime warning preview checks
---
.../components/chat/MessagesTimeline.test.tsx | 26 ++++++++
.../src/components/chat/MessagesTimeline.tsx | 59 +++++++++++--------
2 files changed, 61 insertions(+), 24 deletions(-)
diff --git a/apps/web/src/components/chat/MessagesTimeline.test.tsx b/apps/web/src/components/chat/MessagesTimeline.test.tsx
index daef05631a3..1b3d6f437a5 100644
--- a/apps/web/src/components/chat/MessagesTimeline.test.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.test.tsx
@@ -245,6 +245,32 @@ describe("MessagesTimeline", () => {
expect(markup).not.toContain(">Warning<");
});
+ it("renders whitespace-only runtime warning messages as a plain runtime warning row", async () => {
+ const { MessagesTimeline } = await import("./MessagesTimeline");
+ const markup = renderToStaticMarkup(
+ ,
+ );
+
+ expect(markup).toContain("Runtime Warning");
+ expect(markup).not.toContain('aria-label="Show runtime warning details"');
+ });
+
it("formats changed file paths from the workspace root", async () => {
const { MessagesTimeline } = await import("./MessagesTimeline");
const markup = renderToStaticMarkup(
diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx
index 86e7df6f09c..077fa1cfd84 100644
--- a/apps/web/src/components/chat/MessagesTimeline.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.tsx
@@ -1036,6 +1036,38 @@ function workToneClass(tone: "thinking" | "tool" | "info" | "error"): string {
return "text-muted-foreground/40";
}
+function normalizeRuntimeWarningMessage(message: string | undefined): string | null {
+ const trimmed = message?.trim();
+ return trimmed && trimmed.length > 0 ? trimmed : null;
+}
+
+function formatRuntimeWarningDetail(detail: unknown): string | null {
+ if (typeof detail === "string") {
+ const trimmed = detail.trim();
+ return trimmed.length > 0 ? trimmed : null;
+ }
+
+ if (detail === null || detail === undefined) {
+ return null;
+ }
+
+ try {
+ const serialized = JSON.stringify(detail, null, 2);
+ return typeof serialized === "string" && serialized.length > 0 ? serialized : String(detail);
+ } catch {
+ return String(detail);
+ }
+}
+
+function hasRenderableRuntimeWarningMeta(
+ workEntry: Pick,
+): boolean {
+ return (
+ normalizeRuntimeWarningMessage(workEntry.runtimeWarningMessage) !== null ||
+ formatRuntimeWarningDetail(workEntry.runtimeWarningDetail) !== null
+ );
+}
+
function workEntryPreview(
workEntry: Pick<
TimelineWorkEntry,
@@ -1043,10 +1075,7 @@ function workEntryPreview(
>,
workspaceRoot: string | undefined,
) {
- if (
- workEntry.runtimeWarningMessage !== undefined ||
- workEntry.runtimeWarningDetail !== undefined
- ) {
+ if (hasRenderableRuntimeWarningMeta(workEntry)) {
return null;
}
if (workEntry.command) return workEntry.command;
@@ -1110,24 +1139,6 @@ function toolWorkEntryHeading(workEntry: TimelineWorkEntry): string {
return capitalizePhrase(normalizeCompactToolLabel(workEntry.toolTitle));
}
-function formatRuntimeWarningDetail(detail: unknown): string | null {
- if (typeof detail === "string") {
- const trimmed = detail.trim();
- return trimmed.length > 0 ? trimmed : null;
- }
-
- if (detail === null || detail === undefined) {
- return null;
- }
-
- try {
- const serialized = JSON.stringify(detail, null, 2);
- return typeof serialized === "string" && serialized.length > 0 ? serialized : String(detail);
- } catch {
- return String(detail);
- }
-}
-
const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: {
workEntry: TimelineWorkEntry;
workspaceRoot: string | undefined;
@@ -1148,9 +1159,9 @@ const SimpleWorkEntryRow = memo(function SimpleWorkEntryRow(props: {
const displayText = preview ? `${heading} - ${preview}` : heading;
const hasChangedFiles = (workEntry.changedFiles?.length ?? 0) > 0;
const previewIsChangedFiles = hasChangedFiles && !workEntry.command && !workEntry.detail;
- const runtimeWarningMessage = workEntry.runtimeWarningMessage?.trim() || null;
+ const runtimeWarningMessage = normalizeRuntimeWarningMessage(workEntry.runtimeWarningMessage);
const runtimeWarningDetail = formatRuntimeWarningDetail(workEntry.runtimeWarningDetail);
- const hasRuntimeWarningMeta = runtimeWarningMessage !== null || runtimeWarningDetail !== null;
+ const hasRuntimeWarningMeta = hasRenderableRuntimeWarningMeta(workEntry);
if (hasRuntimeWarningMeta) {
return (