diff --git a/frontend/packages/agent/src/Root.tsx b/frontend/packages/agent/src/Root.tsx index 168f515a..f2f45759 100644 --- a/frontend/packages/agent/src/Root.tsx +++ b/frontend/packages/agent/src/Root.tsx @@ -27,7 +27,11 @@ import { fmtAge, fmtTokens } from "./lib/format.js"; import { resolveDashboardBase } from "./lib/dashboardBase.js"; import { submitPauseResume } from "./lib/pauseAction.js"; import { postModel, postEffort } from "./lib/modelEffort.js"; -import { postCancelTurn, postLogout } from "./lib/termActions.js"; +import { + postCancelTurn, + postLogout, + postNewSession, +} from "./lib/termActions.js"; import type { TokenUsage } from "./types.js"; type OpenPanel = "inbox" | "todos" | null; @@ -225,6 +229,7 @@ export function Root() { onTogglePause={() => {}} thinking={false} onCancelTurn={() => Promise.resolve()} + onNewSession={() => Promise.resolve()} onLogout={() => Promise.resolve()} /> @@ -332,6 +337,15 @@ export function Root() { refresh(); }) } + onNewSession={() => + postNewSession().then((r) => { + if (!r.ok) + liveStreamRef.current?.pushNote( + `✗ /new-session failed${r.detail ? ": " + r.detail : ""}`, + ); + refresh(); + }) + } onLogout={() => postLogout().then((r) => { if (!r.ok) diff --git a/frontend/packages/agent/src/components/StatusChips.tsx b/frontend/packages/agent/src/components/StatusChips.tsx index 8a1f1eaf..dd7fd2b4 100644 --- a/frontend/packages/agent/src/components/StatusChips.tsx +++ b/frontend/packages/agent/src/components/StatusChips.tsx @@ -37,6 +37,12 @@ export interface StatusChipsProps { lastTurnLabel?: string; thinking: boolean; onCancelTurn: () => Promise; + /** Drops all prior `--continue` context and starts fresh. Always + * offered, same as logout — the old app.js overflow menu had it as + * an unconditional click target too, this is just the click path + * catching up to what `/new-session` already does. See + * `termActions.ts`'s `postNewSession`. */ + onNewSession: () => Promise; /** Rotates OAuth creds + parks the agent in needs-login. Always * offered (unlike cancel-turn, not gated on `thinking`) — the old * page's overflow menu had it unconditionally too. See @@ -119,6 +125,7 @@ function StatusMenu({ onTogglePause, thinking, onCancelTurn, + onNewSession, onLogout, }: Pick< StatusChipsProps, @@ -129,16 +136,19 @@ function StatusMenu({ | "onTogglePause" | "thinking" | "onCancelTurn" + | "onNewSession" | "onLogout" >) { const [open, setOpen] = useState(false); const [confirmCancel, setConfirmCancel] = useState(false); + const [confirmNewSession, setConfirmNewSession] = useState(false); const [confirmLogout, setConfirmLogout] = useState(false); const anchorRef = useRef(null); function close() { setOpen(false); setConfirmCancel(false); + setConfirmNewSession(false); setConfirmLogout(false); } @@ -153,7 +163,12 @@ function StatusMenu({ }); } // Always offered, regardless of turn state — same as the old overflow - // menu's unconditional 🔓 logout entry. + // menu's unconditional ↻ new-session and 🔓 logout entries. + options.push({ + value: "new-session", + label: confirmNewSession ? "↻ click again to confirm" : "↻ new session", + danger: true, + }); options.push({ value: "logout", label: confirmLogout ? "🔓 click again to confirm" : "🔓 logout", @@ -189,6 +204,16 @@ function StatusMenu({ close(); return; } + if (value === "new-session") { + // Same arm-then-fire shape as cancel-turn/logout. + if (!confirmNewSession) { + setConfirmNewSession(true); + return; + } + onNewSession(); + close(); + return; + } // value === 'logout': same arm-then-fire shape as cancel-turn. if (!confirmLogout) { setConfirmLogout(true); @@ -222,6 +247,7 @@ export function StatusChips({ lastTurnLabel, thinking, onCancelTurn, + onNewSession, onLogout, }: StatusChipsProps) { return ( @@ -234,6 +260,7 @@ export function StatusChips({ onTogglePause={onTogglePause} thinking={thinking} onCancelTurn={onCancelTurn} + onNewSession={onNewSession} onLogout={onLogout} />