fix(web): émet chat.attached pour les sessions agents structurées au lieu de UNSUPPORTED — #229
This commit is contained in:
@ -52,6 +52,7 @@ export interface CustomAgentChatViewProps {
|
||||
onComposerHeightPxChange?: (heightPx: number) => void;
|
||||
draftScope?: ChatDraftScope | null;
|
||||
mobile?: boolean;
|
||||
onOpenNativeTui?: () => void;
|
||||
onSessionId: (sessionId: string | null) => void;
|
||||
onConversationId: (conversationId: string | null) => void;
|
||||
}
|
||||
@ -118,6 +119,11 @@ interface SlashCommandInvocation {
|
||||
arguments: unknown[];
|
||||
}
|
||||
|
||||
interface ChatErrorState {
|
||||
message: string;
|
||||
openFailure: boolean;
|
||||
}
|
||||
|
||||
function describe(e: unknown): string {
|
||||
if (e && typeof e === "object" && "message" in e) {
|
||||
return String((e as GatewayError).message);
|
||||
@ -125,9 +131,9 @@ function describe(e: unknown): string {
|
||||
return String(e);
|
||||
}
|
||||
|
||||
function describeOpenFailure(e: unknown, mobile: boolean): string {
|
||||
if (!mobile) return describe(e);
|
||||
return "Le CLI custom n'a pas pu démarrer. Vérifiez la configuration du profil ou utilisez la TUI native.";
|
||||
function describeOpenFailure(e: unknown): string {
|
||||
const message = describe(e).trim();
|
||||
return message || "Cause technique non disponible.";
|
||||
}
|
||||
|
||||
function instrumentationError(e: unknown): Record<string, unknown> {
|
||||
@ -734,6 +740,7 @@ export function CustomAgentChatView({
|
||||
onComposerHeightPxChange,
|
||||
draftScope: explicitDraftScope,
|
||||
mobile = false,
|
||||
onOpenNativeTui,
|
||||
onSessionId,
|
||||
onConversationId,
|
||||
}: CustomAgentChatViewProps) {
|
||||
@ -770,9 +777,10 @@ export function CustomAgentChatView({
|
||||
const [profileDialog, setProfileDialog] =
|
||||
useState<ProfileCommandDialogState | null>(null);
|
||||
const [opening, setOpening] = useState(Boolean(sessionId || conversationId));
|
||||
const [openRetryNonce, setOpenRetryNonce] = useState(0);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [stopping, setStopping] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [error, setError] = useState<ChatErrorState | null>(null);
|
||||
const scrollRef = useRef<HTMLDivElement | null>(null);
|
||||
const composerRef = useRef<HTMLTextAreaElement | null>(null);
|
||||
const chatRootRef = useRef<HTMLDivElement | null>(null);
|
||||
@ -1371,7 +1379,9 @@ export function CustomAgentChatView({
|
||||
sessionId: externalSessionId,
|
||||
error: instrumentationError(e),
|
||||
});
|
||||
if (!cancelled) setError(describeOpenFailure(e, mobile));
|
||||
if (!cancelled) {
|
||||
setError({ message: describeOpenFailure(e), openFailure: true });
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) setOpening(false);
|
||||
}
|
||||
@ -1390,7 +1400,7 @@ export function CustomAgentChatView({
|
||||
applyChatViewState,
|
||||
currentConversationCacheKey,
|
||||
conversationId,
|
||||
mobile,
|
||||
openRetryNonce,
|
||||
]);
|
||||
|
||||
const canSend = useMemo(
|
||||
@ -1490,7 +1500,10 @@ export function CustomAgentChatView({
|
||||
|
||||
async function executeSlashCommand(invocation: SlashCommandInvocation) {
|
||||
if (!agent.executeSlashCommand) {
|
||||
setError(`Commande ${invocation.name} indisponible dans ce runtime.`);
|
||||
setError({
|
||||
message: `Commande ${invocation.name} indisponible dans ce runtime.`,
|
||||
openFailure: false,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setDraft("");
|
||||
@ -1546,7 +1559,7 @@ export function CustomAgentChatView({
|
||||
}
|
||||
} catch (e) {
|
||||
const message = `Commande ${invocation.name} échouée: ${describe(e)}`;
|
||||
setError(message);
|
||||
setError({ message, openFailure: false });
|
||||
updateChatViewState((prev) => ({
|
||||
turns: [...prev.turns, { role: "error", text: message, receivedAtMs: Date.now() }],
|
||||
progressIndex: prev.progressIndex,
|
||||
@ -1633,7 +1646,7 @@ export function CustomAgentChatView({
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
setError(describe(e));
|
||||
setError({ message: describe(e), openFailure: false });
|
||||
}
|
||||
}
|
||||
|
||||
@ -1704,7 +1717,7 @@ export function CustomAgentChatView({
|
||||
} catch (recoveryError) {
|
||||
finishInteraction();
|
||||
setBusy(false);
|
||||
setError(describe(recoveryError));
|
||||
setError({ message: describe(recoveryError), openFailure: false });
|
||||
updateChatViewState((prev) => ({
|
||||
turns: [
|
||||
...prev.turns,
|
||||
@ -1717,7 +1730,7 @@ export function CustomAgentChatView({
|
||||
}
|
||||
finishInteraction();
|
||||
setBusy(false);
|
||||
setError(describe(e));
|
||||
setError({ message: describe(e), openFailure: false });
|
||||
updateChatViewState((prev) => ({
|
||||
turns: [...prev.turns, { role: "error", text: describe(e), receivedAtMs: Date.now() }],
|
||||
progressIndex: prev.progressIndex,
|
||||
@ -1779,7 +1792,7 @@ export function CustomAgentChatView({
|
||||
});
|
||||
return;
|
||||
}
|
||||
setError(describe(e));
|
||||
setError({ message: describe(e), openFailure: false });
|
||||
updateChatViewState((prev) => ({
|
||||
turns: [
|
||||
...prev.turns,
|
||||
@ -1821,9 +1834,40 @@ export function CustomAgentChatView({
|
||||
</p>
|
||||
)}
|
||||
{error && (
|
||||
<p role="alert" className="m-3 rounded-md border border-danger/40 bg-danger/10 p-2 text-xs text-danger">
|
||||
{error}
|
||||
</p>
|
||||
<div
|
||||
role="alert"
|
||||
className="m-3 rounded-md border border-danger/40 bg-danger/10 p-2 text-xs text-danger"
|
||||
>
|
||||
{error.openFailure ? (
|
||||
<div className="flex min-w-0 flex-col gap-2">
|
||||
<div className="font-medium">Le CLI custom n'a pas pu démarrer.</div>
|
||||
<div className="break-words">
|
||||
Cause: {error.message}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="primary"
|
||||
disabled={opening}
|
||||
onClick={() => setOpenRetryNonce((value) => value + 1)}
|
||||
>
|
||||
Réessayer
|
||||
</Button>
|
||||
{onOpenNativeTui && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
onClick={onOpenNativeTui}
|
||||
>
|
||||
Ouvrir en TUI native
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
error.message
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{profileDialog && (
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user