Files
openclaw/src/agents
xydt-tanshanshan eb2f763674 fix(agents): add tool-activity heartbeat to keep subagent alive during tool calls (#95536)
* [AI] fix(agents): add run-scoped tool-activity heartbeat to prevent premature subagent idle timeout

Subagent LLM idle watchdog tripped during long-running tool calls because
tool completions did not reset the idle timer.

Implement a per-run tool-activity heartbeat:
1. notifyToolActivity(runId) — published from embedded-runner tool
   execution wrappers (attempt.ts) whenever a tool completes
2. onToolActivity(runId, listener) — consumed by streamWithIdleTimeout
   to reset the idle watchdog on tool completion
3. getLastToolActivityMs(runId) — shared timestamp Map so pre-stream
   activity is visible to armTimer before the first stream iteration
4. clearToolActivityRun(runId) — wired into the run lifecycle finally
   block to clean up listener sets and timestamps when the run exits

Scoped per-run via runId-keyed Maps to prevent concurrent runs from
resetting each other's idle watchdogs.

Related to #94124

* [AI] fix(agents): copy plugin/channel/before-tool-call metadata onto heartbeat tool wrappers

* [AI] test(agents): add metadata preservation regression tests for heartbeat tool wrapper

* [AI] fix(agents): preserve terminal presentation metadata on heartbeat tool wrapper

* [AI] chore: update test comment for four metadata copy calls

* fix: add periodic heartbeat during tool execution

Fire notifyToolActivity at tool start and every 60s via setInterval
so long-running tools survive the 120s LLM idle watchdog.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: remove useless catch clause (no-useless-catch)

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: preserve Code Mode control-tool identity through heartbeat wrapper

Adds copyCodeModeControlToolIdentity() to replicate the WeakSet
membership that markCodeModeControlTool() sets. Without this,
the heartbeat wrapper drops Code Mode identity for exec/wait
tools, weakening before_tool_call policy and approval handling.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: wrap deferred directory tools with heartbeat

resolveDeferredTool hydrates hidden catalog tools outside the
effectiveTools.map() wrapper, so they bypassed the periodic
notifyToolActivity heartbeat. Wrap the hydrated execute function
with the same start/interval/complete notification pattern.

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-11 17:50:33 +08:00
..