feat(agents): skip draft URL injection for copilot + cursor-agent

Both TUIs open with a 'Do you trust this folder?' menu on first launch
that consumes keystrokes as menu input — pasting a URL there either
selects an arbitrary option or quits the session. Mark them with
skipDraftUrlInjection so the workspace still opens cleanly; the user
types/pastes the URL themselves once past the trust menu.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Neil
2026-05-04 22:58:22 -07:00
co-authored by Orca
parent 1d29e84ca7
commit bd1b984a7c
2 changed files with 32 additions and 6 deletions
+12 -4
View File
@@ -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<boolean> {
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
+20 -2
View File
@@ -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<TuiAgent, TuiAgentConfig> = {
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<TuiAgent, TuiAgentConfig> = {
// completion, which would kill the TUI session Orca is hosting.
// `-i/--interactive <prompt>` 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
}
}