From 71504fc2bfa675e426c9d52e6c2a15d5a48162d0 Mon Sep 17 00:00:00 2001 From: Evgeny Zotov Date: Wed, 24 Jun 2026 21:17:27 +0200 Subject: [PATCH 1/2] feat(tui): add /session-id and /session-info slash commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add two native TUI slash commands that copy session information to the clipboard without an LLM round-trip: /session-id — copies the current session ID /session-info — copies "Project ; Session ; ID <session-id>" Both commands appear in the slash command palette under the "Session" category and are only enabled when a session is active. Uses the existing Clipboard context and toast notification system. Supersedes #11938 (closed by automated PR cleanup, not technical rejection). That PR covered only /session-id; this PR adds /session-info as well. Related: #18559 (adds output.cancelled for plugin-based cancellation), #15206 (CLI session info request), #11937 (original feature request). --- packages/tui/src/component/prompt/index.tsx | 42 +++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index aa002080b1dd..ac8973870e08 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -527,6 +527,48 @@ export function Prompt(props: PromptProps) { )) }, }, + { + title: "Copy session ID", + desc: "Copy the current session ID to clipboard", + name: "session.copy-id", + category: "Session", + slashName: "session-id", + enabled: Boolean(props.sessionID), + run: async () => { + if (!props.sessionID || !clipboard.write) return + await clipboard.write(props.sessionID) + toast.show({ + variant: "success", + message: `Copied: ${props.sessionID}`, + duration: 3000, + }) + dialog.clear() + }, + }, + { + title: "Copy session info", + desc: "Copy project path, session title, and session ID to clipboard", + name: "session.copy-info", + category: "Session", + slashName: "session-info", + enabled: Boolean(props.sessionID), + run: async () => { + if (!props.sessionID || !clipboard.write) return + const dir = + (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) || + project.instance.directory() || + "" + const session = sync.session.get(props.sessionID) + const title = session?.title ?? "" + await clipboard.write(`Project ${dir}; Session ${title}; ID ${props.sessionID}`) + toast.show({ + variant: "success", + message: "Copied project, session title, and ID to clipboard", + duration: 3000, + }) + dialog.clear() + }, + }, { title: "Warp", desc: "Change the workspace for the session", From a2d82432211d7e8d1a738399aa0eb64b714be271 Mon Sep 17 00:00:00 2001 From: Evgeny Zotov <gene.zotoff@gmail.com> Date: Thu, 25 Jun 2026 01:44:05 +0200 Subject: [PATCH 2/2] fix: clear input synchronously to prevent LLM submission after clipboard copy The input's onSubmit fires a double-deferred submit() via setTimeout. If the input still contains "/session-id" when that fires, it sends the text to the LLM as a prompt, causing unwanted agent activity. Fix: clear input.setText("") and setStore synchronously in the run handler, before the async clipboard.write(). When the deferred submit() fires, it hits the empty-input guard (if (!store.prompt.input) return false) and exits. --- packages/tui/src/component/prompt/index.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/tui/src/component/prompt/index.tsx b/packages/tui/src/component/prompt/index.tsx index ac8973870e08..65723abe30cd 100644 --- a/packages/tui/src/component/prompt/index.tsx +++ b/packages/tui/src/component/prompt/index.tsx @@ -536,13 +536,19 @@ export function Prompt(props: PromptProps) { enabled: Boolean(props.sessionID), run: async () => { if (!props.sessionID || !clipboard.write) return + // Clear input synchronously before the async clipboard write. + // The input's onSubmit fires a double-deferred submit() via setTimeout; + // if the input still contains "/session-id" when that fires, it sends + // the text to the LLM as a prompt. + input.setText("") + setStore("prompt", { input: "", parts: [] }) + dialog.clear() await clipboard.write(props.sessionID) toast.show({ variant: "success", message: `Copied: ${props.sessionID}`, duration: 3000, }) - dialog.clear() }, }, { @@ -554,6 +560,10 @@ export function Prompt(props: PromptProps) { enabled: Boolean(props.sessionID), run: async () => { if (!props.sessionID || !clipboard.write) return + // Clear input synchronously — see /session-id command for rationale. + input.setText("") + setStore("prompt", { input: "", parts: [] }) + dialog.clear() const dir = (project.instance.path().worktree === "/" ? undefined : project.instance.path().worktree) || project.instance.directory() || @@ -566,7 +576,6 @@ export function Prompt(props: PromptProps) { message: "Copied project, session title, and ID to clipboard", duration: 3000, }) - dialog.clear() }, }, {