)}
- {composioCallCard && status !== "error" && (
+ {composioCallCard && (
{composioCallCard.account && (
)}
+ {composioCallCard.sessionId && (
+
+ )}
+ {composioCallCard.recoverySummary && (
+
+ )}
{composioCallCard.keyArgs.length > 0 && (
0 && (
+
);
case "search":
return (
diff --git a/apps/web/app/components/chat-message.test.tsx b/apps/web/app/components/chat-message.test.tsx
index cfd54ee83a88c..69a41b1d25b69 100644
--- a/apps/web/app/components/chat-message.test.tsx
+++ b/apps/web/app/components/chat-message.test.tsx
@@ -191,4 +191,82 @@ describe("ChatMessage", () => {
expect(button.querySelector('img[src="/integrations/stripe-logomark.svg"]')).toBeNull();
});
+
+ it("renders persisted Dench Integration failures with their error details", async () => {
+ const user = userEvent.setup();
+
+ render(
+ ,
+ );
+
+ await user.click(screen.getByRole("button", { name: /thought/i }));
+
+ expect(
+ screen.getByText(/Validation failed for tool "composio_call_tool"/),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(/posthog \/ POSTHOG_LIST_ALL_PROJECTS_ACROSS_ORGANIZATIONS/),
+ ).toBeInTheDocument();
+ expect(screen.getByText(/"project_id": "proj_123"/)).toBeInTheDocument();
+ });
+
+ it("renders live Dench Integration failures with streamed output errors", async () => {
+ const user = userEvent.setup();
+
+ render(
+ ,
+ );
+
+ await user.click(screen.getByRole("button", { name: /thought/i }));
+
+ expect(
+ screen.getByText(/Gateway rejected the bridge invocation./),
+ ).toBeInTheDocument();
+ expect(
+ screen.getByText(/posthog \/ POSTHOG_LIST_ALL_PROJECTS_ACROSS_ORGANIZATIONS/),
+ ).toBeInTheDocument();
+ });
});
diff --git a/apps/web/app/components/chat-message.tsx b/apps/web/app/components/chat-message.tsx
index beb76e097d8cd..0e7edd075f060 100644
--- a/apps/web/app/components/chat-message.tsx
+++ b/apps/web/app/components/chat-message.tsx
@@ -161,13 +161,19 @@ function groupParts(parts: UIMessage["parts"]): MessageSegment[] {
status: toolStatus(tp.state, tp.preliminary === true),
});
} else {
+ const output = asRecord(tp.output);
+ const status = toolStatus(tp.state, tp.preliminary === true);
chain.push({
kind: "tool",
toolName: tp.toolName,
toolCallId: tp.toolCallId,
- status: toolStatus(tp.state, tp.preliminary === true),
+ status,
args: asRecord(tp.input),
- output: asRecord(tp.output),
+ output,
+ errorText:
+ status === "error" && typeof output?.error === "string"
+ ? output.error
+ : undefined,
});
}
} else if (part.type.startsWith("tool-")) {
@@ -218,6 +224,7 @@ function groupParts(parts: UIMessage["parts"]): MessageSegment[] {
status: toolStatus(resolvedState, tp.preliminary === true),
args: asRecord(tp.input) ?? asRecord(tp.args),
output: asRecord(tp.output) ?? asRecord(tp.result),
+ errorText: tp.errorText,
});
}
}
@@ -765,13 +772,13 @@ function ComposioActionButton({