* fix(interactive): preserve button command values in fallback text for degraded approval UX
* fix(interactive): keep callback values private in fallback text and narrow Feishu interactive detection
- P1: Skip rendering action.type === "callback" values in
renderMessagePresentationFallbackText to avoid leaking opaque
channel/plugin data into user-visible text. Command and legacy
values are still rendered.
- P2: Replace hasMessagePresentationBlocks/hasInteractiveReplyBlocks
with isMessagePresentationInteractiveBlock so Feishu comment
guidance only appears when the presentation actually contains
buttons or selects, not for text-only blocks.
- Update tests: callback button now shows label-only; all 137 tests pass.
* fix(interactive): only render typed command values in fallback text, keep legacy value private
* fix(feishu): gate document-comment command guidance on actual command action
* docs(message-presentation): document command/callback value fallback visibility
* fix(feishu): omit command guidance when URL overrides fallback command text
* docs: regenerate docs_map.md
* fix(interactive): exclude disabled buttons from fallback command rendering and guidance
* fix(interactive): extract hasRenderedCommandAction, exclude disabled buttons from command fallback
* fix(feishu): preserve command guidance marker through core presentation rendering
* fix(feishu): type-narrow channelData.feishu with isRecord before reading rendered-command marker
* fix(feishu): move hasRenderedCommandAction from public SDK into Feishu plugin as local helper
Keep the helper local to the only caller (Feishu outbound) instead of
adding a new public plugin SDK API contract. The shared fallback renderer
in renderMessagePresentationFallbackText already inlines the same
command-visibility logic; a local helper is sufficient for the Feishu
comment-thread guidance gate.
* refactor(feishu): tighten fallback command marker
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(anthropic): restore Fable 5 Vertex simple completions
* test(agents): satisfy custom API model types
* test(ci): route reliability test from temp helper
* test(agents): satisfy custom API model types
Makes native iMessage polls behave correctly end to end.
What changed:
- Inbound polls render with a numbered-options vote cue so the agent casts a native vote instead of answering the poll in prose.
- poll-vote resolves the poll reference from pollId/pollGuid/messageId and now defaults to the current inbound poll message when the model omits it; still errors when no reference exists.
- poll-vote echo suppression is session-scoped, so the redundant spoken answer is dropped across the separate poll and comment runs.
- A poll's inline-reply caption is folded (not delivered as a standalone question) only when the poll creator and reply sender are both known and equal; unknown/mismatched sender falls through to the normal inbound decision gate, so no inbound reply is silently dropped.
Evidence:
- 64 passing tests in the poll suites (poll-comment, poll-render, actions), incl. sender fail-closed regressions; pnpm build clean.
- Two Codex autoreviews clean (patch is correct); ClawSweeper re-review rated it platinum hermit.
- Live-verified on macOS 26.4.1 on the deployed gateway: poll "What color pill?" -> native vote delivered with a 7333-byte payload, caption folded, zero echo.
Note: vote delivery also depends on the imsg vote-stamp fix (openclaw/imsg#150); OpenClaw ships ahead of imsg per owner decision.
* fix(fal): route grok-imagine and nano-banana-2-lite edits to correct endpoints
The fal image-generation provider appends '/image-to-image' to any model
that isn't 'openai/gpt-image-*' or 'fal-ai/nano-banana-*' when reference
images are supplied. That's wrong for two models fal serves:
- `xai/grok-imagine-image`: fal 404s on '/image-to-image'. The real edit
endpoint is '/quality/edit'. The endpoint also expects lowercase
resolution values ('1k'/'2k' only) and a distinct aspect_ratio enum.
- `google/nano-banana-2-lite`: fal 404s on '/image-to-image'. The real
edit endpoint is '/edit'. The endpoint does not accept a 'resolution'
parameter.
Add schema entries for both models so ensureFalModelPath and
applyFalImageGeometry pick the right suffix and body shape. Introduce
resolution allowlist support ('resolutions: readonly string[]') and
lowercase transform ('resolutionCase: "lower"') on the schema; existing
schemas keep their behaviour (nb2 still forwards uppercase resolution
unchanged; flux/gpt-image/nb2/krea untouched). Refactor
ensureFalModelPath to consult schema.appendEditPath instead of hardcoded
prefix checks so future models only need a schema entry.
Tested:
- Existing 49 fal unit tests still pass; added 9 new tests covering the
two new endpoints and their guard conditions (32 -> 34 tests in the
image-generation-provider suite).
- Live fal.ai calls confirm both endpoints return 200 with real
reference images; the buggy old URLs still return 404.
* fix(fal): preserve standard edit routing
* fix(image): apply inferred resolution per model
* fix(image): preserve provider reference limits
* fix(image): resolve reference limits per model
* fix(fal): preserve nano banana family limits
* test(ios): stub generated file list helper
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
When no accountId was provided, resolveTelegramAccount and resolveTelegramToken
resolved the implicit default account first. Any leftover TELEGRAM_BOT_TOKEN or
top-level channels.telegram.botToken then shadowed the configured
channels.telegram.defaultAccount and default-agent binding, so gateway sends,
heartbeats, and message actions went out through the wrong bot and typically
failed with chat not found in groups the stale bot never joined. Message
actions could also fail with a missing-token error when only the named default
account had a token.
Resolve the configured default account id up front in both optional-account
entry points, matching the nextcloud-talk resolver and createTelegramActionGate.
* fix(memory-wiki): retry transient existing-page reads in wiki_apply and chatgpt import
A create_synthesis re-run and a ChatGPT conversations re-import swallowed
every existing-page read error and treated the page as brand-new, so one
transient read failure silently emptied the user's ## Notes block and
dropped hand-added frontmatter. Route both reads through a retry-once
helper that treats only a missing page as new and propagates persistent
failures, matching the ingest and imported-source fix from #98360.
* fix(memory-wiki): preserve fs-safe policy failures
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(discord): guard JSON.parse against malformed API response bodies
Wrap JSON.parse(text) in requestDiscord with try/catch to prevent a malformed Discord API response body from throwing an unhandled SyntaxError and crashing the process. On parse failure, throw a DiscordApiError with a descriptive message so the caller can handle it gracefully.
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: lsr911 <liao.shirong@xydigit.com>
* fix(clawsweeper): address review for automerge-openclaw-openclaw-97889 (validation-1)
* chore: trigger CI re-run for flaky checks-node-compact-small-whole-2
Signed-off-by: lsr911 <liao.shirong@xydigit.com>
* chore: trigger CI re-run for flaky runtime-inventory-drift-check
Signed-off-by: lsr911 <liao.shirong@xydigit.com>
* chore: trigger CI re-run for flaky checks-node-compact-large-whole-1
Signed-off-by: lsr911 <liao.shirong@xydigit.com>
* fix(discord): guard JSON.parse against malformed API response bodies
* fix(discord): guard JSON.parse against malformed API response bodies
---------
Signed-off-by: lsr911 <liao.shirong@xydigit.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
When keys is empty, keys[keys.length - 1] returns undefined, and the
previous ?? fallback would silently create a property with an empty
string key on the target object. Add an early return so empty keys are
a no-op instead of silently polluting the object.
Review on #98102: the durable-retry classifier treated every MediaFetchError
`fetch_failed` as retryable, but `fetch_failed` also covers permanent failures
(SSRF/guard denials, local Bot API path/read errors). On spooled replay those
would requeue forever with the user-facing warning suppressed.
Move the policy to its owner: add `isDurablyRetryableMediaFetchError` to
`src/media/fetch.ts`, defined as `shouldRetryMediaFetch` (the canonical in-loop
transient policy) plus shutdown/abort `fetch_failed`. A restart-window abort is
the primary inbound-media loss vector and is recoverable on replay, whereas
in-loop retry mid-shutdown is futile; permanent `fetch_failed`, other 4xx, and
size limits stay non-retryable so they cannot loop in the spool.
Telegram reuses the shared helper instead of a local classifier
(`isAbortError`/`isTransientNetworkError` live in core; the classification must
not drift). Adds permanent-`fetch_failed` regression coverage at the unit and
behavioral level. Media-group partial delivery (#55216) unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A single-message inbound media fetch failure was handled best-effort: warn
and let the update complete, so bot-core acked the spooled update. During a
restart window the warning send also failed and the document was silently
lost with no durable retry.
Classify transient media fetch failures (network/abort fetch_failed and
408/429/5xx HTTP) and record them as failed-retryable, so spooled-replay
updates are kept and re-driven by the ingress spool with backoff instead of
acked. Live updates still ack and warn; permanent failures (size limit, 4xx)
stay best-effort. The retry notice is suppressed on spooled replay to avoid
repeating it on each replay.
The durable-retry classifier deliberately diverges from media/fetch.ts
shouldRetryMediaFetch on shutdown aborts: in-loop retry of a shutdown abort
is futile, but durable re-spool must retry it -- that abort is the primary
restart-window loss vector. Media-group partial delivery (#55216) is left
unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>