fix(agents): keep remote launches on the optimistic delivery verdict

Receipt eligibility reads LOCAL evidence — locally-observed hook statuses
and the local managed-home install probe — which says nothing about a
remote host whose hooks may silently not fire. In a mixed local+remote
session that evidence would strict-gate an SSH launch and false-fail a
delivered prompt (review finding F1). Remote launches keep the optimistic
legacy verdict until host-scoped observed evidence exists (follow-up).

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
brennanb2025
2026-07-09 00:57:02 -07:00
co-authored by Orca
parent 2361bbe436
commit d6a4303e9d
5 changed files with 45 additions and 4 deletions
@@ -284,5 +284,26 @@ describe('deliverLaunchPromptToAgentTab', () => {
expect(mocks.watchForPromptSubmitReceipt).not.toHaveBeenCalled()
})
it('keeps the optimistic verdict for remote launches', async () => {
// Why: eligibility reads LOCAL hook evidence, which says nothing about a
// remote host whose hooks may silently not fire — strict-gating there
// would false-fail delivered prompts. Remote graduates in a follow-up
// with host-scoped observed evidence.
mocks.isPromptReceiptEligible.mockResolvedValue(true)
await expect(
deliverLaunchPromptToAgentTab({
tabId: 'tab-1',
agent: 'codex',
content: 'Fix failing checks',
submit: true,
forcePaste: true,
remoteLaunch: true
})
).resolves.toBe(true)
expect(mocks.watchForPromptSubmitReceipt).not.toHaveBeenCalled()
})
})
})
@@ -20,10 +20,16 @@ export async function deliverLaunchPromptToAgentTab(args: {
content: string
submit: boolean
forcePaste: boolean
/** Why: receipt eligibility reads LOCAL evidence (local hook install state,
* locally-observed statuses), which says nothing about a remote host whose
* hooks may silently not fire — strict-gating there would false-fail
* delivered prompts. Remote launches keep the optimistic verdict until
* host-scoped evidence exists (follow-up). */
remoteLaunch?: boolean
timeoutMs?: number
onTimeout?: (reason?: LaunchPromptDeliveryFailureReason) => void
}): Promise<boolean> {
const { tabId, agent, content, submit, forcePaste, timeoutMs, onTimeout } = args
const { tabId, agent, content, submit, forcePaste, remoteLaunch, timeoutMs, onTimeout } = args
const shouldSeed =
submit === true && content.trim().length > 0 && isNativeChatSupportedAgent(agent)
@@ -49,7 +55,10 @@ export async function deliverLaunchPromptToAgentTab(args: {
// delivery has no submitted paste, so it never waits on a receipt (which
// would never arrive).
const requireReceipt =
submit === true && !deliversViaNativePrefill && (await isPromptReceiptEligible(agent))
submit === true &&
remoteLaunch !== true &&
!deliversViaNativePrefill &&
(await isPromptReceiptEligible(agent))
const receiptWatch = requireReceipt
? watchForPromptSubmitReceipt({ tabId, agent, since: Date.now() })
: null
@@ -309,6 +309,7 @@ export function launchAgentInNewTab(args: LaunchAgentInNewTabArgs): LaunchAgentI
agent,
submit: submitPastedPrompt,
forcePaste: forcePasteAfterLaunch,
remoteLaunch: isRemote || runtimeEnvironmentId != null,
onTimeout: (reason) => {
const state = useAppStore.getState()
const tabsForWorktree = state.tabsByWorktree[worktreeId] ?? []
@@ -142,14 +142,23 @@ export async function pasteDirectWorkItemDraftWhenAgentReady(args: {
content: string
submit?: boolean
forcePaste?: boolean
remoteLaunch?: boolean
}): Promise<void> {
const { primaryTabId, startupPlan, content, submit = false, forcePaste = false } = args
const {
primaryTabId,
startupPlan,
content,
submit = false,
forcePaste = false,
remoteLaunch
} = args
await deliverLaunchPromptToAgentTab({
tabId: primaryTabId,
content,
agent: startupPlan.agent,
submit,
forcePaste,
remoteLaunch,
onTimeout: (reason) => {
const label = submit ? 'prompt' : 'work item context'
toast.message(
@@ -326,7 +326,8 @@ export async function launchWorkItemDirect(args: LaunchWorkItemDirectArgs): Prom
startupPlan,
content: draftContent,
submit: promptDelivery === 'submit-after-ready',
forcePaste: promptDelivery === 'submit-after-ready'
forcePaste: promptDelivery === 'submit-after-ready',
remoteLaunch: typeof getConnectionId(worktreeId) === 'string'
})
return true
}