revert prettier churn on four files the cliamp move never touched
I ran `bunx prettier --write` over whole directories instead of the files I edited, so FileViewContainer, SelectionActions, usePipelineRunner and Providers got rewrapped into a diff about cliamp. Pure whitespace, zero behaviour, and exactly what CLAUDE.md warns about — unexplained churn in someone else's file. Worth noting what it revealed rather than just undoing it: those four were not prettier-clean to begin with, so `bun format` on a clean tree would rewrite them too. That is a pre-existing inconsistency, not mine to fix in this commit. bunx tsgo clean.
This commit is contained in:
+4
-14
@@ -1,17 +1,5 @@
|
|||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
import {
|
import { Loader2, Folder, ClipboardPaste, FolderPlus, FolderUp, LayoutGrid, Upload, ClipboardCopy, MessageSquare, Download, Mic } from 'lucide-react';
|
||||||
Loader2,
|
|
||||||
Folder,
|
|
||||||
ClipboardPaste,
|
|
||||||
FolderPlus,
|
|
||||||
FolderUp,
|
|
||||||
LayoutGrid,
|
|
||||||
Upload,
|
|
||||||
ClipboardCopy,
|
|
||||||
MessageSquare,
|
|
||||||
Download,
|
|
||||||
Mic,
|
|
||||||
} from 'lucide-react';
|
|
||||||
import { getIcon } from 'material-file-icons';
|
import { getIcon } from 'material-file-icons';
|
||||||
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@/components/ui/context-menu';
|
import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuTrigger } from '@/components/ui/context-menu';
|
||||||
import type { UseFileBrowserAppType } from '../useFileBrowserApp';
|
import type { UseFileBrowserAppType } from '../useFileBrowserApp';
|
||||||
@@ -104,7 +92,9 @@ export const FileViewContainer = ({ fileBrowserManager }: FileViewContainerProps
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
) : searchResults ? (
|
) : searchResults ? (
|
||||||
<div className="flex items-center justify-center py-20 text-duck-dark/40 text-sm">No results found</div>
|
<div className="flex items-center justify-center py-20 text-duck-dark/40 text-sm">
|
||||||
|
No results found
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
+2
-10
@@ -6,16 +6,8 @@ type SelectionActionsProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const SelectionActions = ({ fileBrowserManager }: SelectionActionsProps) => {
|
export const SelectionActions = ({ fileBrowserManager }: SelectionActionsProps) => {
|
||||||
const {
|
const { selected, clipboard, handleCut, handleCopy, handlePaste, handleDownloadSelected, handleDeleteSelected, setSelected } =
|
||||||
selected,
|
fileBrowserManager;
|
||||||
clipboard,
|
|
||||||
handleCut,
|
|
||||||
handleCopy,
|
|
||||||
handlePaste,
|
|
||||||
handleDownloadSelected,
|
|
||||||
handleDeleteSelected,
|
|
||||||
setSelected,
|
|
||||||
} = fileBrowserManager;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
+197
-271
@@ -33,61 +33,19 @@ type ParallelStep = {
|
|||||||
|
|
||||||
type ServerMessage =
|
type ServerMessage =
|
||||||
| { jobId: string; type: 'pipeline:init'; steps: StepDef[] }
|
| { jobId: string; type: 'pipeline:init'; steps: StepDef[] }
|
||||||
| {
|
| { jobId: string; type: 'step:start'; stepIndex: number; taskName: string; iteration?: { current: number; total: number; label: string } }
|
||||||
jobId: string;
|
| { jobId: string; type: 'step:complete'; stepIndex: number; cost?: { inputTokens: number; outputTokens: number; totalUSD: number } }
|
||||||
type: 'step:start';
|
|
||||||
stepIndex: number;
|
|
||||||
taskName: string;
|
|
||||||
iteration?: { current: number; total: number; label: string };
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
jobId: string;
|
|
||||||
type: 'step:complete';
|
|
||||||
stepIndex: number;
|
|
||||||
cost?: { inputTokens: number; outputTokens: number; totalUSD: number };
|
|
||||||
}
|
|
||||||
| { jobId: string; type: 'step:skip'; stepIndex: number; label: string; reason: string }
|
| { jobId: string; type: 'step:skip'; stepIndex: number; label: string; reason: string }
|
||||||
| {
|
| { jobId: string; type: 'step:parallel'; stepIndex: number; taskName: string; iterations: string[]; concurrency: number }
|
||||||
jobId: string;
|
|
||||||
type: 'step:parallel';
|
|
||||||
stepIndex: number;
|
|
||||||
taskName: string;
|
|
||||||
iterations: string[];
|
|
||||||
concurrency: number;
|
|
||||||
}
|
|
||||||
| { jobId: string; type: 'step:waiting'; stepIndex: number; iterationLabel?: string; elapsed: number }
|
| { jobId: string; type: 'step:waiting'; stepIndex: number; iterationLabel?: string; elapsed: number }
|
||||||
| { jobId: string; type: 'iteration:start'; stepIndex: number; label: string }
|
| { jobId: string; type: 'iteration:start'; stepIndex: number; label: string }
|
||||||
| {
|
| { jobId: string; type: 'iteration:complete'; stepIndex: number; label: string; cost?: { inputTokens: number; outputTokens: number; totalUSD: number } }
|
||||||
jobId: string;
|
|
||||||
type: 'iteration:complete';
|
|
||||||
stepIndex: number;
|
|
||||||
label: string;
|
|
||||||
cost?: { inputTokens: number; outputTokens: number; totalUSD: number };
|
|
||||||
}
|
|
||||||
| { jobId: string; type: 'iteration:error'; stepIndex: number; label: string; error: string }
|
| { jobId: string; type: 'iteration:error'; stepIndex: number; label: string; error: string }
|
||||||
| { jobId: string; type: 'assistant:delta'; text: string; iterationLabel?: string }
|
| { jobId: string; type: 'assistant:delta'; text: string; iterationLabel?: string }
|
||||||
| { jobId: string; type: 'assistant:text'; text: string; iterationLabel?: string }
|
| { jobId: string; type: 'assistant:text'; text: string; iterationLabel?: string }
|
||||||
| {
|
| { jobId: string; type: 'tool:start'; toolCallId: string; toolName: string; toolInput: Record<string, unknown>; iterationLabel?: string }
|
||||||
jobId: string;
|
| { jobId: string; type: 'tool:result'; toolCallId: string; output: string; isError: boolean; iterationLabel?: string }
|
||||||
type: 'tool:start';
|
| { jobId: string; type: 'pipeline:complete'; totalCost: { inputTokens: number; outputTokens: number; totalUSD: number } }
|
||||||
toolCallId: string;
|
|
||||||
toolName: string;
|
|
||||||
toolInput: Record<string, unknown>;
|
|
||||||
iterationLabel?: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
jobId: string;
|
|
||||||
type: 'tool:result';
|
|
||||||
toolCallId: string;
|
|
||||||
output: string;
|
|
||||||
isError: boolean;
|
|
||||||
iterationLabel?: string;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
jobId: string;
|
|
||||||
type: 'pipeline:complete';
|
|
||||||
totalCost: { inputTokens: number; outputTokens: number; totalUSD: number };
|
|
||||||
}
|
|
||||||
| { jobId: string; type: 'error'; message: string }
|
| { jobId: string; type: 'error'; message: string }
|
||||||
| { jobId: string; type: 'stopped' }
|
| { jobId: string; type: 'stopped' }
|
||||||
| { type: 'job:created'; jobId: string }
|
| { type: 'job:created'; jobId: string }
|
||||||
@@ -103,18 +61,12 @@ export function usePipelineRunner() {
|
|||||||
const [parallelStep, setParallelStep] = useState<ParallelStep | null>(null);
|
const [parallelStep, setParallelStep] = useState<ParallelStep | null>(null);
|
||||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||||
const [streamingText, setStreamingText] = useState('');
|
const [streamingText, setStreamingText] = useState('');
|
||||||
const [totalCost, setTotalCost] = useState<{ inputTokens: number; outputTokens: number; totalUSD: number } | null>(
|
const [totalCost, setTotalCost] = useState<{ inputTokens: number; outputTokens: number; totalUSD: number } | null>(null);
|
||||||
null,
|
|
||||||
);
|
|
||||||
const [runningCost, setRunningCost] = useState({ inputTokens: 0, outputTokens: 0, totalUSD: 0 });
|
const [runningCost, setRunningCost] = useState({ inputTokens: 0, outputTokens: 0, totalUSD: 0 });
|
||||||
const [hasError, setHasError] = useState(false);
|
const [hasError, setHasError] = useState(false);
|
||||||
const [skippedItems, setSkippedItems] = useState<Array<{ label: string; reason: string }>>([]);
|
const [skippedItems, setSkippedItems] = useState<Array<{ label: string; reason: string }>>([]);
|
||||||
const [elapsed, setElapsed] = useState(0);
|
const [elapsed, setElapsed] = useState(0);
|
||||||
const [waitingStatus, setWaitingStatus] = useState<{
|
const [waitingStatus, setWaitingStatus] = useState<{ stepIndex: number; elapsed: number; iterationLabel?: string } | null>(null);
|
||||||
stepIndex: number;
|
|
||||||
elapsed: number;
|
|
||||||
iterationLabel?: string;
|
|
||||||
} | null>(null);
|
|
||||||
const wsRef = useRef<WebSocket | null>(null);
|
const wsRef = useRef<WebSocket | null>(null);
|
||||||
const streamBufferRef = useRef('');
|
const streamBufferRef = useRef('');
|
||||||
const startTimeRef = useRef<number>(0);
|
const startTimeRef = useRef<number>(0);
|
||||||
@@ -132,10 +84,7 @@ export function usePipelineRunner() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const stopTimer = useCallback(() => {
|
const stopTimer = useCallback(() => {
|
||||||
if (timerRef.current) {
|
if (timerRef.current) { clearInterval(timerRef.current); timerRef.current = null; }
|
||||||
clearInterval(timerRef.current);
|
|
||||||
timerRef.current = null;
|
|
||||||
}
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const addCost = useCallback((cost: { inputTokens: number; outputTokens: number; totalUSD: number }) => {
|
const addCost = useCallback((cost: { inputTokens: number; outputTokens: number; totalUSD: number }) => {
|
||||||
@@ -146,184 +95,178 @@ export function usePipelineRunner() {
|
|||||||
}));
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleEvent = useCallback(
|
const handleEvent = useCallback((msg: ServerMessage) => {
|
||||||
(msg: ServerMessage) => {
|
// Filter events by jobId (ignore events from other jobs)
|
||||||
// Filter events by jobId (ignore events from other jobs)
|
if ('jobId' in msg && msg.jobId && jobIdRef.current && msg.jobId !== jobIdRef.current) return;
|
||||||
if ('jobId' in msg && msg.jobId && jobIdRef.current && msg.jobId !== jobIdRef.current) return;
|
|
||||||
|
|
||||||
switch (msg.type) {
|
switch (msg.type) {
|
||||||
case 'job:created':
|
case 'job:created':
|
||||||
jobIdRef.current = msg.jobId;
|
jobIdRef.current = msg.jobId;
|
||||||
setJobId(msg.jobId);
|
setJobId(msg.jobId);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'job:state':
|
case 'job:state':
|
||||||
// Reconnection to a completed/failed job
|
// Reconnection to a completed/failed job
|
||||||
if (
|
if (msg.status === 'completed' || msg.status === 'failed' || msg.status === 'stopped' || msg.status === 'interrupted') {
|
||||||
msg.status === 'completed' ||
|
setPhase('done');
|
||||||
msg.status === 'failed' ||
|
if (msg.cost) setTotalCost(msg.cost as { inputTokens: number; outputTokens: number; totalUSD: number });
|
||||||
msg.status === 'stopped' ||
|
if (msg.status === 'failed' || msg.status === 'interrupted') setHasError(true);
|
||||||
msg.status === 'interrupted'
|
stopTimer();
|
||||||
) {
|
|
||||||
setPhase('done');
|
|
||||||
if (msg.cost) setTotalCost(msg.cost as { inputTokens: number; outputTokens: number; totalUSD: number });
|
|
||||||
if (msg.status === 'failed' || msg.status === 'interrupted') setHasError(true);
|
|
||||||
stopTimer();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'pipeline:init':
|
|
||||||
setSteps(msg.steps);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'step:start':
|
|
||||||
flushStream();
|
|
||||||
setMessages([]);
|
|
||||||
setParallelStep(null);
|
|
||||||
setWaitingStatus(null);
|
|
||||||
inParallelRef.current = false;
|
|
||||||
setCurrentStep({
|
|
||||||
taskName: msg.taskName,
|
|
||||||
iteration: msg.iteration,
|
|
||||||
status: 'running',
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'step:complete':
|
|
||||||
flushStream();
|
|
||||||
setWaitingStatus(null);
|
|
||||||
setCurrentStep((prev) => (prev ? { ...prev, status: 'complete', cost: msg.cost } : null));
|
|
||||||
if (msg.cost) addCost(msg.cost);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'step:skip':
|
|
||||||
setSkippedItems((prev) => [...prev, { label: msg.label, reason: msg.reason }]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'step:waiting':
|
|
||||||
setWaitingStatus({ stepIndex: msg.stepIndex, elapsed: msg.elapsed, iterationLabel: msg.iterationLabel });
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'step:parallel':
|
|
||||||
flushStream();
|
|
||||||
setMessages([]);
|
|
||||||
setCurrentStep(null);
|
|
||||||
inParallelRef.current = true;
|
|
||||||
setParallelStep({
|
|
||||||
stepIndex: msg.stepIndex,
|
|
||||||
taskName: msg.taskName,
|
|
||||||
concurrency: msg.concurrency,
|
|
||||||
iterations: msg.iterations.map((label) => ({ label, status: 'pending' })),
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'iteration:start':
|
|
||||||
setParallelStep((prev) => {
|
|
||||||
if (!prev) return prev;
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
iterations: prev.iterations.map((it) => (it.label === msg.label ? { ...it, status: 'running' } : it)),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'iteration:complete':
|
|
||||||
setParallelStep((prev) => {
|
|
||||||
if (!prev) return prev;
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
iterations: prev.iterations.map((it) =>
|
|
||||||
it.label === msg.label ? { ...it, status: 'complete', cost: msg.cost } : it,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
if (msg.cost) addCost(msg.cost);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'iteration:error':
|
|
||||||
setParallelStep((prev) => {
|
|
||||||
if (!prev) return prev;
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
iterations: prev.iterations.map((it) =>
|
|
||||||
it.label === msg.label ? { ...it, status: 'error', error: msg.error } : it,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'assistant:delta':
|
|
||||||
// Skip messages from parallel sub-agents (shown in iteration grid instead)
|
|
||||||
if (inParallelRef.current && msg.iterationLabel) break;
|
|
||||||
setWaitingStatus(null);
|
|
||||||
streamBufferRef.current += msg.text;
|
|
||||||
setStreamingText(streamBufferRef.current);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'assistant:text': {
|
|
||||||
if (inParallelRef.current && msg.iterationLabel) break;
|
|
||||||
const text = msg.text || streamBufferRef.current;
|
|
||||||
if (text) {
|
|
||||||
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text }]);
|
|
||||||
}
|
|
||||||
streamBufferRef.current = '';
|
|
||||||
setStreamingText('');
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'tool:start':
|
case 'pipeline:init':
|
||||||
if (inParallelRef.current && msg.iterationLabel) break;
|
setSteps(msg.steps);
|
||||||
setWaitingStatus(null);
|
break;
|
||||||
flushStream();
|
|
||||||
setMessages((prev) => [
|
case 'step:start':
|
||||||
|
flushStream();
|
||||||
|
setMessages([]);
|
||||||
|
setParallelStep(null);
|
||||||
|
setWaitingStatus(null);
|
||||||
|
inParallelRef.current = false;
|
||||||
|
setCurrentStep({
|
||||||
|
taskName: msg.taskName,
|
||||||
|
iteration: msg.iteration,
|
||||||
|
status: 'running',
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step:complete':
|
||||||
|
flushStream();
|
||||||
|
setWaitingStatus(null);
|
||||||
|
setCurrentStep((prev) => prev ? { ...prev, status: 'complete', cost: msg.cost } : null);
|
||||||
|
if (msg.cost) addCost(msg.cost);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step:skip':
|
||||||
|
setSkippedItems((prev) => [...prev, { label: msg.label, reason: msg.reason }]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step:waiting':
|
||||||
|
setWaitingStatus({ stepIndex: msg.stepIndex, elapsed: msg.elapsed, iterationLabel: msg.iterationLabel });
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'step:parallel':
|
||||||
|
flushStream();
|
||||||
|
setMessages([]);
|
||||||
|
setCurrentStep(null);
|
||||||
|
inParallelRef.current = true;
|
||||||
|
setParallelStep({
|
||||||
|
stepIndex: msg.stepIndex,
|
||||||
|
taskName: msg.taskName,
|
||||||
|
concurrency: msg.concurrency,
|
||||||
|
iterations: msg.iterations.map((label) => ({ label, status: 'pending' })),
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'iteration:start':
|
||||||
|
setParallelStep((prev) => {
|
||||||
|
if (!prev) return prev;
|
||||||
|
return {
|
||||||
...prev,
|
...prev,
|
||||||
{
|
iterations: prev.iterations.map((it) =>
|
||||||
role: 'tool' as const,
|
it.label === msg.label ? { ...it, status: 'running' } : it,
|
||||||
id: randomId(),
|
|
||||||
toolCallId: msg.toolCallId,
|
|
||||||
toolName: msg.toolName,
|
|
||||||
toolInput: msg.toolInput,
|
|
||||||
output: undefined,
|
|
||||||
isError: false,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'tool:result':
|
|
||||||
if (inParallelRef.current && msg.iterationLabel) break;
|
|
||||||
setMessages((prev) =>
|
|
||||||
prev.map((m) =>
|
|
||||||
m.role === 'tool' && 'toolCallId' in m && m.toolCallId === msg.toolCallId
|
|
||||||
? { ...m, output: msg.output, isError: msg.isError }
|
|
||||||
: m,
|
|
||||||
),
|
),
|
||||||
);
|
};
|
||||||
break;
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case 'pipeline:complete':
|
case 'iteration:complete':
|
||||||
flushStream();
|
setParallelStep((prev) => {
|
||||||
setTotalCost(msg.totalCost);
|
if (!prev) return prev;
|
||||||
setPhase('done');
|
return {
|
||||||
stopTimer();
|
...prev,
|
||||||
break;
|
iterations: prev.iterations.map((it) =>
|
||||||
|
it.label === msg.label ? { ...it, status: 'complete', cost: msg.cost } : it,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (msg.cost) addCost(msg.cost);
|
||||||
|
break;
|
||||||
|
|
||||||
case 'error':
|
case 'iteration:error':
|
||||||
flushStream();
|
setParallelStep((prev) => {
|
||||||
setMessages((prev) => [...prev, { role: 'error' as const, id: randomId(), text: msg.message }]);
|
if (!prev) return prev;
|
||||||
setHasError(true);
|
return {
|
||||||
setPhase('done');
|
...prev,
|
||||||
stopTimer();
|
iterations: prev.iterations.map((it) =>
|
||||||
break;
|
it.label === msg.label ? { ...it, status: 'error', error: msg.error } : it,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case 'stopped':
|
case 'assistant:delta':
|
||||||
flushStream();
|
// Skip messages from parallel sub-agents (shown in iteration grid instead)
|
||||||
setPhase('done');
|
if (inParallelRef.current && msg.iterationLabel) break;
|
||||||
stopTimer();
|
setWaitingStatus(null);
|
||||||
break;
|
streamBufferRef.current += msg.text;
|
||||||
|
setStreamingText(streamBufferRef.current);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'assistant:text': {
|
||||||
|
if (inParallelRef.current && msg.iterationLabel) break;
|
||||||
|
const text = msg.text || streamBufferRef.current;
|
||||||
|
if (text) {
|
||||||
|
setMessages((prev) => [...prev, { role: 'assistant', id: randomId(), text }]);
|
||||||
|
}
|
||||||
|
streamBufferRef.current = '';
|
||||||
|
setStreamingText('');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
[flushStream, stopTimer, addCost],
|
case 'tool:start':
|
||||||
);
|
if (inParallelRef.current && msg.iterationLabel) break;
|
||||||
|
setWaitingStatus(null);
|
||||||
|
flushStream();
|
||||||
|
setMessages((prev) => [
|
||||||
|
...prev,
|
||||||
|
{
|
||||||
|
role: 'tool' as const,
|
||||||
|
id: randomId(),
|
||||||
|
toolCallId: msg.toolCallId,
|
||||||
|
toolName: msg.toolName,
|
||||||
|
toolInput: msg.toolInput,
|
||||||
|
output: undefined,
|
||||||
|
isError: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'tool:result':
|
||||||
|
if (inParallelRef.current && msg.iterationLabel) break;
|
||||||
|
setMessages((prev) =>
|
||||||
|
prev.map((m) =>
|
||||||
|
m.role === 'tool' && 'toolCallId' in m && m.toolCallId === msg.toolCallId
|
||||||
|
? { ...m, output: msg.output, isError: msg.isError }
|
||||||
|
: m,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'pipeline:complete':
|
||||||
|
flushStream();
|
||||||
|
setTotalCost(msg.totalCost);
|
||||||
|
setPhase('done');
|
||||||
|
stopTimer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
flushStream();
|
||||||
|
setMessages((prev) => [...prev, { role: 'error' as const, id: randomId(), text: msg.message }]);
|
||||||
|
setHasError(true);
|
||||||
|
setPhase('done');
|
||||||
|
stopTimer();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'stopped':
|
||||||
|
flushStream();
|
||||||
|
setPhase('done');
|
||||||
|
stopTimer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, [flushStream, stopTimer, addCost]);
|
||||||
|
|
||||||
// The socket below is opened once and must stay open, so its listener is registered once too — and would
|
// The socket below is opened once and must stay open, so its listener is registered once too — and would
|
||||||
// hold the first render's `handleEvent` forever. That closure carries `flushStream`'s captured
|
// hold the first render's `handleEvent` forever. That closure carries `flushStream`'s captured
|
||||||
@@ -365,35 +308,32 @@ export function usePipelineRunner() {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const run = useCallback(
|
const run = useCallback((taskDirName: string, inputs: Record<string, string>, cwd?: string, model?: string, startAt?: number) => {
|
||||||
(taskDirName: string, inputs: Record<string, string>, cwd?: string, model?: string, startAt?: number) => {
|
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
||||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
|
|
||||||
|
|
||||||
setPhase('running');
|
setPhase('running');
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
setStreamingText('');
|
setStreamingText('');
|
||||||
setTotalCost(null);
|
setTotalCost(null);
|
||||||
setRunningCost({ inputTokens: 0, outputTokens: 0, totalUSD: 0 });
|
setRunningCost({ inputTokens: 0, outputTokens: 0, totalUSD: 0 });
|
||||||
setHasError(false);
|
setHasError(false);
|
||||||
setSkippedItems([]);
|
setSkippedItems([]);
|
||||||
setCurrentStep(null);
|
setCurrentStep(null);
|
||||||
setParallelStep(null);
|
setParallelStep(null);
|
||||||
setWaitingStatus(null);
|
setWaitingStatus(null);
|
||||||
setElapsed(0);
|
setElapsed(0);
|
||||||
setJobId(null);
|
setJobId(null);
|
||||||
jobIdRef.current = null;
|
jobIdRef.current = null;
|
||||||
streamBufferRef.current = '';
|
streamBufferRef.current = '';
|
||||||
|
|
||||||
startTimeRef.current = Date.now();
|
startTimeRef.current = Date.now();
|
||||||
if (timerRef.current) clearInterval(timerRef.current);
|
if (timerRef.current) clearInterval(timerRef.current);
|
||||||
timerRef.current = setInterval(() => {
|
timerRef.current = setInterval(() => {
|
||||||
setElapsed(Math.floor((Date.now() - startTimeRef.current) / 1000));
|
setElapsed(Math.floor((Date.now() - startTimeRef.current) / 1000));
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
wsRef.current.send(JSON.stringify({ type: 'run', taskDirName, inputs, cwd, model, startAt }));
|
wsRef.current.send(JSON.stringify({ type: 'run', taskDirName, inputs, cwd, model, startAt }));
|
||||||
},
|
}, []);
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const stop = useCallback(() => {
|
const stop = useCallback(() => {
|
||||||
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN || !jobIdRef.current) return;
|
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN || !jobIdRef.current) return;
|
||||||
@@ -401,21 +341,7 @@ export function usePipelineRunner() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
phase,
|
phase, isConnected, jobId, steps, currentStep, parallelStep, messages, streamingText,
|
||||||
isConnected,
|
totalCost, runningCost, hasError, skippedItems, elapsed, waitingStatus, run, stop,
|
||||||
jobId,
|
|
||||||
steps,
|
|
||||||
currentStep,
|
|
||||||
parallelStep,
|
|
||||||
messages,
|
|
||||||
streamingText,
|
|
||||||
totalCost,
|
|
||||||
runningCost,
|
|
||||||
hasError,
|
|
||||||
skippedItems,
|
|
||||||
elapsed,
|
|
||||||
waitingStatus,
|
|
||||||
run,
|
|
||||||
stop,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import { EmbeddableChat } from '../../apps/Chat/EmbeddableChat';
|
|||||||
|
|
||||||
type SetSearchParams = ReturnType<typeof useSearchParams>[1];
|
type SetSearchParams = ReturnType<typeof useSearchParams>[1];
|
||||||
|
|
||||||
const onReplaceView =
|
const onReplaceView = (setSearchParams: SetSearchParams) =>
|
||||||
(setSearchParams: SetSearchParams) =>
|
|
||||||
(viewPath: string, viewRoot: string, ephemeralPath: string, ephemeralRoot: string) => {
|
(viewPath: string, viewRoot: string, ephemeralPath: string, ephemeralRoot: string) => {
|
||||||
setSearchParams((prev) => {
|
setSearchParams((prev) => {
|
||||||
const next = new URLSearchParams(prev);
|
const next = new URLSearchParams(prev);
|
||||||
@@ -37,12 +36,7 @@ export function ViewerProvider({ children }: { children: ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileViewerProvider
|
<FileViewerProvider filePath={viewPath} fileName={fileName} onOpenFile={onOpenFile} onReplaceView={onReplaceView(setSearchParams)}>
|
||||||
filePath={viewPath}
|
|
||||||
fileName={fileName}
|
|
||||||
onOpenFile={onOpenFile}
|
|
||||||
onReplaceView={onReplaceView(setSearchParams)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</FileViewerProvider>
|
</FileViewerProvider>
|
||||||
);
|
);
|
||||||
@@ -65,13 +59,7 @@ export function EphemeralProvider({ children }: { children: ReactNode }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileViewerProvider
|
<FileViewerProvider filePath={ephemeralPath} fileName={fileName} root={ephemeralRoot} onOpenFile={onOpenFile} onReplaceView={onReplaceView(setSearchParams)}>
|
||||||
filePath={ephemeralPath}
|
|
||||||
fileName={fileName}
|
|
||||||
root={ephemeralRoot}
|
|
||||||
onOpenFile={onOpenFile}
|
|
||||||
onReplaceView={onReplaceView(setSearchParams)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</FileViewerProvider>
|
</FileViewerProvider>
|
||||||
);
|
);
|
||||||
@@ -85,13 +73,7 @@ export function Ephemeral2Provider({ children }: { children: ReactNode }) {
|
|||||||
const fileName = ephemeral2Path.split('/').pop() ?? '';
|
const fileName = ephemeral2Path.split('/').pop() ?? '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FileViewerProvider
|
<FileViewerProvider filePath={ephemeral2Path} fileName={fileName} root={ephemeral2Root} autoPlay={ephemeral2Auto} onReplaceView={onReplaceView(setSearchParams)}>
|
||||||
filePath={ephemeral2Path}
|
|
||||||
fileName={fileName}
|
|
||||||
root={ephemeral2Root}
|
|
||||||
autoPlay={ephemeral2Auto}
|
|
||||||
onReplaceView={onReplaceView(setSearchParams)}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</FileViewerProvider>
|
</FileViewerProvider>
|
||||||
);
|
);
|
||||||
@@ -103,14 +85,15 @@ export const ChatEphemeralBody = () => {
|
|||||||
const chatContext = searchParams.get('chatContext') ?? '';
|
const chatContext = searchParams.get('chatContext') ?? '';
|
||||||
const chatType = searchParams.get('chatType') as 'file' | 'folder' | null;
|
const chatType = searchParams.get('chatType') as 'file' | 'folder' | null;
|
||||||
|
|
||||||
const cwdPath = chatType === 'file' ? chatContext.substring(0, chatContext.lastIndexOf('/')) || '/' : chatContext;
|
const cwdPath = chatType === 'file'
|
||||||
|
? chatContext.substring(0, chatContext.lastIndexOf('/')) || '/'
|
||||||
|
: chatContext;
|
||||||
|
|
||||||
const tag = chatType === 'file' ? 'file' : 'folder';
|
const tag = chatType === 'file' ? 'file' : 'folder';
|
||||||
const path = chatContext.replace(/^\//, '');
|
const path = chatContext.replace(/^\//, '');
|
||||||
const message =
|
const message = chatType === 'file'
|
||||||
chatType === 'file'
|
? `[${tag}: ${path}] Let's talk about this file`
|
||||||
? `[${tag}: ${path}] Let's talk about this file`
|
: `[${tag}: ${path || '/'}] consider, for this session, this directory as your current working directory`;
|
||||||
: `[${tag}: ${path || '/'}] consider, for this session, this directory as your current working directory`;
|
|
||||||
|
|
||||||
const handleMessageComplete = useCallback(() => {
|
const handleMessageComplete = useCallback(() => {
|
||||||
bumpFilesRefresh();
|
bumpFilesRefresh();
|
||||||
|
|||||||
Reference in New Issue
Block a user