fix(pi): settle delayed compact ownership

This commit is contained in:
2026-08-26 09:41:25 +08:00
parent f5e6a04c7d
commit 9f05e2d7e1
12 changed files with 334 additions and 25 deletions

View File

@@ -48,6 +48,32 @@ const THINKING_OPTIONS: Array<{ value: ConversationThinkingLevel; label: string
{ value: 'high', label: '高思考' },
];
interface LocalActionError {
message: string;
backendCode?: string;
startedGeneration: number;
startedSeq: number;
}
function localActionError(
error: unknown,
startedGeneration: number,
startedSeq: number,
): LocalActionError {
const details = error && typeof error === 'object'
&& 'details' in error
&& error.details
&& typeof error.details === 'object'
? error.details as { backendCode?: unknown }
: null;
return {
message: error instanceof Error ? error.message : String(error),
...(typeof details?.backendCode === 'string' ? { backendCode: details.backendCode } : {}),
startedGeneration,
startedSeq,
};
}
function runLabel(snapshot: ConversationSnapshot | null): string {
if (!snapshot) return '正在准备';
const { run } = snapshot;
@@ -90,7 +116,7 @@ export function CodingConversationHeader({
const vendors = useProviderStore((state) => state.vendors);
const options = useMemo(() => buildCodingModelOptions(accounts, vendors), [accounts, vendors]);
const [busyAction, setBusyAction] = useState<string | null>(null);
const [actionError, setActionError] = useState<string | null>(null);
const [actionError, setActionError] = useState<LocalActionError | null>(null);
const [renameOpen, setRenameOpen] = useState(false);
const [titleDraft, setTitleDraft] = useState(conversation?.title ?? '');
@@ -110,13 +136,27 @@ export function CodingConversationHeader({
? Math.min(100, Math.round((context.usedTokens / context.contextWindow) * 100))
: 0;
const recoverable = Boolean(snapshot?.run.error?.recoverable || snapshot?.worker.error?.recoverable);
const runtimeErrorCode = snapshot?.run.error?.code ?? snapshot?.worker.error?.code;
const cursorAdvancedPastAction = Boolean(actionError && snapshot && (
snapshot.cursor.workerGeneration > actionError.startedGeneration
|| (snapshot.cursor.workerGeneration === actionError.startedGeneration
&& snapshot.cursor.seq > actionError.startedSeq)
));
const visibleActionError = actionError?.backendCode === 'CODING_REQUEST_UNCERTAIN'
&& !running
&& runtimeErrorCode !== 'CODING_REQUEST_UNCERTAIN'
&& cursorAdvancedPastAction
? null
: actionError;
const perform = (key: string, action: () => Promise<void>) => {
if (busyAction || !conversation) return;
const startedGeneration = snapshot?.cursor.workerGeneration ?? 0;
const startedSeq = snapshot?.cursor.seq ?? 0;
setBusyAction(key);
setActionError(null);
void action()
.catch((error) => setActionError(error instanceof Error ? error.message : String(error)))
.catch((error) => setActionError(localActionError(error, startedGeneration, startedSeq)))
.finally(() => setBusyAction((current) => current === key ? null : current));
};
@@ -226,7 +266,7 @@ export function CodingConversationHeader({
</Button>
</div>
)}
{actionError && <p className="border-t border-destructive/10 bg-destructive/5 px-5 py-2 text-xs text-destructive" role="alert">{actionError}</p>}
{visibleActionError && <p className="border-t border-destructive/10 bg-destructive/5 px-5 py-2 text-xs text-destructive" role="alert">{visibleActionError.message}</p>}
<Dialog open={renameOpen} onOpenChange={setRenameOpen}>
<DialogContent>