summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoreytans <[email protected]>2026-02-13 13:25:47 +0200
committerGitHub <[email protected]>2026-02-13 05:25:47 -0600
commite242fe19e48f6aa70e5c3f7d54f34d688181edb2 (patch)
treee6a8789e50b6ea84e881f63825ef9f71ac770c33
parentf991a6c0b6bba97be27f3c132c14c5fa78d05536 (diff)
downloadopencode-e242fe19e48f6aa70e5c3f7d54f34d688181edb2.tar.gz
opencode-e242fe19e48f6aa70e5c3f7d54f34d688181edb2.zip
fix(web): use prompt_async endpoint to avoid timeout over VPN/tunnel (#12749)
-rw-r--r--packages/app/e2e/prompt/prompt-async.spec.ts43
-rw-r--r--packages/app/src/components/prompt-input/submit.ts2
2 files changed, 44 insertions, 1 deletions
diff --git a/packages/app/e2e/prompt/prompt-async.spec.ts b/packages/app/e2e/prompt/prompt-async.spec.ts
new file mode 100644
index 000000000..ce9b1a7a3
--- /dev/null
+++ b/packages/app/e2e/prompt/prompt-async.spec.ts
@@ -0,0 +1,43 @@
+import { test, expect } from "../fixtures"
+import { promptSelector } from "../selectors"
+import { sessionIDFromUrl } from "../actions"
+
+// Regression test for Issue #12453: the synchronous POST /message endpoint holds
+// the connection open while the agent works, causing "Failed to fetch" over
+// VPN/Tailscale. The fix switches to POST /prompt_async which returns immediately.
+test("prompt succeeds when sync message endpoint is unreachable", async ({ page, sdk, gotoSession }) => {
+ test.setTimeout(120_000)
+
+ // Simulate Tailscale/VPN killing the long-lived sync connection
+ await page.route("**/session/*/message", (route) => route.abort("connectionfailed"))
+
+ await gotoSession()
+
+ const token = `E2E_ASYNC_${Date.now()}`
+ await page.locator(promptSelector).click()
+ await page.keyboard.type(`Reply with exactly: ${token}`)
+ await page.keyboard.press("Enter")
+
+ await expect(page).toHaveURL(/\/session\/[^/?#]+/, { timeout: 30_000 })
+ const sessionID = sessionIDFromUrl(page.url())!
+
+ try {
+ // Agent response arrives via SSE despite sync endpoint being dead
+ await expect
+ .poll(
+ async () => {
+ const messages = await sdk.session.messages({ sessionID, limit: 50 }).then((r) => r.data ?? [])
+ return messages
+ .filter((m) => m.info.role === "assistant")
+ .flatMap((m) => m.parts)
+ .filter((p) => p.type === "text")
+ .map((p) => p.text)
+ .join("\n")
+ },
+ { timeout: 90_000 },
+ )
+ .toContain(token)
+ } finally {
+ await sdk.session.delete({ sessionID }).catch(() => undefined)
+ }
+})
diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts
index 49d75a95e..9a1fba5d5 100644
--- a/packages/app/src/components/prompt-input/submit.ts
+++ b/packages/app/src/components/prompt-input/submit.ts
@@ -385,7 +385,7 @@ export function createPromptSubmit(input: PromptSubmitInput) {
const send = async () => {
const ok = await waitForWorktree()
if (!ok) return
- await client.session.prompt({
+ await client.session.promptAsync({
sessionID: session.id,
agent,
model,