| 1 | import { useEffect, useRef, useState } from "react"; |
| 2 | import { app } from "../lib/bridge"; |
| 3 | import { useT } from "../lib/i18n"; |
| 4 | import type { ToolRecoveryBindings, ToolRecoverySnapshot } from "../lib/toolRecovery"; |
| 5 | import "./ToolRecoveryPanel.css"; |
| 6 | |
| 7 | export function ToolRecoveryPanel({ tabId, sessionKey, running, refreshKey, bindings = app }: { |
| 8 | tabId: string; sessionKey: string; running: boolean; refreshKey: number; |
| 9 | bindings?: ToolRecoveryBindings; |
| 10 | }) { |
| 11 | const t = useT(); |
| 12 | const generation = useRef(0); |
| 13 | const [snapshot, setSnapshot] = useState<ToolRecoverySnapshot | null>(null); |
| 14 | const [error, setError] = useState(""); |
| 15 | useEffect(() => { |
| 16 | const own = ++generation.current; |
| 17 | setSnapshot(null); setError(""); |
| 18 | if (!running && bindings.GetToolRecoveryForTab) { |
| 19 | void bindings.GetToolRecoveryForTab(tabId).then(next => { |
| 20 | if (generation.current === own) setSnapshot(next); |
| 21 | }).catch(err => { if (generation.current === own) setError(String(err)); }); |
| 22 | } |
| 23 | return () => { generation.current++; }; |
| 24 | }, [bindings, tabId, sessionKey, running, refreshKey]); |
| 25 | |
| 26 | if (!snapshot?.calls.length && !error) return null; |
| 27 | return <section className="notice-line notice-line--warn tool-recovery-panel" aria-label={t("toolRecovery.title")}> |
| 28 | <details open> |
| 29 | <summary className="notice-line__title">{t("toolRecovery.title")}</summary> |
| 30 | <div className="notice-line__text"> |
| 31 | {error && <p role="alert">{error}</p>} |
| 32 | {!!snapshot?.calls.length && <p>{t("toolRecovery.retired")}</p>} |
| 33 | {(snapshot?.calls ?? []).map(call => <div key={call.identity.attempt_id}> |
| 34 | <p>{call.identity.canonical_tool} · {t("toolRecovery.unknown")}</p> |
| 35 | <details><summary>{t("toolRecovery.details")}</summary> |
| 36 | <p>{call.identity.resource_scope}</p> |
| 37 | <p>{call.identity.argument_digest}</p> |
| 38 | {call.arguments !== undefined && <pre>{JSON.stringify(call.arguments, null, 2)}</pre>} |
| 39 | </details> |
| 40 | </div>)} |
| 41 | </div> |
| 42 | </details> |
| 43 | </section>; |
| 44 | } |
| 45 |