authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-23 18:06:49+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-23 18:06:49+02:00
log315b27385d7eb36a56b410fa8b624fc2ed246278
tree5621d9b87ba171649b450da2accc3c9fcc569792
parent719c905eacdcb04e11dd4ce1e515a44455ed0874
signature Signed by PGP key B5690EEEBB952194

[codex] Deduplicate connection failure messaging (#4367)

Co-authored-by: codex <codex@users.noreply.github.com>

4 files changed, 25 insertions(+), 22 deletions(-)

apps/web/src/components/ChatView.tsx+2-2
......@@ -22,7 +22,7 @@ import {
2222 TerminalOpenInput,
2323} from "@t3tools/contracts";
2424import {
25 connectionStatusText,
25 connectionStatusTitle,
2626 type EnvironmentConnectionPresentation,
2727} from "@t3tools/client-runtime/connection";
2828import {
......@@ -1815,7 +1815,7 @@ function ChatViewContent(props: ChatViewProps) {
18151815 id: `environment-unavailable:${activeEnvironmentUnavailableState.environmentId}`,
18161816 variant: connection.phase === "error" ? "error" : "warning",
18171817 icon: <WifiOffIcon />,
1818 title: `${activeEnvironmentUnavailableState.label}: ${connectionStatusText(connection)}`,
1818 title: `${activeEnvironmentUnavailableState.label}: ${connectionStatusTitle(connection)}`,
18191819 description:
18201820 connection.error ??
18211821 "Reconnect this environment before sending messages or running actions.",
apps/web/src/components/chat/ChatComposer.tsx+6-13
......@@ -18,10 +18,7 @@ import {
1818 PROVIDER_SEND_TURN_MAX_ATTACHMENTS,
1919 PROVIDER_SEND_TURN_MAX_IMAGE_BYTES,
2020} from "@t3tools/contracts";
21import {
22 connectionStatusText,
23 type EnvironmentConnectionPresentation,
24} from "@t3tools/client-runtime/connection";
21import type { EnvironmentConnectionPresentation } from "@t3tools/client-runtime/connection";
2522import { serializeComposerFileLink } from "@t3tools/shared/composerTrigger";
2623import { createModelSelection, normalizeModelSlug } from "@t3tools/shared/model";
2724import {
......@@ -2577,15 +2574,11 @@ export const ChatComposer = memo(function ChatComposer(props: ChatComposerProps)
25772574 ? "Add feedback to refine the plan, or leave this blank to implement it"
25782575 : projectSelectionRequired
25792576 ? "Choose a project above to start a thread"
2580 : environmentUnavailable
2581 ? `${environmentUnavailable.label}: ${connectionStatusText(
2582 environmentUnavailable.connection,
2583 )}`
2584 : noProviderAvailable
2585 ? "Enable a provider in Settings to send a message"
2586 : phase === "disconnected"
2587 ? "Ask for follow-up changes or attach images"
2588 : "Ask anything, @tag files/folders, $use skills, or / for commands"
2577 : noProviderAvailable
2578 ? "Enable a provider in Settings to send a message"
2579 : phase === "disconnected"
2580 ? "Ask for follow-up changes or attach images"
2581 : "Ask anything, @tag files/folders, $use skills, or / for commands"
25892582 }
25902583 disabled={isConnecting || isComposerApprovalState || projectSelectionRequired}
25912584 />
packages/client-runtime/src/connection/presentation.test.ts+10-7
......@@ -12,6 +12,7 @@ import {
1212 connectionCatalogDisplayUrl,
1313 connectionPhaseMessage,
1414 connectionStatusText,
15 connectionStatusTitle,
1516 presentEnvironmentConnection,
1617 presentConnectionState,
1718} from "./presentation.ts";
......@@ -123,13 +124,15 @@ describe("connection presentation", () => {
123124 });
124125
125126 it("combines reconnect progress with the latest failure", () => {
126 expect(
127 connectionStatusText({
128 phase: "reconnecting",
129 error: "Relay request timed out.",
130 traceId: "trace-retry",
131 }),
132 ).toBe("Failed to connect. Reconnecting... Reason: Relay request timed out.");
127 const connection = {
128 phase: "reconnecting",
129 error: "Relay request timed out.",
130 traceId: "trace-retry",
131 } as const;
132 expect(connectionStatusText(connection)).toBe(
133 "Failed to connect. Reconnecting... Reason: Relay request timed out.",
134 );
135 expect(connectionStatusTitle(connection)).toBe("Failed to connect. Reconnecting...");
133136 });
134137
135138 it("presents the supervisor's offline state without consulting shell state", () => {
packages/client-runtime/src/connection/presentation.ts+7
......@@ -76,6 +76,13 @@ export function connectionStatusText(connection: EnvironmentConnectionPresentati
7676 }
7777}
7878
79export function connectionStatusTitle(connection: EnvironmentConnectionPresentation): string {
80 if (connection.phase === "reconnecting" && connection.error) {
81 return "Failed to connect. Reconnecting...";
82 }
83 return connectionStatusText({ ...connection, error: null });
84}
85
7986export function presentEnvironmentConnection(
8087 state: SupervisorConnectionState,
8188): EnvironmentConnectionPresentation {