* fix(googlechat): keep accounts.default streaming when doctor migrates named accounts
* fix(googlechat): canonicalize migrated streaming
Replace the layered doctor migration with canonical root/default/account materialization and prove runtime resolution matches the stored config.\n\nCo-authored-by: tiffanychum <71036662+tiffanychum@users.noreply.github.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(line): retry inbound media through the ingress drain on transient download failure
* fix(line): retry all transient inbound media failures
Co-authored-by: 許元豪 <146086744+edenfunf@users.noreply.github.com>
* style(line): format media retry test
* test(line): use scanner-safe quote token
* fix(line): narrow durable media retries
* fix(line): preserve status after body cleanup
* chore(changelog): avoid concurrent entry conflict
* style(line): format retry helper signature
* test(line): assert rejected media chunk
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Model-call spans wrap outbound provider inference requests, so classify them as CLIENT independently of the optional GenAI naming convention. Generic tool spans remain unchanged because their shared events can represent local work.
Release note: operators filtering dashboards or alerts on span kind will see model-call spans move from INTERNAL to CLIENT.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(litellm): guard loopback hostname auto-allow with isIP to prevent DNS bypass
The isAutoAllowedLitellmHostname helper auto-enables private-network access
for loopback-style hosts. Before this fix, lowered.startsWith("127.")
matched DNS hostnames like 127.evil.com, letting remote endpoints bypass
the explicit allowPrivateNetwork opt-in — a SSRF risk.
Add isIP(host)===4 guard so only literal IPv4 loopback addresses qualify.
Same canonical pattern as extensions/slack/src/monitor/relay-source.ts:271
and the codex loopback fix.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(litellm): cover loopback endpoint policy
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(imessage): keep fast progress out of chat
* test(imessage): require fast progress callback
---------
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
* fix(whatsapp): evaluate mention patterns when other members are @-mentioned
isBotMentionedFromTargets returned false as soon as a group message
carried native @-mentions that did not include the bot, so the
configured mentionPatterns were never evaluated and a message like
'marlow, look at @SomeoneElse's message' was silently dropped by the
requireMention gate (#109488).
Fall through to the configured text mentionPatterns when the native
@-mention JIDs do not include the bot. The loose self-number digit
fallback stays suppressed in that shape: an @-tag of another member
injects that member's number into the body, so substring digit
matching is unreliable there.
Fixes#109488
* refactor(whatsapp): unify mention pattern gating
---------
Co-authored-by: Marcus Castro <mcaxtr@openclaw.ai>
* fix(thread-ownership): bound 409 response read and preserve cancel semantics
The thread-ownership 409 conflict path still used unbounded resp.json(),
inconsistent with the bounded readers shipped across the other channel/provider
success paths. A compromised or buggy forwarder returning a multi-GB 409 body
would buffer the entire body before JSON.parse, OOMing the agent.
Bound the 409 body via readResponseTextLimited (8 KiB cap; a 409 owner field is
~30 bytes). Wrap the parse in try/catch so a truncated or malformed 409 body
still cancels the send (owner logged as "unknown") — the 409 status itself
means another agent owns this thread, independent of body parseability.
The discord webhook and huggingface /v1/models success reads from the original
PR were superseded upstream by #98098 and #101079 (readProviderJsonResponse),
so this branch now carries only the thread-ownership fix.
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(thread-ownership): bound forwarder conflict handling
---------
Co-authored-by: Claude <noreply@anthropic.com>
* fix(reef): contain startup friend reconcile failures
The periodic reconcile treats a transient relay failure as non-fatal, but
the startup reconcile was a bare await outside the lifecycle. A relay 429
there rejects startAccount, and the supervisor restart that follows is
itself what escalates relay rate limiting -- so Reef gives up permanently
after MAX_RESTARTS while the relay is still throttling.
Move the startup reconcile into runReefChannelLifecycle so both paths
share one failure policy, and activate through an onReady hook to preserve
the existing ordering: refresh peer keys, activate, then start the inbox.
* fix(reef): harden reconcile recovery
Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>
* docs(changelog): credit Reef reconcile fix
* test(reef): satisfy exact-head validation
* test(reef): satisfy lint contract
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(discord): reopening a long code fence overflows maxChars
When a chunk is flushed mid code-fence, the next chunk reopens the fence
with the full original opening line (info string included), but the
reserve accounting only budgets the closing marker. A long fence opening
line therefore pushed reopened continuation chunks past maxChars, which
Discord rejects with HTTP 400.
Reopen continuation chunks with a bare marker when the full opening line
would not leave room for the close and body, and size split segments
against the reopen prefix so no chunk exceeds maxChars. This mirrors the
existing closing-fence-overflow guard on the opening/reopen side.
* fix(discord): preserve code after fence reopen
Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>
* fix(discord): preserve limits when fences cannot fit
Co-authored-by: Yigtwxx <yigiterdogan023@gmail.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(zai): keep probe deadline through stalled error-body reads
Fixes an issue where Z.AI endpoint detection could hang indefinitely
when a probe returned error headers and then stalled on the error
response body. The previous helper cleared the AbortSignal as soon as
fetch resolved headers, so a never-chunking error body bypassed the
probe deadline.
Keep one AbortController deadline across the probe request and the
bounded error-body read, and pass a clamped chunkTimeoutMs into
readResponseWithLimit so a stalled error body fails closed inside the
remaining budget. The chunkTimeoutMs is clamped to the remaining
deadline after fetch returns headers, so the idle timeout does not
outlast the overall probe deadline.
Tighten the stalled-body test timing assertion from 5s to 2x timeoutMs
to verify the deadline is actually respected.
* fix(zai): enforce probe body deadline
Co-authored-by: NIO <noreply@github.com>
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: NIO <noreply@github.com>
Avoid buffering unbounded provider error streams and cover the resource lifecycle with a deterministic cancellation contract.
Co-authored-by: Peter Steinberger <steipete@gmail.com>