molecule-core/workspace-server
Hongming Wang bf1dc6b6a5 feat(platform): idle-based A2A timeout, drop 5-min canvas hardcode
The previous canvas-default 5-min absolute deadline pre-empted any
chat that legitimately ran longer (multi-turn tool use, large
synthesis tasks) and made every wedged-SDK call burn 5 full minutes
before the user saw anything. Replaced with a per-dispatch idle
timeout: cancel the request only when the broadcaster has been
silent for `idleTimeoutDuration` (60s). Any progress event for the
workspace — agent_log tool-use rows, task_update, a2a_send,
a2a_receive — resets the clock.

Mechanics:

- new applyIdleTimeout helper subscribes to events.Broadcaster's
  per-workspace SSE channel, drains its messages, resets a
  time.Timer on each one, cancels the wrapped ctx when the timer
  fires. Cleanup goroutine + subscription lives only as long as
  the returned cancel func is uncalled.
- dispatchA2A now takes workspaceID as a parameter, applies the
  idle timeout always (canvas + agent), and combines its cancel
  with the existing 30-min agent-to-agent ceiling cancel into one
  func the caller defers.
- Canvas dispatches no longer have an absolute ceiling at all —
  the idle timer is the only "give up" signal. A healthy chat
  reporting tool-use telemetry every few seconds runs forever;
  a wedged runtime fails in 60s instead of 5 min.
- isUpstreamBusyError now also recognises context.Canceled (the
  error class our idle cancel produces, distinct from
  DeadlineExceeded). Same 503-busy retry semantics.

Tests:

- TestApplyIdleTimeout_FiresOnSilence — 60ms idle, no events,
  ctx cancels with context.Canceled.
- TestApplyIdleTimeout_ResetsOnEvent — event mid-window extends
  the deadline; ctx alive past original deadline, then cancels
  on the second silence window.
- TestApplyIdleTimeout_NilBroadcasterDegradesGracefully — defensive
  no-op for paths that don't wire a broadcaster.
- 3 existing dispatchA2A tests updated for the new workspaceID
  param + the always-non-nil cancel return shape.

This pairs with Piece 1's per-tool-use telemetry (166c7f77): the
broadcaster events that reset the idle timer ARE the agent_log
rows the workspace started emitting per tool call. So the same
event stream feeds both the chat progress feed AND the proxy's
deadline.

Full Go test suite passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 08:34:55 -07:00
..
cmd/server fix(canvas,dotenv): review-driven hardening of fit gate + parser parity 2026-04-24 22:23:51 -07:00
internal feat(platform): idle-based A2A timeout, drop 5-min canvas hardcode 2026-04-25 08:34:55 -07:00
migrations feat(platform,workspace): SDK-wedge detection + workspace_status ENUM 2026-04-25 00:59:15 -07:00
pkg/provisionhook feat(#1957): wire gh-identity plugin into workspace-server 2026-04-24 15:01:41 +00:00
.ci-force chore: force Platform(Go) CI run on main — validate go vet clean 2026-04-21 15:43:19 +00:00
.gitignore feat(ws-server): pull env from CP on startup 2026-04-19 02:41:15 -07:00
.golangci.yaml chore(workspace-server): add golangci.yaml disabling errcheck 2026-04-24 07:16:54 +00:00
Dockerfile chore: extract ContextMenu Zustand fix + a2a_proxy local-docker SSRF bypass + workspace-server Dockerfile GID entrypoint 2026-04-22 20:00:16 -07:00
Dockerfile.tenant feat(terminal): remote path via aws ec2-instance-connect + pty 2026-04-21 18:13:29 -07:00
entrypoint-tenant.sh fix(security): add USER directive before ENTRYPOINT in all tenant images (#1155) 2026-04-20 23:51:33 +00:00
go.mod feat(#1957): wire gh-identity plugin into workspace-server 2026-04-24 15:01:41 +00:00
go.sum feat(#1957): wire gh-identity plugin into workspace-server 2026-04-24 18:28:18 +00:00