Mirror upstream bash-preexec so preexec dispatch test reflects real command

The naive $BASH_COMMAND imitation captured Orca's chained
__orca_osc133_epilogue instead of the user command, failing on CI.
Read the command from history like real bash-preexec does.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinjing
2026-07-05 22:23:48 -07:00
co-authored by Orca
parent 41985953a0
commit 625f106ec0
2 changed files with 36 additions and 21 deletions
+20 -5
View File
@@ -590,24 +590,39 @@ describePosix('daemon shell-ready launch config', () => {
)
itWithBash(
'dispatches bash-preexec callbacks against the real command, not Orca hooks',
'dispatches a non-empty preexec_functions against the real command, not Orca hooks',
async () => {
const { getDaemonBashShellReadyRcfileContent } = await importFreshShellReady()
// Why: Orca's epilogue captures bash-preexec's re-armed DEBUG trap and
// chains it. A non-empty preexec_functions must still see the user's real
// command as $BASH_COMMAND — not __orca_osc133_epilogue or a __bp_* helper.
// chains it. A real preexec callback must fire against the user's command —
// not __orca_osc133_epilogue. Mirror upstream bash-preexec faithfully: it
// enables `functrace` (so Orca's `trap -p DEBUG` capture sees its trap),
// defers that install to the first prompt via PROMPT_COMMAND, and reads the
// command from `history` (so DEBUG fires on prompt hooks never dispatch a
// phantom). The naive `$BASH_COMMAND` imitation does none of these.
writeFileSync(
join(userDataPath, '.bash_profile'),
[
'preexec_functions=(__user_preexec)',
'__user_preexec() { printf \'USER_PREEXEC:%s\\n\' "$1"; }',
'__bp_inside=0',
'__bp_last_hist=""',
'__bp_preexec_invoke_exec() {',
' (( __bp_inside > 0 )) && return',
' [[ -n "${__bp_interactive_mode:-}" ]] || return',
' local __bp_inside=1',
' local this_command',
' this_command="$(builtin history 1)"',
' this_command="${this_command#"${this_command%%[![:space:]]*}"}"',
' this_command="${this_command#* }"',
' this_command="${this_command#"${this_command%%[![:space:]]*}"}"',
' [[ -n "$this_command" && "$this_command" != "$__bp_last_hist" ]] || return',
' __bp_last_hist="$this_command"',
' __bp_interactive_mode=""',
' local f',
' for f in "${preexec_functions[@]}"; do "$f" "$BASH_COMMAND"; done',
' for f in "${preexec_functions[@]}"; do "$f" "$this_command"; done',
'}',
"__bp_arm() { __bp_interactive_mode=1; trap '__bp_preexec_invoke_exec' DEBUG; }",
"__bp_arm() { set -o functrace; __bp_interactive_mode=1; trap '__bp_preexec_invoke_exec' DEBUG; }",
'PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND;}__bp_arm"'
].join('\n')
)
@@ -1757,15 +1757,14 @@ export function connectPanePty(
}
return pendingWrite.then(() => interruptInference.flushPending())
}
// Why: a launched/hook-known agent pane must confirm — not trust — a 133;D so a
// full-screen agent's leaked nested-shell 133;D can't clear its tab identity,
// even on a restore where no command-start read has recorded evidence yet.
const paneHasKnownAgentIdentity = (): boolean => {
const state = useAppStore.getState()
// Why: the 133;D confirmation guard and the visible-pane resampler both key off
// "does this pane expect an agent"; derive each signal once so the two callers
// can't drift and silently reintroduce the icon bug this fix closes.
const paneHasLiveHookAgentIcon = (state: ReturnType<typeof useAppStore.getState>): boolean => {
const entry = state.agentStatusByPaneKey[cacheKey]
if (entry?.state !== 'done' && agentTypeToIconAgent(entry?.agentType)) {
return true
}
return entry?.state !== 'done' && Boolean(agentTypeToIconAgent(entry?.agentType))
}
const paneExpectsLaunchAgent = (state: ReturnType<typeof useAppStore.getState>): boolean => {
const tab = (state.tabsByWorktree[deps.worktreeId] ?? []).find(
(candidate) => candidate.id === deps.tabId
)
@@ -1773,6 +1772,13 @@ export function connectPanePty(
tab?.launchAgent ?? paneStartup?.launchAgent ?? paneStartup?.initialAgentStatus?.agent
)
}
// Why: a launched/hook-known agent pane must confirm — not trust — a 133;D so a
// full-screen agent's leaked nested-shell 133;D can't clear its tab identity,
// even on a restore where no command-start read has recorded evidence yet.
const paneHasKnownAgentIdentity = (): boolean => {
const state = useAppStore.getState()
return paneHasLiveHookAgentIcon(state) || paneExpectsLaunchAgent(state)
}
// Why: a plain `codex`/`grok` sets its OSC title and the shell never repaints
// it on exit, so a confirmed return-to-shell must clear a title that still
// names an agent — otherwise the tab reads "grok" over a bare prompt. Only
@@ -1811,16 +1817,10 @@ export function connectPanePty(
if (foreground?.agent) {
return
}
const entry = state.agentStatusByPaneKey[cacheKey]
if (entry?.state !== 'done' && agentTypeToIconAgent(entry?.agentType)) {
if (paneHasLiveHookAgentIcon(state)) {
return
}
const tab = (state.tabsByWorktree[deps.worktreeId] ?? []).find(
(candidate) => candidate.id === deps.tabId
)
const expectsAgent = Boolean(
tab?.launchAgent ?? paneStartup?.launchAgent ?? paneStartup?.initialAgentStatus?.agent
)
const expectsAgent = paneExpectsLaunchAgent(state)
// Why: with no shell mark yet, launchAgent bootstrap already paints the icon,
// so a read is pointless. Otherwise probe the foreground: a shell mark (from a
// reattach or a full-screen agent's leaked nested-shell 133;D) can hide a