Skip to content

fix(bridges): replace wall-clock cap with idle timeout#34

Closed
replicas-connector[bot] wants to merge 2 commits into
mainfrom
fix/idle-timeout-no-wallclock
Closed

fix(bridges): replace wall-clock cap with idle timeout#34
replicas-connector[bot] wants to merge 2 commits into
mainfrom
fix/idle-timeout-no-wallclock

Conversation

@replicas-connector

Copy link
Copy Markdown
Contributor

Summary

You asked why have a wall-clock cap at all — fair. It was the wrong signal: fired regardless of whether the agent was stuck or just doing legitimate long work. Replacing with an idle timeout (no /history events for N minutes).

Changes (both bridges)

  • MAX_WATCH_DURATION_MSIDLE_TIMEOUT_MS (30 min idle)
  • Matrix: reuse existing lastEventAt (already tracked for the · idle Ns indicator)
  • TG: add lastFreshAt storage write whenever fresh.length > 0 on alarm tick
  • Failed-frame copy: "Agent went silent for N min — treating as stuck. Send a new message to start a fresh session."

Same cleanup behavior on timeout: DELETE upstream replica, flush KV mappings, setTerminal + deleteAll.

Behavior

  • Agent does 4 hours of multi-step ops with events landing every few seconds → keeps running, no cap.
  • Agent gets stuck waiting on a hung tool → after 30 min of silence, the bridge cleans up and tells the user to send a new message.

Validation

  • Matrix: 173/173 ✅ · typecheck clean · deployed 5419497d
  • Telegram: 99/99 ✅ · typecheck clean · deployed 18cfe9d0

Test plan

  • Typecheck + tests + deploy
  • Smoke: kick off a long agent task with continuous tool calls — confirm it runs past 60 min
  • Smoke: kill the agent process so events stop landing — confirm idle timeout fires after 30 min and emits the Failed frame

Created by Jaden (jadengarza@pm.me) with Replicas
Workspace  ·  Slack Thread

replicas-connector Bot and others added 2 commits May 30, 2026 18:56
Findings from Lark Admin room review: last turn there hit the 30-min
timeout while the agent was *actively* making progress through multi-
step MCP config cleanups. Session has 317 cumulative steps / 40 turns
— this is real heavy ops work, not a runaway loop.

Two fixes:

**#1 Bump MAX_WATCH_DURATION_MS from 30 → 60 min** (both bridges)

Lark Admin's "Watch timed out — agent ran longer than 30 min" was a
false-positive cap on legitimate work. 60 min keeps runaway-loop
protection while letting heavy ops finish.

**#2 Cancel upstream replica + flush KV on timeout**

When the watcher gave up at the cap, the upstream Replicas workspace
kept processing silently — burning credits indefinitely. The bridge
had no visibility into what state the replica ended up in.

On timeout we now:
- DELETE /v1/replica/{id} so the workspace stops
- Flush `room:{roomId}` / `replica:{id}` (or `chat:{id}:thread:{tid}`
  for TG) KV mappings so the next message in the room spawns fresh
- Then setTerminal + deleteAll as before
- Failed-frame message updated: "Send a new message to start a fresh
  session." so the user knows the room is recoverable

Matches the behavior we already had on claude-result 404/410 paths.

**Validation**
- Matrix: 173/173 ✅ · typecheck clean · deployed `1d6a1f2d`
- Telegram: 99/99 ✅ · typecheck clean · deployed `944a6aa3`

Co-Authored-By: itsablabla <itsablabla@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Jaden's question: why have a wall-clock cap at all? The cap was the
wrong signal — wall-clock fires regardless of whether the agent is
actually stuck or just doing legitimate long work. Lark Admin hit the
30-min cap mid-progress; bumping to 60 was a temporary patch.

The correct signal for "stuck" is *no /history events for N minutes*.
While the agent keeps emitting events (tool calls, thinking, text),
the watcher keeps watching — no matter how long. When events go
silent for IDLE_TIMEOUT_MS, the agent is actually stuck and we clean
up.

**Changes (both bridges):**

- `MAX_WATCH_DURATION_MS` → `IDLE_TIMEOUT_MS` (30 min idle)
- Matrix: reuse existing `lastEventAt` (already tracked for the idle
  indicator)
- TG: add `lastFreshAt` storage write whenever `fresh.length > 0` on
  the alarm tick
- Failed-frame copy: "Agent went silent for N min — treating as stuck.
  Send a new message to start a fresh session."

Same cleanup behavior on timeout: DELETE upstream replica, flush
KV mappings, setTerminal + deleteAll.

**Validation**
- Matrix: 173/173 ✅ · typecheck clean · deployed `5419497d`
- Telegram: 99/99 ✅ · typecheck clean · deployed `18cfe9d0`

Co-Authored-By: itsablabla <itsablabla@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@capy-ai

capy-ai Bot commented May 30, 2026

Copy link
Copy Markdown
Contributor

Capy auto-review is paused for this organization because the monthly auto-review limit has been reached. Increase the limit or turn it off in billing settings to resume automatic reviews.

@replicas-connector

Copy link
Copy Markdown
Contributor Author

Superseded by v2 branch (rebased on top of #33 to avoid conflicts)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants