Summary
A Workspace chat turn is hard-capped at 300 seconds and dies there. The cap is a module constant with no setting, no env var and no per-agent override — and it silently overrides the agent's own configured execution timeout, which defaults to 3600s and is operator-settable from 60–7200s.
Context
Reported from live use of /workspace: longer turns die after ~5 minutes and there is no way to regulate it.
src/backend/client_portal/service.py:
PORTAL_TURN_TIMEOUT_SECONDS = 300
That constant is passed verbatim as timeout_seconds= into run_resumable_turn for every Workspace turn. Meanwhile the platform already has a first-class per-agent bound — agent_ownership.execution_timeout_seconds (TIMEOUT-001, default 3600s, GET/PUT /api/agents/{name}/timeout, range 60–7200) — which the Workspace path never consults. So an operator who deliberately raised an agent's timeout to an hour still gets 5 minutes in the surface people actually work in, and nothing tells them why.
Two things to keep intact while changing the number, both load-bearing and both documented in that file:
Raising the default alone is not the whole fix — an operator with a long-running agent needs the bound to follow that agent, and a client watching a 20-minute turn needs the surface to stay honest about it rather than presenting a dead spinner.
Acceptance Criteria
Technical Notes
Summary
A Workspace chat turn is hard-capped at 300 seconds and dies there. The cap is a module constant with no setting, no env var and no per-agent override — and it silently overrides the agent's own configured execution timeout, which defaults to 3600s and is operator-settable from 60–7200s.
Context
Reported from live use of
/workspace: longer turns die after ~5 minutes and there is no way to regulate it.src/backend/client_portal/service.py:That constant is passed verbatim as
timeout_seconds=intorun_resumable_turnfor every Workspace turn. Meanwhile the platform already has a first-class per-agent bound —agent_ownership.execution_timeout_seconds(TIMEOUT-001, default 3600s,GET/PUT /api/agents/{name}/timeout, range 60–7200) — which the Workspace path never consults. So an operator who deliberately raised an agent's timeout to an hour still gets 5 minutes in the surface people actually work in, and nothing tells them why.Two things to keep intact while changing the number, both load-bearing and both documented in that file:
PORTAL_ATTEMPT_CEILING_SECONDS= turn timeout + 10 (HTTP slack) + 300 (the Async chat_with_agent: long execution silently fails with null response (reader-thread) #678 reader-race auto-retry's second call), andPORTAL_MAX_TURN_SECONDS= 2 × attempt ceiling + 60 (a resume whose JSONL is gone re-runs the whole turn cold).PORTAL_INFLIGHT_TTL_SECONDSis sized off that. Raising the turn timeout without these moving reintroduces the feat(workspace): absorb the Session surface, with continuity parity (ent#358) #2120/bug(workspace): awaitPersistedReply can poll for an hour when the in-flight marker is orphaned #2133 failure: the in-flight marker expires while a live, already-billed turn is still running, and the client is told nothing is running.REPLY_MAX_WAIT_MS_FALLBACKinsrc/frontend/src/components/portal/portalUtils.js), so the client must not keep waiting on a shorter or longer clock than the server enforces.Raising the default alone is not the whole fix — an operator with a long-running agent needs the bound to follow that agent, and a client watching a 20-minute turn needs the surface to stay honest about it rather than presenting a dead spinner.
Acceptance Criteria
execution_timeout_secondsrather than a constant that silently overrides it (clamped to a sane Workspace ceiling if one is wanted)PORTAL_ATTEMPT_CEILING_SECONDS,PORTAL_MAX_TURN_SECONDSandPORTAL_INFLIGHT_TTL_SECONDSstay derived from the turn bound — no constant left sized against the old 300stests/unit/test_2133_*pins (or their successors) still hold against the real constants.env-only — with a named validation error on out-of-range inputTechnical Notes
src/backend/client_portal/service.py(PORTAL_TURN_TIMEOUT_SECONDSand the derived ceiling/marker constants; therun_resumable_turncall site),src/backend/services/session_turn_service.pyagent_ownership.execution_timeout_seconds(TIMEOUT-001, feat: increase default chat execution timeout from 15m to 60m #665),GET/PUT /api/agents/{name}/timeout, and the bug/design: schedule vs agent timeout precedence is silent + SIGKILL error message is ambiguous #929 rule that a schedule'stimeout_secondsmay not exceed the agent cap — the same relationship applies heresrc/frontend/src/components/portal/portalUtils.js(REPLY_MAX_WAIT_MS_FALLBACK,PORTAL_ATTEMPT_CEILING_S)