Commit Graph
63185 Commits
Author SHA1 Message Date
e845a26fd6 fix(agents): fail fast with attributable reason after MCP stdio session dies mid-run (#98738)
* fix(agents): fail fast with attributable reason after MCP stdio session dies mid-run

Wires MCP Client onclose/onerror during bundle-mcp session creation so a
crashed/exited server flips session.connected instead of staying stale.
Next tool/resource/prompt call throws a domain-specific 'is disconnected'
error immediately instead of surfacing the SDK's generic 'Not connected'.
A disconnected reused session is retired and rebuilt fresh on the next
catalog pass rather than reused, since the SDK chains onclose/onerror
cumulatively on repeat connect() and the stdio transport never clears its
read buffer on an unexpected exit.

* fix(agents): retire a reused MCP session that dies mid-refresh, not just pre-refresh

codex review found: the catch-path retirement in getCatalog()'s per-server
task only covered two cases (fresh session that never connected this pass,
and a non-reused session that failed for any reason) - a reused session
that was healthy when this pass started but disconnects mid-refresh (child
process dies between ensureSessionConnected() returning and
listAllToolsBestEffort() finishing) hit neither branch, so onclose flipped
connected=false but the dead session object stayed in the map until the
next catalog rebuild happened to notice it. Added the missing branch plus
a regression test that kills the child process mid tools/list on a reused
session and asserts the session is purged from the map within the same
refresh (not just fail-fast on tool calls, which already worked).

* fix(agents): retire a dead reused MCP session even across overlapping catalog generations

codex round-2 review found: the mid-refresh retirement branch still
skipped when sharedWithNewerGeneration was true, which protects a
still-alive session another generation is actively using - but once
onclose flips session.connected=false, the transport is dead for every
generation sharing that object, so that guard no longer applies.
Simplified to a single !session.connected branch that always retires
(retireSessionIfCurrent already no-ops safely if a newer generation
replaced the map entry), and dropped the now-write-only
connectedForCatalog local it replaced.

* test(scripts): allow MCP SDK callback suppressions

* fix(agents): retire closed MCP sessions

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-01 22:55:15 -07:00
Vincent Koc ebb43e2dca test(ci): route cli reliability helper dependency 2026-07-01 22:53:32 -07:00
Ayaan ZaidiandGitHub a5bf55d690 refactor(agents): fold assistant string normalization into transcript ingest (#98908) 2026-07-01 22:44:02 -07:00
Ayaan Zaidi d02d8b1ea5 fix(agents): keep cli live retry artifacts
Fixes #98897
2026-07-01 22:39:24 -07:00
Ayaan Zaidi 52cabf877d fix(agents): recover cli context overflow sessions
Fixes #98897
2026-07-01 22:39:24 -07:00
Ayaan Zaidi b60803dfaa fix(auto-reply): persist heartbeat cli session bindings
Fixes #98895
2026-07-01 22:39:18 -07:00
Ayaan Zaidi 8939c10d77 fix(agents): handle claude live write timeout rejection
Fixes #98894
2026-07-01 22:39:18 -07:00
rabsef-bicrymandGitHub c8dba85b5d fix: keep Telegram outbound context on transcript time (#98769) 2026-07-01 22:15:10 -07:00
Gio Della-LiberaandGitHub b2c507c5f0 Doctor: expose tool result cap findings (#97500)
* doctor: expose tool result cap findings

* doctor: include inherited tool cap findings

* doctor: align tool cap agent paths
2026-07-01 21:49:59 -07:00
Omar ShahineandGitHub 8c7ac9b9b9 fix(imessage): native poll vote-cue, echo suppression, and same-sender comment fold
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.
2026-07-01 21:30:29 -07:00
Ayaan ZaidiandGitHub e095dc8409 fix(agents): normalize string tool-result replay (#98891) 2026-07-01 21:19:21 -07:00
6e79ca3cbc fix(fal): route grok-imagine and nano-banana-2-lite edits to correct endpoints (#98688)
* 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>
2026-07-01 20:42:25 -07:00
Ayaan Zaidi 6a96bbc7ad fix(telegram): keep timed-out webhook lanes guarded (#98806) 2026-07-01 20:33:38 -07:00
Ayaan Zaidi 2643c8cbfb fix(telegram): stop rate-limiting authenticated webhook traffic 2026-07-01 20:33:38 -07:00
Ayaan Zaidi ac10e6bff5 fix(telegram): close webhook and evicted probe transports 2026-07-01 20:33:38 -07:00
Ayaan Zaidi b23500a935 fix(telegram): retry transient webhook startup init errors 2026-07-01 20:33:38 -07:00
Ayaan Zaidi b7ec2851f3 fix(telegram): spool webhook updates durably before acking Telegram 2026-07-01 20:33:38 -07:00
Ayaan Zaidi 13613ecaf1 fix(telegram): align account inspection default routing 2026-07-01 20:29:18 -07:00
yetvalandAyaan Zaidi 85771aec05 fix(telegram): honor configured defaultAccount for omitted-account sends and actions
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.
2026-07-01 20:29:18 -07:00
Vincent Koc d310b786cf fix(ci): reset SwiftPM state between build retries 2026-07-01 20:26:18 -07:00
Vincent Koc 1f38396239 test(memory): align dreaming corpus promotion expectations 2026-07-01 20:26:18 -07:00
joshavant 8e95e56e2d chore(ios): generate release artifacts locally 2026-07-01 22:05:26 -05:00
Vincent Koc 4a2a97777e test(maturity): derive expected score from source 2026-07-01 19:51:15 -07:00
Vincent Koc 87178b8466 chore(config): refresh documentation baseline 2026-07-01 19:49:38 -07:00
Vincent Koc 1fcce9f1ee test(vercel-ai-gateway): complete guarded fetch fixtures 2026-07-01 19:49:38 -07:00
Vincent Koc 76db9a3376 fix(memory-wiki): preserve guarded collision handling 2026-07-01 19:49:38 -07:00
5ff247b99e test: prefer auto-cleaning temp dir helper (#93209)
Summary:
- The branch adds `useAutoCleanupTempDirTracker()`, broadens the temp-dir warning reporter to flag new manual helper imports/usages, updates docs, and migrates two script tests to the new helper.
- PR surface: Tests +301, Docs +1, Other +248. Total +550 across 8 files.
- Reproducibility: not applicable. this is test/tooling cleanup, and the changed behavior is exercised through helper/reporter tests and CI evidence rather than a user reproduction path.

Automerge notes:
- PR branch already contained follow-up commit before automerge: test: harden temp dir helper guard
- PR branch already contained follow-up commit before automerge: test: clarify auto cleanup temp dir helper name
- PR branch already contained follow-up commit before automerge: test: cover existing mkdtemp temp dir forms
- PR branch already contained follow-up commit before automerge: test: read staged temp helper source from index

Validation:
- ClawSweeper review passed for head 1fdd7d2a9a.
- Required merge gates passed before the squash merge.

Prepared head SHA: 1fdd7d2a9a
Review: https://github.com/openclaw/openclaw/pull/93209#issuecomment-4705653665

Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Approved-by: hxy91819
2026-07-02 02:46:28 +00:00
joshavant 7fa26e088d ci: remove security guard rollout floor 2026-07-01 21:21:31 -05:00
Josh AvantandGitHub 0cdce79221 docs: update mobile app release messaging (#98843) 2026-07-01 21:11:56 -05:00
Peter SteinbergerandGitHub 313560d5b9 feat(ios): modernize navigation and settings (#98811)
* feat(ios): modernize navigation and settings

* fix(ios): remove obsolete settings helpers

* test(ios): update i18n collector fixture

* test(ios): update i18n collector fixture

* test(ios): update i18n collector fixture
2026-07-02 02:43:49 +01:00
Vincent Koc cc76e06e7a test(plugins): align release fixtures with runtime contracts 2026-07-01 18:34:53 -07:00
joshavant 080481fec9 Require explicit iOS release versions 2026-07-01 20:24:38 -05:00
Peter SteinbergerandGitHub c1eee1a41a fix(ci): recover incomplete Swift build caches (#98818) 2026-07-02 02:23:08 +01:00
db6a3c2e87 fix(memory-wiki): retry transient existing-page reads in wiki_apply and chatgpt import (#98787)
* 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>
2026-07-01 18:19:49 -07:00
wangmiao0668000666andGitHub a7a444e7ef fix(nostr): clear per-relay publish timeout timer to prevent dangling handles (#98720) 2026-07-01 18:10:38 -07:00
Vincent Koc 38442853e1 fix(release): ignore HTML entities in note references 2026-07-01 18:07:08 -07:00
qingminlongandGitHub 40e02a418c fix(wizard): reject loose gateway port input (#98689)
* fix(wizard): reject loose gateway port input

* fix(wizard): reuse shared gateway port parser
2026-07-01 18:06:21 -07:00
Vincent Koc 1cbcff7c89 test(gateway): isolate live release agent state 2026-07-01 18:02:09 -07:00
Vincent Koc c0511692d8 test(plugins): repair prerelease validation fixtures 2026-07-01 18:02:09 -07:00
lsr911GitHubClaudeclawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
050e1f3b5c fix(discord): guard JSON.parse against malformed API response bodies (#97889)
* 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>
2026-07-01 17:50:01 -07:00
27104c1afb fix(codex): preserve app approvals in side forks (#98812)
Co-authored-by: Peter Steinberger <58493+steipete@users.noreply.github.com>
2026-07-02 01:42:28 +01:00
c49714f7b6 fix(agents): don't inject A2A turns into isolated-cron sessions_send (#92257) (#92283)
* fix(agents): don't inject A2A turns into isolated-cron sessions_send (#92257)

Fire-and-forget sessions_send (timeoutSeconds === 0) with announce
delivery runs the A2A ping-pong loop. For a cross-session send
(requester != target) the loop's first iteration feeds the target
agent's reply back into the requester session as a new turn. For a
normal requester that roundtrip is intended, but for an *isolated cron*
requester it injects reply context into the isolated run and causes an
agent feedback loop.

Narrow the fix to isolated-cron requesters only (detected by a session
key containing ":cron:" or channel "cron" -- the same signal used by
src/agents/subagent-registry.ts and set in
src/cron/isolated-agent/run.ts), NOT by timeoutSeconds. Gating on
timeoutSeconds was too broad: it disabled the intended ping-pong for
normal cross-session fire-and-forget sends.

Two hunks in src/agents/tools/sessions-send-tool.ts, both reusing one
`isIsolatedCronRequester` gate:

1. Force maxPingPongTurns to 0 in the runSessionsSendA2AFlow invocation
   only for an isolated-cron requester. The a2a flow's ping-pong guard
   (`maxPingPongTurns > 0`) then skips the requester-injection loop and
   proceeds straight to the announce step in the TARGET session,
   preserving fire-and-forget announce delivery. Normal requesters keep
   the configured turn count.

2. Read baselineReply for fire-and-forget sends when same-session (prior
   behavior) OR isolated-cron. Without a baseline fingerprint, a2a.ts
   would treat pre-existing assistant text in the target session (e.g.
   an unrelated concurrent cron's output) as "the reply" and
   misattribute it. The normal cross-session fire leg's history-call
   count is unchanged from origin/main. Read failures are tolerated so a
   snapshot error never blocks accepting the send.

Tests (sessions.test.ts): an isolated-cron cross-session fire-and-forget
forwards maxPingPongTurns: 0; a normal-requester regression guard
(discord:group:req / channel discord) forwards the configured turn count
(not 0), proving the normal ping-pong is preserved. a2a flow tests
(sessions-send-tool.a2a.test.ts): with turns=0 + requester!=target the
requester is never stepped but the target is still announced, and a
baseline-matching reply is neither injected nor announced.

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

* fix(agents): gate isolated-cron A2A on canonical cron-run classifier (#92257)

Address Codex [P2] review: replace the raw `:cron:` substring detector with
the canonical isCronRunSessionKey so a non-canonical cron-like requester key
(e.g. agent:main:slack:cron:job:run:uuid) keeps its intended cross-session
ping-pong. Keep the requesterChannel === "cron" arm (isolated cron runs set
that channel in src/cron/isolated-agent/run.ts). Add a regression covering the
non-canonical key.

* refactor(agents): drop dead cron-channel A2A arm, gate on canonical key only (#92257)

The requesterChannel === "cron" arm was unreachable: agentChannel is always a
DeliverableMessageChannel from resolveGatewayMessageChannel(messageProvider),
never the literal "cron". The channel: "cron" in src/cron/isolated-agent/run.ts
labels diagnostics events/lifecycle, not the tool channel. Gate on
isCronRunSessionKey alone and fix the misleading comment.

Tests drove the dead arm via agentChannel/requesterChannel "cron" plus a
non-canonical key (agent:main:cron:run:abc, which isCronRunSessionKey rejects).
Switch them to a canonical cron-run key (agent:main:cron:job:run:abc) and a
normal delivery channel so they exercise the real production gate.

* fix(agents): align cron A2A fallback baselines

* chore: prepare branch refresh

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-01 17:34:26 -07:00
zhangLei99586andGitHub 273729bd95 fix(browser): guard setDeep against empty keys array (#98138)
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.
2026-07-01 17:29:56 -07:00
Ayaan Zaidi 3558391a75 docs(telegram): move maintainer decisions into scoped AGENTS.md 2026-07-01 17:19:16 -07:00
joshavant 6ad2626cfd fix(ios): generate light app store screenshots only 2026-07-01 19:16:23 -05:00
Masato HoshinoandGitHub 8e28e88387 fix(gateway): distinguish reachable gateway from failed status probe (#98183)
* fix(gateway): distinguish reachable gateway from failed status probe

* fix(status): gate owns-port RPC recovery guidance on no stale gateway PIDs

inspectGatewayRestart can set health.healthy from bare reachability after
ownership attribution failed, while still returning a non-empty
staleGatewayPids. Printing owns-port recovery guidance in that case
contradicted the dedicated stale-PID diagnostic below it. Require
staleGatewayPids to be empty before treating healthy as owns-port proof.
2026-07-01 17:04:31 -07:00
Vincent Koc dbf837f574 docs(changelog): refresh 2026.7.1 notes 2026-07-01 16:58:44 -07:00
Peter SteinbergerandGitHub 3e50f41dd6 improve(ios): simplify Talk controls and composer alignment (#98736)
* refactor(ios): streamline talk and composer controls

* test(ios): restore Talk control state

* chore(ios): refresh native i18n inventory
2026-07-02 00:48:37 +01:00
Peter SteinbergerandGitHub 17482a4026 fix(ci): restore docs and test type checks (#98792) 2026-07-02 00:36:52 +01:00
Gio Della-LiberaandGitHub cdaafe198c doctor: expose channel plugin blocker findings (#97496) 2026-07-01 16:31:52 -07:00