Skip to content

Commit c806b9e

Browse files
committed
🤖 Add tool error examples to ActiveWorkspaceWithChat story
Instead of creating separate story, add tool error examples to the existing chat story for more realistic context. Shows: - File read errors (success: false format) - WRITE DENIED errors (collapsed by default) - Unknown tool errors (AI SDK error format)
1 parent d60ee2b commit c806b9e

File tree

1 file changed

+46
-155
lines changed

1 file changed

+46
-155
lines changed

src/App.stories.tsx

Lines changed: 46 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -618,159 +618,35 @@ export const ActiveWorkspaceWithChat: Story = {
618618
},
619619
});
620620

621-
// Mark as caught up
622-
callback({ type: "caught-up" });
623-
}, 100);
624-
625-
return () => {
626-
// Cleanup
627-
};
628-
},
629-
onMetadata: () => () => undefined,
630-
sendMessage: () => Promise.resolve({ success: true, data: undefined }),
631-
resumeStream: () => Promise.resolve({ success: true, data: undefined }),
632-
interruptStream: () => Promise.resolve({ success: true, data: undefined }),
633-
truncateHistory: () => Promise.resolve({ success: true, data: undefined }),
634-
replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }),
635-
getInfo: () => Promise.resolve(null),
636-
executeBash: () =>
637-
Promise.resolve({
638-
success: true,
639-
data: { success: true, output: "", exitCode: 0, wall_duration_ms: 0 },
640-
}),
641-
},
642-
},
643-
});
644-
645-
// Set initial workspace selection
646-
localStorage.setItem(
647-
"selectedWorkspace",
648-
JSON.stringify({
649-
workspaceId: workspaceId,
650-
projectPath: "/home/user/projects/my-app",
651-
projectName: "my-app",
652-
namedWorkspacePath: "/home/user/.cmux/src/my-app/feature",
653-
})
654-
);
655-
656-
initialized.current = true;
657-
}
658-
659-
return <AppLoader />;
660-
};
661-
662-
return <AppWithChatMocks />;
663-
},
664-
};
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
621+
// User asking to test error handling
721622
callback({
722-
id: "msg-1",
623+
id: "msg-10",
723624
role: "user",
724-
parts: [{ type: "text", text: "Try calling some tools" }],
625+
parts: [
626+
{
627+
type: "text",
628+
text: "Can you show me some examples of error handling?",
629+
},
630+
],
725631
metadata: {
726-
historySequence: 1,
727-
timestamp: STABLE_TIMESTAMP - 120000,
632+
historySequence: 10,
633+
timestamp: STABLE_TIMESTAMP - 160000,
728634
},
729635
});
730636

731-
// Assistant response with multiple tool errors
637+
// Assistant response with various tool errors
732638
callback({
733-
id: "msg-2",
639+
id: "msg-11",
734640
role: "assistant",
735641
parts: [
736642
{
737643
type: "text",
738-
text: "I'll try various tools to demonstrate error handling.",
644+
text: "I'll demonstrate various error scenarios:",
739645
},
740-
// Tool error format #1: AI SDK error (tool doesn't exist)
646+
// Tool error format #1: File read error
741647
{
742648
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",
649+
toolCallId: "call-5",
774650
toolName: "file_read",
775651
state: "output-available",
776652
input: {
@@ -781,10 +657,10 @@ export const ToolErrorsDisplay: Story = {
781657
error: "ENOENT: no such file or directory, open '/nonexistent/file.txt'",
782658
},
783659
},
784-
// Tool error format #4: File edit with WRITE DENIED (should be collapsed)
660+
// Tool error format #2: WRITE DENIED (should be collapsed)
785661
{
786662
type: "dynamic-tool",
787-
toolCallId: "call-4",
663+
toolCallId: "call-6",
788664
toolName: "file_edit_replace_string",
789665
state: "output-available",
790666
input: {
@@ -798,37 +674,52 @@ export const ToolErrorsDisplay: Story = {
798674
"WRITE DENIED, FILE UNMODIFIED: File has been modified since it was read. Please re-read the file and try again.",
799675
},
800676
},
801-
// Tool error format #5: Policy disabled tool
677+
// Tool error format #3: AI SDK error (policy disabled)
802678
{
803679
type: "dynamic-tool",
804-
toolCallId: "call-5",
805-
toolName: "file_edit_insert",
680+
toolCallId: "call-7",
681+
toolName: "unknown_tool",
806682
state: "output-available",
807683
input: {
808-
file_path: "src/new.ts",
809-
line_offset: 0,
810-
content: "console.log('test');",
684+
someParam: "value",
811685
},
812686
output: {
813687
error:
814-
"Tool execution skipped because the requested tool is disabled by policy.",
688+
"Tool 'unknown_tool' is not available. Available tools: bash, file_read, file_edit_replace_string",
815689
},
816690
},
817691
{
818692
type: "text",
819-
text: "As you can see, various tool errors are displayed with clear error messages and 'failed' status.",
693+
text: "As you can see, different error types are displayed with clear indicators and appropriate styling.",
820694
},
821695
],
822696
metadata: {
823-
historySequence: 2,
824-
timestamp: STABLE_TIMESTAMP - 110000,
825-
model: "anthropic:claude-sonnet-4-20250514",
697+
historySequence: 11,
698+
timestamp: STABLE_TIMESTAMP - 150000,
699+
model: "claude-sonnet-4-20250514",
826700
},
827701
});
702+
703+
// Mark as caught up
704+
callback({ type: "caught-up" });
828705
}, 100);
829706

830-
return () => undefined;
707+
return () => {
708+
// Cleanup
709+
};
831710
},
711+
onMetadata: () => () => undefined,
712+
sendMessage: () => Promise.resolve({ success: true, data: undefined }),
713+
resumeStream: () => Promise.resolve({ success: true, data: undefined }),
714+
interruptStream: () => Promise.resolve({ success: true, data: undefined }),
715+
truncateHistory: () => Promise.resolve({ success: true, data: undefined }),
716+
replaceChatHistory: () => Promise.resolve({ success: true, data: undefined }),
717+
getInfo: () => Promise.resolve(null),
718+
executeBash: () =>
719+
Promise.resolve({
720+
success: true,
721+
data: { success: true, output: "", exitCode: 0, wall_duration_ms: 0 },
722+
}),
832723
},
833724
},
834725
});
@@ -840,7 +731,7 @@ export const ToolErrorsDisplay: Story = {
840731
workspaceId: workspaceId,
841732
projectPath: "/home/user/projects/my-app",
842733
projectName: "my-app",
843-
namedWorkspacePath: "/home/user/.cmux/src/my-app/tool-errors-demo",
734+
namedWorkspacePath: "/home/user/.cmux/src/my-app/feature",
844735
})
845736
);
846737

@@ -850,6 +741,6 @@ export const ToolErrorsDisplay: Story = {
850741
return <AppLoader />;
851742
};
852743

853-
return <AppWithToolErrors />;
744+
return <AppWithChatMocks />;
854745
},
855746
};

0 commit comments

Comments
 (0)