Commit Graph

41068 Commits

Author SHA1 Message Date
Dallin Romney 7df05d5fc0 refactor(qa): remove live-channel scenario registries and wrappers (#115752)
* refactor(qa): remove duplicate channel scenario registries

* refactor(qa): route live scenarios through shared runners

* fix(qa): isolate WhatsApp driver retries

* refactor(qa): add module export flow arguments

* test(qa): include scenario metadata in matrix fixture
2026-07-30 13:56:34 +08:00
Peter Steinberger ea42a4612a fix(plugins): plugin install survives pnpm-only overrides (#116245)
* fix(plugins): preserve compatible npm overrides on retry

* fix(plugins): keep npm compatibility type internal

* fix(plugins): bound managed override retries
2026-07-29 22:43:34 -07:00
Vincent Koc 814d1fdf1a fix(test): review Codex runtime scan chunks (#116212)
* fix(test): review Codex runtime scan chunks

* fix(test): count reviewed scan findings
2026-07-30 13:02:57 +08:00
Miorbnli 5e1e70e423 fix(systemd): clear start-limit latch before gateway start (#116178)
* fix(systemd): clear start-limit latch before gateway start

systemd latches a unit into failed/start-limit-hit after a crash loop,
then refuses systemctl start until reset-failed clears the latch. The
reset-failed step only ran for the restart action, so the natural
recovery command `openclaw gateway start` failed on a crash-looped
gateway while `openclaw gateway restart` recovered it.

Run reset-failed before both start and restart (stop is unchanged),
mirroring launchd's startLaunchAgent which calls launchctl enable before
kickstart. reset-failed is idempotent and a no-op on a healthy unit.

* test(systemd): cover start-limit latch clearing on system-scope start

Mirrors the existing system-scope restart test: when startSystemdService
targets a system unit as root, it must emit reset-failed before start so
a crash-looped gateway recovers via `openclaw gateway start` too.

* test(systemd): assert healthy start tolerates reset-failed no-op

reset-failed is idempotent on a non-failed unit (systemd returns success
without changing unit state), so prepending it to start cannot regress a
healthy gateway. Covers the merge-risk concern that every start now
depends on an extra reset-failed call.

* test(systemd): cover reset failure and stop controls

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 12:47:56 +08:00
Vincent Koc 2899323fe3 fix(security): expose every executable call in plugin scans (#116222)
* fix(security): report every scanner line match

* fix(security): bound scanner match evidence
2026-07-30 12:45:25 +08:00
Ayaan Zaidi c63241d3ce fix(channels): resolve tool progress against the caller's stream mode
resolveChannelStreamingPreviewToolProgress guessed a mode when streaming.mode
was unset, so it could never be right for every channel: guessing "partial"
dropped Discord and Telegram's explicit progress.toolProgress opt-out, and my
earlier switch to "progress" broke it the other way for Slack, Mattermost, and
Teams, whose real default is partial. With both keys configured and disagreeing,
the wrong one won.

The helper has no channel identity, so it stops guessing and takes the mode the
caller already resolved. All nine call sites pass theirs. Omitting it keeps the
previous configured-mode reading, so untouched callers behave exactly as before.

Reported by ClawSweeper on #116143.
2026-07-30 13:36:21 +09:00
Ayaan Zaidi c6633aead5 refactor(channels): render the progress label as a block, not a line
The label was smuggled into the rolling line array as a `{draftLabel}`
sentinel so it would participate in the maxLines window, then special-cased
in three places plus an index lookup to pull it back out for its own block.

A label is not a line; it is a block that yields its slot once real work
fills the window. Saying that directly removes the sentinel, every
`isLabelLine` check, and the double map over the line list, while keeping the
documented scroll-away behavior and the plan/tool budget split.

Adds the missing boundary test for a checklist that consumes the whole line
budget: `slice(-0)` returns every element, so that case needs the explicit
empty-window branch rather than falling out of the arithmetic.
2026-07-30 13:36:21 +09:00
Ayaan Zaidi 06aa81a73f fix(channels): show tool lines under the progress status headline
A status headline replaced the rolling tool lines instead of sitting above
them, so a default Discord draft showed one preamble sentence for an entire
tool-heavy turn. Operators reached for `/verbose` to see any activity, which
delivers durable per-tool-call messages and floods the channel.

- Render the headline above the lines; both stay visible in one message.
- Shorten the start gate from 5s to 1.5s. The gate only creates the draft
  when the timer fires and finalize cancels it, so quick answers still post
  no draft while a 3s tool turn stops being silent.
- Drop Discord's label-gated tool-progress default so
  resolveChannelStreamingPreviewToolProgress is the single owner. An explicit
  `toolProgress: false` still silences the lines.
- Resolve that toggle against a "progress" mode guess when `streaming.mode`
  is unset, so the progress-draft channels stop ignoring an explicit
  `progress.toolProgress` opt-out.

Telegram now defaults to `streaming.mode: "progress"` like Discord; set
`"partial"` to keep streamed answer text. Its renderer draws work lines from
the compositor's structured lines, so `rendersRollingLinesNatively` keeps
them out of the composed text rather than printing every line twice.
2026-07-30 13:36:21 +09:00
harish ganeshmurthy 5496cff965 feat(cron): include run time in failure alerts (#80246)
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: haishmg <4529977+haishmg@users.noreply.github.com>
2026-07-30 12:25:10 +08:00
Vincent Koc b2136fa234 fix(test): serialize transcript mirror dirty-state setup (#116207) 2026-07-30 12:12:55 +08:00
Eden 9c6e06b557 fix(auto-reply): deliver ingress-retried messages after their queued run is dropped (#115891)
* fix(auto-reply): deliver ingress-retried messages after their queued run is dropped

* fix(auto-reply): key queued dedupe release by adoption lifecycle

Overflow-summary compaction clones a queued run onto a new object, so a
release keyed by the run object missed runs completed via their clone and
the ingress retry stayed suppressed on the summarize drop policy. The
adoption lifecycle reference survives cloning and already keys admission
state, so it is the identity the release must use.

* fix(auto-reply): guard queued dedupe ownership

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 11:19:01 +08:00
wangmiao0668000666 b942db4d56 fix(agents): throw CompactionError when summarization fails (#115500)
* fix(agents): throw CompactionError when summarization fails

Transform compaction summarization from silent infinite failure loops
into clear, actionable errors.

Previously, when all summarization attempts failed, summarizeWithFallbackResult
returned a generic fallback string blaming "size limits". This caused:
- Misleading error messages (auth/network errors reported as "size limits")
- Callers unable to distinguish real summaries from failures
- Silent infinite retry loops where token counts climbed indefinitely

Now, CompactionError is thrown with the original error details preserved.
Callers immediately know compaction failed and can take appropriate action
(switch model, notify user, etc.).

Fixes #115413

Co-Authored-By: nebulacoder-v8.0 <noreply@zte.com.cn>

* fixup: export CompactionError from agent-core index, remove dead code

* fixup: address ClawSweeper review - keep CompactionError internal, track last error, add failure-proof test

* fixup: type corrections in failure-proof test

* fixup: type annotate failure-proof messages as UserMessage[]

* fixup: avoid possibly-undefined array access in failure-proof test

* test(agents): prove compaction provider failure cancellation

---------

Co-authored-by: nebulacoder-v8.0 <noreply@zte.com.cn>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 10:28:01 +08:00
Vincent Koc 3016666e5c fix(test): isolate ACP startup output stream (#116076) 2026-07-30 10:27:19 +08:00
Yuval Dinodia cee6203f44 fix(agents): deny all tools when scheduled authority names a removed account (#115932)
* fix(agents): deny all tools when scheduled authority names a removed account

resolveGroupToolPolicy returned { allow: [] } as its fail-closed sentinel
when a scheduled run named a channel account that is no longer configured.
The runtime matcher treats an empty allowlist as "allow everything not
denied", so revoking the creator account widened the scheduled tool surface
to the full set including exec and apply_patch instead of denying it.

The sentinel now carries an explicit wildcard deny, which the matcher and
the plugin-harness native-tool clamp both honor.

* test(agents): cover scheduled-run tool denial after owner account removal

Adds capability-profile level coverage that a scheduled run whose named
owner account was removed resolves to a deny-all group policy, exercising
the scheduled-authority handoff from resolveConversationCapabilityProfile
through resolveRequesterToolPolicies into resolveGroupToolPolicy.

* fix(gateway): enforce scheduled account authority for loopback tools

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 10:24:53 +08:00
Riive 82af1bf7d4 fix(cron): stop replaying old schedule slots after a cron job is edited (#115779)
* fix(cron): bound restart catch-up to the active schedule

Editing a recurring job's schedule made the gateway fire it immediately on
the next restart. Startup catch-up compares the new schedule's previous slot
against lastRunAtMs, which still belongs to the retired schedule, so a slot
that never existed under the old schedule counted as missed.

Record when scheduling inputs take effect and replay a missed slot only when
it is newer than that. Jobs whose schedule never changed carry no stamp and
keep replaying every computed slot, so catch-up is unchanged for them.

The missed-slot predicate was duplicated in the runnable check and the
backoff-deferral pass; both now share one helper so the bound cannot drift.

Fixes #91944

* fix(cron): protect schedule activation ownership

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 10:07:13 +08:00
Vincent Koc d5e3c68632 fix(daemon): block conflicting systemd gateway owners (#116162) 2026-07-30 09:53:38 +08:00
Vincent Koc 51f9f061f0 docs(config): clarify user timezone contract (#116160) 2026-07-30 09:49:18 +08:00
Vincent Koc 12e5eb6a23 fix(ui): clarify model setup flows (#116086) 2026-07-30 09:47:57 +08:00
joshavant 21db50efc7 refactor(whatsapp): keep inbound boundary transport-private 2026-07-29 20:34:55 -05:00
Vincent Koc f970e5093b refactor(channels): add portable inbound boundary 2026-07-29 20:34:55 -05:00
Vincent Koc f4969c33d9 fix(sessions): migrate legacy transcripts during SQLite import (#116077) 2026-07-30 09:28:47 +08:00
xin zhuang 2e1bf01f51 fix(config): allow explicit main agent bindings when agents.list is non-empty (#89419)
* fix(config): preserve explicit main route bindings

* fix(config): preserve configured main-like agent bindings

* test(routing): cover implicit main roster precedence

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 09:15:44 +08:00
Vincent Koc 3f918d4b3c fix(hooks): honor user timezone in session memory (#116136) 2026-07-30 09:04:52 +08:00
Yuval Dinodia 8ce1e18d2f fix(agents): apply_patch rewrites bytes on hunk context lines (#116128)
* fix(agents): apply_patch rewrites bytes on hunk context lines

A fuzzy apply_patch update replaced the entire matched span with the
model-authored patch text, so trailing whitespace, typographic punctuation,
and tab indentation on lines the hunk marked as context were overwritten
while the tool reported plain success.

The parser now records which emitted lines came in as context and which old
line each one came from, and the update applier keeps the file's own bytes
for those lines. Added and removed lines are still written from the patch.

* test(agents): cover apply_patch context preservation

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 09:04:04 +08:00
Wynne668 77ec4c5420 fix(config): reject invalid heartbeat activeHours without cadence (#102319)
* fix(config): validate heartbeat activeHours without cadence

* fix(config): repair invalid heartbeat active hours

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 08:54:36 +08:00
Ayaan Zaidi 06a5b974ad fix(proxy): keep the capture header predicate module-private
Only the shared redaction helper needs to be public; the name predicate is
an implementation detail and an unused export trips the dependency check.
2026-07-30 09:48:33 +09:00
Ayaan Zaidi 879894a5bd fix(proxy): redact sensitive headers in standalone debug proxy captures
The standalone debug proxy wrote raw request and response headers into
capture rows while the patched-fetch runtime redacted at the parallel
call sites, so a capture taken through the proxy could persist
Authorization, Cookie, and API-key values to disk.

Move the runtime's redaction policy into a leaf module both writers
import instead of adding a second copy, so the two capture paths cannot
drift. The shared helper also flattens node's array-valued headers,
which the standalone proxy passes in directly, and keeps value-level
registered-secret redaction for header names that are not themselves
sensitive.

Reported by SebTardif in #90009; supersedes #82951, which redacted only
by header name and predates the proxy-server rewrite.
2026-07-30 09:48:33 +09:00
Josh Boys edbb1fca86 fix(doctor): fail closed on unknown channel schema (#116025)
doctor --fix copies allowFrom into groupAllowFrom for any channel
whose capabilities allow the fallback, guarded by a check against
the compile-time generated channel schema metadata. That metadata
only covers core-compiled channels; extension-installed channels
like agentmail have no entry there. The guard treated a missing
schema as "no restriction" and wrote groupAllowFrom anyway, which
agentmail's own runtime schema then rejected, aborting the whole
doctor --fix batch (including unrelated pending migrations).

Fail closed instead: without schema info, don't write the field.

Fixes #116024
2026-07-30 08:43:07 +08:00
Vincent Koc e473598c2c fix(ui): make model setup actionable before selection (#116079) 2026-07-30 08:34:43 +08:00
Vincent Koc 156d2e623e refactor(channels): own account config mutations (#115970) 2026-07-30 08:20:41 +08:00
Jason (Json) 9991f49ebb fix(discord): keep activity receipts in adopted threads (#116119)
* fix(discord): keep active thread replies together

* fix(discord): preserve adopted thread progress receipts

* test(discord): prove adopted thread reply matching

* fix(discord): preserve receipt on draft finalization failure
2026-07-29 18:08:18 -06:00
metaforismo 3cda5ad32f perf(gateway): cache lazy server methods import (#116060)
* perf(gateway): cache lazy server methods import

* refactor(gateway): use shared lazy runtime loader
2026-07-29 17:07:29 -07:00
Peter Steinberger a19132e7eb fix(codex): prevent approval promotion from blocking unattended runs (#116117)
* fix(codex): honor hook approval ownership

* fix(codex): keep permission grants human-gated
2026-07-29 17:00:16 -07:00
Ayaan Zaidi d8074d7ca2 fix(daemon): require system-scope ownership before removing the user unit
Doctor's dueling-scope repair removed the user-scope gateway unit on file
presence alone. A system unit file that is merely staged, disabled, failed, or
uncheckable would then cost the operator their only working gateway, and the
default confirm value is true so non-interactive repair amplifies it.

Gate the removal on isSystemUnitActiveOrEnabled: the system unit must be the
live or boot-configured supervisor. Otherwise doctor explains the ambiguity and
prints both fix directions instead of deleting anything. An erroring probe
fails closed for the same reason.

Applies the ClawSweeper rank-up move on #91221.
2026-07-30 08:52:05 +09:00
北京刻熵科技有限责任公司 dcfd6a535d fix(daemon): only treat same-canonical-name user+system units as dueling
Address review: a marker-owned system unit with a *different* name (e.g. an
intentional rescue-bot gateway on the same host) must not be classified as a
duplicate of the canonical user unit, or doctor could remove a legitimate user
gateway. Dueling now requires user.unitName === system.unitName; the direct
canonical system path still matches the real #79375 case. Adds a regression
test for "user canonical + custom marker-owned system" -> kind:"user".
2026-07-30 08:52:05 +09:00
北京刻熵科技有限责任公司 9308557f72 fix(daemon): detect and resolve dueling user+system systemd gateway units
After upgrading on Linux, a user-scope unit
(~/.config/systemd/user/openclaw-gateway.service) and a system-scope unit
(/etc/systemd/system/openclaw-gateway.service) can both exist and both try to
manage the gateway. They bind the same port and each instance's stale-process
detection SIGTERMs the other, producing an endless restart cascade (#79375).

The detector `findInstalledSystemdGatewayScope` checked the user path first and
returned early, so it could never surface that both scopes coexist.

This adds, in three layers:

1. Detection: `findSystemdGatewayInstallation` — a discriminated union
   (none/user/system/dueling) that reports every installed scope without
   early-returning. `findInstalledSystemdGatewayScope` is refactored to delegate
   to it while preserving the exact user-first preference its four lifecycle
   callers rely on (no behavior change for stop/restart/is-enabled/runtime).
   Adds `uninstallUserSystemdGatewayUnit` (removes only the $HOME user unit, no
   root needed) and the pure `formatDuelingScopesWarning` helper.

2. Doctor: `maybeResolveDuelingSystemdGatewayScopes` detects the dueling state
   and, after the existing confirm/policy gate, removes the redundant user-scope
   unit while keeping the root-installed system unit authoritative. Declining or
   an externally-managed policy falls back to the existing cleanup hints.

3. Startup guard: in service mode, when a stale-kill actually happened, log a
   targeted remediation pointing at `openclaw doctor --fix` instead of letting
   the loop look like routine stale cleanup. Diagnostic only — the kill decision
   is unchanged.
2026-07-30 08:52:05 +09:00
SunnyShu0925 b96ada0680 test: add stalled provider response body timeout coverage 2026-07-30 08:48:52 +09:00
SunnyShu0925 45b34a6dca fix(infra): bound stalled provider JSON/text/error-body reads with default chunk timeout 2026-07-30 08:48:52 +09:00
Vincent Koc 342826b904 test(gateway): cover hook account replay scope (#116121) 2026-07-30 07:23:51 +08:00
Vincent Koc 1f56e2da21 fix(messages): validate configured envelope timezone (#116103)
* fix(messages): validate configured envelope timezone

* fix(messages): preserve UTC envelope rendering

* fix(messages): preserve local fallback mode
2026-07-30 07:18:13 +08:00
Vincent Koc 9e041cd386 fix(hooks): preserve multi-account agent delivery (#116095)
* fix(hooks): preserve account routing for agent delivery

Refs #43866

Co-authored-by: Paul Desmond Parker <paul.parker@dcconnect.cn>

* test(gateway): prove hook account delivery handoff

---------

Co-authored-by: Paul Desmond Parker <paul.parker@dcconnect.cn>
2026-07-30 07:10:21 +08:00
Peter Steinberger 23e151a016 fix(config): reject reserved __proto__ MCP server name (#116112) 2026-07-29 16:09:26 -07:00
Sergio Cadavid 42e21ad13b fix(plugins): keep run context available after registration (#107028) 2026-07-30 07:07:33 +08:00
Vincent Koc 8530edb057 fix(plugins): deliver subagent completion to current requester (#116091)
* fix(plugins): deliver subagent completion to requester

Co-authored-by: ambitioncn <36698505+ambitioncn@users.noreply.github.com>

* test(qa): register current-requester plugin fixture

* fix(plugins): scope requester authority per hook

---------

Co-authored-by: ambitioncn <36698505+ambitioncn@users.noreply.github.com>
2026-07-30 07:06:17 +08:00
Vincent Koc fc95d7190c fix(hooks): route mapped wake events to configured sessions (#116109)
Fixes #64556
2026-07-30 06:56:19 +08:00
Ayaan Zaidi daaaef95f0 fix(gateway): cap channels stuck in pending restart
The health monitor lets a pending-restart continuation skip the cooldown so a
timed-out recovery stop can finish on the next pass. That continuation was
never recorded, so an account stuck in restartPending with reconnectAttempts 0
restarted on every check forever, bypassing maxRestartsPerHour entirely: stop
and start thrash the channel manager with log spam and no give-up state.

Only the first continuation per pending episode is now free. Later passes
rejoin cooldown and the hourly budget, and the free pass re-arms only after
the account genuinely leaves restartPending (running again or the pending flag
dropped) so a transient reconnectAttempts bump cannot mint new free restarts.
2026-07-30 07:53:14 +09:00
Peter Steinberger 695391b3ce fix(gateway): evict idle agent terminal sessions under pool pressure (#116101)
* fix(gateway): evict idle agent terminal sessions under pool pressure

* fix(gateway): claim eviction victims and kill only after replacement spawn

* fix(gateway): revalidate eviction victims after the replacement spawn

* fix(gateway): count in-flight reservations when committing eviction

* fix(gateway): reselect eviction victim at commit time

* fix(gateway): release eviction claims on cancelled opens
2026-07-29 15:31:44 -07:00
Vincent Koc 925fdb023e refactor(ui): remove retired time format bootstrap (#116097) 2026-07-29 22:23:27 +00:00
Vincent Koc 43945b836a fix(hooks): retain manual reset memory admission (#116094)
Refs #113396
2026-07-30 06:22:31 +08:00
Vincent Koc 54e273e695 fix(agents): keep date reasoning current in long-running sessions (#116090)
* fix(agents): ground date reasoning below cache boundary

Co-authored-by: Deepak Jain <406777+deepujain@users.noreply.github.com>

* docs: refresh temporal context map

* chore: leave release notes to release curation

* test(agents): keep compaction date mock complete

---------

Co-authored-by: Deepak Jain <406777+deepujain@users.noreply.github.com>
2026-07-29 22:12:47 +00:00