* fix(agents): run heartbeat_prompt_contribution on harness prompt builds
Harness runtimes (e.g. the Codex app-server) assemble the prompt through
resolveAgentHarnessBeforePromptBuildResult rather than the embedded runner's
resolvePromptBuildHookResult. The harness helper ran before_prompt_build and
before_agent_start but never invoked heartbeat_prompt_contribution, so that hook
silently no-ops on those runtimes: plugins that contribute heartbeat context via
the documented hook get nothing on heartbeat turns.
Invoke heartbeat_prompt_contribution from the harness helper too, gated on
ctx.trigger === "heartbeat", merging its prepend/append context ahead of the
before_prompt_build / before_agent_start contributions (matching the embedded
path's ordering). before_prompt_build appendContext is already honored here, so
no change is needed for boot-style append contributions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(agents): preserve heartbeat hook ordering
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(exec): preserve turn-source routing target in approval followups for plugin channels
When an async exec approval is resolved and the originating session is
resumed, buildAgentFollowupArgs forwarded the turn-source to/accountId/threadId
only for built-in deliverable channels or gateway-internal channels. For an
external channel plugin whose channel is not in the in-process deliverable set,
the followup dispatched channel alone and dropped the recipient, so the resumed
agent reply routed to webchat instead of the originating channel.
Forward the turn-source routing fields whenever the resolved delivery target is
not used, matching how the channel itself is already preserved, so the gateway
can route the post-approval reply back to the originating channel.
Fixes#96103
* fix(exec): normalize followup thread routing
* fix(exec): normalize followup thread routing
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ports): route isPortBusy through checkPortInUse to catch IPv4-only occupants
* fix(ports): treat PortUsageStatus unknown as busy in isPortBusy
Per ClawSweeper review: checkPortInUse returns 'unknown' when every host
probe fails for a non-EADDRINUSE reason. Treating unknown as 'not busy'
could cause forceFreePortAndWait to exit before lsof/fuser inspects the
port. Conservative fix: only 'free' means not busy; everything else
(busy or unknown) triggers further inspection.
* fix(ports): reuse canonical multi-address probe
* fix(ports): reuse canonical multi-address probe
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* fix(doctor): ignore skipped local embedding probe
* fix(doctor): keep skipped local model diagnostics
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
The install-resolution path (fetchClawHubSkillInstallResolution) still read
ClawHub JSON with an unbounded response.json(), the one ClawHub JSON reader
left uncapped by the prior hardening. Route it through the existing
parseClawHubJsonBody helper so every ClawHub JSON success/structured-block
body is bounded by the same 16 MiB cap and cancels the stream on overflow.
Pure reuse of the helper introduced in this PR (no new abstraction); adds a
regression test that an oversized install-resolution body is rejected and the
underlying stream is cancelled.
ClawHub is an external marketplace (untrusted source); fetchJson read the
success body via response.json() and readErrorBody read the error body via
response.text(), both without a byte cap, so a hostile or malfunctioning host
could exhaust memory with an unbounded response. Read both through the existing
read-response-with-limit helpers (16 MiB cap for JSON, 8 KiB / 400 chars for the
error snippet), cancelling the stream on overflow/idle. Symmetric counterpart to
the Anthropic error-stream hardening in #95108.