Skip to content

Commit d60ee2b

Browse files
committed
🤖 Add ToolErrorsDisplay story to demonstrate error handling
Shows both error formats in visual storybook: 1. AI SDK errors: { error: '...' } 2. Tool implementation errors: { success: false, error: '...' } Includes examples of: - Nonexistent tool errors - Bash command failures - File read errors - WRITE DENIED errors (collapsed by default) - Policy disabled tool errors All display with 'failed' status and styled error boxes.
1 parent 0955e5a commit d60ee2b

File tree

1 file changed

+191
-0
lines changed

1 file changed

+191
-0
lines changed

src/App.stories.tsx

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,3 +662,194 @@ export const ActiveWorkspaceWithChat: Story = {
662662
return <AppWithChatMocks />;
663663
},
664664
};
665+
666+
/**
667+
* Tool Errors Story - Shows both error formats in GenericToolCall
668+
*/
669+
export const ToolErrorsDisplay: Story = {
670+
render: () => {
671+
const AppWithToolErrors = () => {
672+
const initialized = useRef(false);
673+
674+
if (!initialized.current) {
675+
const workspaceId = "my-app-tool-errors";
676+
677+
// Setup mock API
678+
setupMockAPI({
679+
projects: new Map([
680+
[
681+
"/home/user/projects/my-app",
682+
{
683+
path: "/home/user/projects/my-app",
684+
workspaces: [],
685+
},
686+
],
687+
]),
688+
workspaces: [
689+
{
690+
id: workspaceId,
691+
name: "tool-errors-demo",
692+
projectPath: "/home/user/projects/my-app",
693+
projectName: "my-app",
694+
namedWorkspacePath: "/home/user/.cmux/src/my-app/tool-errors-demo",
695+
},
696+
],
697+
apiOverrides: {
698+
workspace: {
699+
create: () => Promise.resolve({ success: false, error: "Mock" }),
700+
list: () => Promise.resolve([]),
701+
rename: () => Promise.resolve({ success: false, error: "Mock" }),
702+
remove: () => Promise.resolve({ success: false, error: "Mock" }),
703+
fork: () => Promise.resolve({ success: false, error: "Mock" }),
704+
openTerminal: () => Promise.resolve(undefined),
705+
sendMessage: () => Promise.resolve({ success: true, data: undefined }),
706+
resumeStream: () => Promise.resolve({ success: true, data: undefined }),
707+
interruptStream: () => Promise.resolve({ success: true, data: undefined }),
708+
truncateHistory: () => Promise.resolve({ success: true, data: undefined }),
709+
replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }),
710+
getInfo: () => Promise.resolve(null),
711+
executeBash: () =>
712+
Promise.resolve({
713+
success: true,
714+
data: { success: true, output: "", exitCode: 0, wall_duration_ms: 0 },
715+
}),
716+
onMetadata: () => () => undefined,
717+
onChat: (workspaceId, callback) => {
718+
// Simulate chat history with various tool errors
719+
setTimeout(() => {
720+
// User message
721+
callback({
722+
id: "msg-1",
723+
role: "user",
724+
parts: [{ type: "text", text: "Try calling some tools" }],
725+
metadata: {
726+
historySequence: 1,
727+
timestamp: STABLE_TIMESTAMP - 120000,
728+
},
729+
});
730+
731+
// Assistant response with multiple tool errors
732+
callback({
733+
id: "msg-2",
734+
role: "assistant",
735+
parts: [
736+
{
737+
type: "text",
738+
text: "I'll try various tools to demonstrate error handling.",
739+
},
740+
// Tool error format #1: AI SDK error (tool doesn't exist)
741+
{
742+
type: "dynamic-tool",
743+
toolCallId: "call-1",
744+
toolName: "nonexistent_tool",
745+
state: "output-available",
746+
input: {
747+
someParam: "value",
748+
},
749+
output: {
750+
error:
751+
"Tool 'nonexistent_tool' is not available. Available tools: bash, file_read, file_edit_replace_string, file_edit_insert, propose_plan, todo_write, todo_read, web_search",
752+
},
753+
},
754+
// Tool error format #2: Tool implementation error (bash command fails)
755+
{
756+
type: "dynamic-tool",
757+
toolCallId: "call-2",
758+
toolName: "bash",
759+
state: "output-available",
760+
input: {
761+
script: "exit 1",
762+
},
763+
output: {
764+
success: false,
765+
error: "Command exited with code 1",
766+
exitCode: 1,
767+
wall_duration_ms: 42,
768+
},
769+
},
770+
// Tool error format #3: File read error
771+
{
772+
type: "dynamic-tool",
773+
toolCallId: "call-3",
774+
toolName: "file_read",
775+
state: "output-available",
776+
input: {
777+
filePath: "/nonexistent/file.txt",
778+
},
779+
output: {
780+
success: false,
781+
error: "ENOENT: no such file or directory, open '/nonexistent/file.txt'",
782+
},
783+
},
784+
// Tool error format #4: File edit with WRITE DENIED (should be collapsed)
785+
{
786+
type: "dynamic-tool",
787+
toolCallId: "call-4",
788+
toolName: "file_edit_replace_string",
789+
state: "output-available",
790+
input: {
791+
file_path: "src/test.ts",
792+
old_string: "const x = 1;",
793+
new_string: "const x = 2;",
794+
},
795+
output: {
796+
success: false,
797+
error:
798+
"WRITE DENIED, FILE UNMODIFIED: File has been modified since it was read. Please re-read the file and try again.",
799+
},
800+
},
801+
// Tool error format #5: Policy disabled tool
802+
{
803+
type: "dynamic-tool",
804+
toolCallId: "call-5",
805+
toolName: "file_edit_insert",
806+
state: "output-available",
807+
input: {
808+
file_path: "src/new.ts",
809+
line_offset: 0,
810+
content: "console.log('test');",
811+
},
812+
output: {
813+
error:
814+
"Tool execution skipped because the requested tool is disabled by policy.",
815+
},
816+
},
817+
{
818+
type: "text",
819+
text: "As you can see, various tool errors are displayed with clear error messages and 'failed' status.",
820+
},
821+
],
822+
metadata: {
823+
historySequence: 2,
824+
timestamp: STABLE_TIMESTAMP - 110000,
825+
model: "anthropic:claude-sonnet-4-20250514",
826+
},
827+
});
828+
}, 100);
829+
830+
return () => undefined;
831+
},
832+
},
833+
},
834+
});
835+
836+
// Set initial workspace selection
837+
localStorage.setItem(
838+
"selectedWorkspace",
839+
JSON.stringify({
840+
workspaceId: workspaceId,
841+
projectPath: "/home/user/projects/my-app",
842+
projectName: "my-app",
843+
namedWorkspacePath: "/home/user/.cmux/src/my-app/tool-errors-demo",
844+
})
845+
);
846+
847+
initialized.current = true;
848+
}
849+
850+
return <AppLoader />;
851+
};
852+
853+
return <AppWithToolErrors />;
854+
},
855+
};

0 commit comments

Comments
 (0)