diff --git a/src/renderer/src/lib/agent-paste-draft.ts b/src/renderer/src/lib/agent-paste-draft.ts index c2597551515..177059a6818 100644 --- a/src/renderer/src/lib/agent-paste-draft.ts +++ b/src/renderer/src/lib/agent-paste-draft.ts @@ -1,4 +1,5 @@ import type { TuiAgent } from '../../../shared/types' +import { TUI_AGENT_CONFIG } from '../../../shared/tui-agent-config' import { detectAgentStatusFromTitle } from '../../../shared/agent-detection' import { isShellProcess } from '@/lib/tui-agent-startup' import { useAppStore } from '@/store' @@ -36,9 +37,12 @@ const READINESS_TIMEOUT_MS = 12000 * `onTimeout` lets the caller surface a UI hint (e.g. toast) when the agent * doesn't reach a ready state inside `timeoutMs`. * - * `agent` is currently informational only — kept on the call signature so - * future per-agent specializations (e.g. an `unsupported` opt-out) have a - * place to land without retouching every call site. + * Some agents (Copilot, cursor-agent) gate first-run launches behind a + * trust/permission menu that captures any keystroke as menu input. Those + * agents set `skipDraftUrlInjection: true` in TUI_AGENT_CONFIG and this + * helper returns false without touching the PTY for them — the workspace + * still opens cleanly, the user just types/pastes the URL themselves once + * past the menu. */ export async function pasteDraftWhenAgentReady(args: { tabId: string @@ -48,7 +52,11 @@ export async function pasteDraftWhenAgentReady(args: { timeoutMs?: number onTimeout?: () => void }): Promise { - const { tabId, expectedProcess, content, timeoutMs, onTimeout } = args + const { tabId, expectedProcess, content, agent, timeoutMs, onTimeout } = args + + if (agent && TUI_AGENT_CONFIG[agent].skipDraftUrlInjection) { + return false + } const ready = await waitForTuiInputReady(tabId, expectedProcess, { timeoutMs: timeoutMs ?? READINESS_TIMEOUT_MS diff --git a/src/shared/tui-agent-config.ts b/src/shared/tui-agent-config.ts index a5ed20950a2..9c9ded3e4ea 100644 --- a/src/shared/tui-agent-config.ts +++ b/src/shared/tui-agent-config.ts @@ -12,6 +12,13 @@ export type TuiAgentConfig = { launchCmd: string expectedProcess: string promptInjectionMode: AgentPromptInjectionMode + /** Why: Copilot and Cursor-Agent open with a "do you trust this folder?" + * permission prompt that consumes any keystrokes (including bracketed + * paste) as menu input. There's no reliable signal for "trust prompt + * dismissed" from outside the TUI, so the safest behavior is to skip the + * draft URL pre-fill for these agents — the workspace still opens, the + * user just types/pastes the URL themselves once they're past the menu. */ + skipDraftUrlInjection?: boolean } // Why: the new-workspace handoff depends on three pieces of per-agent @@ -122,7 +129,13 @@ export const TUI_AGENT_CONFIG: Record = { detectCmd: 'cursor-agent', launchCmd: 'cursor-agent', expectedProcess: 'cursor-agent', - promptInjectionMode: 'argv' + promptInjectionMode: 'argv', + // Why: cursor-agent opens with a "Do you trust this directory?" prompt + // that consumes typed input as menu shortcuts ([a]/[w]/[q]). Pasting the + // URL while the menu is up either selects an option or quits — neither + // is helpful. Skip the draft pre-fill until/unless we have a reliable + // signal that the trust menu has been dismissed. + skipDraftUrlInjection: true }, droid: { detectCmd: 'droid', @@ -168,6 +181,11 @@ export const TUI_AGENT_CONFIG: Record = { // completion, which would kill the TUI session Orca is hosting. // `-i/--interactive ` starts an interactive session with the // initial prompt pre-executed — the behavior Orca needs. - promptInjectionMode: 'flag-interactive' + promptInjectionMode: 'flag-interactive', + // Why: Copilot's first-launch trust prompt ("Do you trust the files in + // this folder?") consumes any keystrokes as numbered-menu input. A + // pasted URL either picks an option or fails the menu validation. Skip + // the draft pre-fill — same reasoning as cursor-agent above. + skipDraftUrlInjection: true } }