Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { MCPServerStreamableHttp } from "@openai/agents";
import type { GeneralAgent } from "../agents/general.js";
import type { Logger, LogEvent } from "../classes/logger.js";
import type { Config } from "../classes/config.js";
import chalk from "chalk";
import { renderMarkdown } from "../utils/markdown.js";
import { MessageArea } from "./MessageArea.js";
import { InputBox } from "./InputBox.js";
Expand Down Expand Up @@ -49,6 +50,7 @@ export function App({
const processingRef = useRef(false);
const queueRef = useRef<string[]>([]);
const [queueCount, setQueueCount] = useState(0);
const abortRef = useRef<AbortController | null>(null);
const initialProcessed = useRef(false);
const [focusArea, setFocusArea] = useState<FocusArea>("input");
const [scrollX, setScrollX] = useState(0);
Expand Down Expand Up @@ -105,6 +107,12 @@ export function App({
setFocusArea("input");
}, []);

const handleCancel = useCallback(() => {
if (abortRef.current) {
abortRef.current.abort();
}
}, []);

// Subscribe to logger events
useEffect(() => {
const unsubLog = logger.onLog((event: LogEvent) => {
Expand Down Expand Up @@ -147,6 +155,9 @@ export function App({
setToolCallCount(0);
setStartTime(Date.now());

const abort = new AbortController();
abortRef.current = abort;

try {
const stream = await agent.chat(input, [mcpServer]);
const textStream = stream.toTextStream({
Expand All @@ -155,10 +166,43 @@ export function App({

let accumulated = "";
for await (const chunk of textStream) {
if (abort.signal.aborted) break;
accumulated += chunk;
setStreamingText(accumulated);
}

if (abort.signal.aborted) {
const cancelMsg = chalk.red("Response cancelled.");
if (accumulated) {
setMessages((prev) => [
...prev,
{
role: "assistant",
content: accumulated,
rendered: renderMarkdown(accumulated),
timestamp: logger.getTimestamp(),
},
{
role: "assistant",
content: cancelMsg,
rendered: cancelMsg,
timestamp: logger.getTimestamp(),
},
]);
} else {
setMessages((prev) => [
...prev,
{
role: "assistant",
content: cancelMsg,
rendered: cancelMsg,
timestamp: logger.getTimestamp(),
},
]);
}
return;
}

const finalOutput = await agent.finalizeStream(stream);
const content = finalOutput || accumulated;

Expand All @@ -183,6 +227,7 @@ export function App({
},
]);
} finally {
abortRef.current = null;
setIsStreaming(false);
setStreamingText("");
setStartTime(null);
Expand Down Expand Up @@ -269,10 +314,12 @@ export function App({
contextDir={config.context_dir}
queueCount={queueCount}
focus={focusArea === "input"}
isStreaming={isStreaming}
onToggleFocus={handleToggleFocus}
onScroll={handleScroll}
onScrollVertical={handleScrollVertical}
onReturnToInput={handleReturnToInput}
onCancel={handleCancel}
/>
</Box>
);
Expand Down
9 changes: 9 additions & 0 deletions components/InputBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ interface InputBoxProps {
contextDir: string;
queueCount: number;
focus: boolean;
isStreaming: boolean;
onToggleFocus: () => void;
onScroll: (direction: "left" | "right") => void;
onScrollVertical: (direction: "up" | "down") => void;
onReturnToInput: () => void;
onCancel: () => void;
}

const DELIM = "\0";
Expand All @@ -36,10 +38,12 @@ export function InputBox({
contextDir,
queueCount,
focus,
isStreaming,
onToggleFocus,
onScroll,
onScrollVertical,
onReturnToInput,
onCancel,
}: InputBoxProps) {
const [value, setValue] = useState("");
const historyFile = path.join(contextDir, "chat_history.txt");
Expand All @@ -63,6 +67,11 @@ export function InputBox({
);

useInput((input, key) => {
if (key.escape && isStreaming) {
onCancel();
return;
}

if (key.tab) {
onToggleFocus();
return;
Expand Down
4 changes: 4 additions & 0 deletions components/MessageArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ export function MessageArea({
` 🕝 ${Math.round((Date.now() - startTime) / 1000)}s`}
{toolCallCount > 0 &&
` | 🛠️ ${toolCallCount} tool call${toolCallCount > 1 ? "s" : ""}`}
{" | "}
</Text>
<Text color="yellow" dimColor>
[esc] to cancel
</Text>
</Box>
{streamingText && <Text>{renderMarkdown(streamingText)}</Text>}
Expand Down