diff --git a/docs/tools.md b/docs/tools.md index 3694e7a..76d3ac8 100644 --- a/docs/tools.md +++ b/docs/tools.md @@ -497,9 +497,11 @@ from hanging.** The contract, precisely: - Tickets live for the tab's lifetime. There is no server, so there is nothing to expire against; a reload retires every one of them. -The timeout is `GATE_TIMEOUT_MS`, currently 25 s. That number is a guess chosen to be survivable, not -a measurement — `blocking.ts` says so at the definition. Measure the real tolerance of the hosts you -target and set it comfortably under whatever you measure. +The timeout is `GATE_TIMEOUT_MS`, currently 8 s, and that number is a measurement. A 25 s gate lost a +`propose_hypotheses` call outright: the ChatGPT in-app browser cut the agent's control connection at +roughly 20 s, so the host gave up before the gate returned its ticket — the card rendered and the +human's decision was recorded, but the agent received neither. Re-measure before targeting a host with +a different tolerance, and keep the value comfortably under whatever you measure. ### 12. `ask_human_visual({ question, choices, hintAtMs?, ticket? })` diff --git a/traces/src/lib/webmcp/blocking.ts b/traces/src/lib/webmcp/blocking.ts index b270c8a..fc11406 100644 --- a/traces/src/lib/webmcp/blocking.ts +++ b/traces/src/lib/webmcp/blocking.ts @@ -16,15 +16,22 @@ import type { Gate, GateResult } from '@/types/domain' * if they aren't. The agent then retries with the ticket, which reads to the model as a normal * polling loop rather than a broken page. * - * Measure the real tolerance of the agents you target before trusting this number, and set - * GATE_TIMEOUT_MS comfortably under whatever you measure. + * The ChatGPT in-app browser has since been measured at roughly 20 s before it severs the connection, + * which is what `GATE_TIMEOUT_MS` is set against. Re-measure before targeting a different host. */ /** - * TODO: Day 1 — replace with the measured value from S1, minus a healthy margin. - * 25s is a guess chosen to be survivable rather than correct — it is not a measurement. + * Measured, not guessed. A 25 s gate lost a `propose_hypotheses` call outright: the ChatGPT in-app + * browser severed the agent's control connection at roughly 20 s, so the host gave up before the gate + * could hand back its ticket. The page had already rendered the card and the human's decision was + * still recorded — the agent just never received either, which is the one outcome the contract exists + * to prevent. + * + * 8 s sits far enough under that ceiling to survive a slower host, and costs nothing: a human who is + * already looking at the screen answers inside it, and one who isn't was always going to arrive by + * ticket. Raising this back up trades a guaranteed reply for a slightly shorter happy path. */ -export const GATE_TIMEOUT_MS = 25_000 +export const GATE_TIMEOUT_MS = 8_000 /** One tool call currently awaiting this question. An agent that polls produces several over time. */ type Waiter = { diff --git a/traces/src/lib/webmcp/tools/blocking-tools.test.ts b/traces/src/lib/webmcp/tools/blocking-tools.test.ts index 64198e1..43c8833 100644 --- a/traces/src/lib/webmcp/tools/blocking-tools.test.ts +++ b/traces/src/lib/webmcp/tools/blocking-tools.test.ts @@ -15,7 +15,7 @@ import { snapshotFindingTool } from './snapshot-finding' * failure path come back as a sentence rather than a hang. * * Every blocking case here settles through the store — a human answering — rather than by waiting out - * GATE_TIMEOUT_MS, which is 25 seconds and would make this suite unusable. The timeout path itself is + * GATE_TIMEOUT_MS, which would add its full duration to every test. The timeout path itself is * already pinned in blocking.test.ts; what these tests add is that the *store* half is connected to it, * because a gate nobody can answer and a gate nobody is watching look identical from the agent's side. */