* fix: reject malformed UTF-8 in live provider model catalog responses
Add { fatal: true } to TextDecoder in readLiveModelCatalogJson so
invalid UTF-8 bytes throw a TypeError before JSON.parse instead of
silently becoming U+FFFD.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(plugin-sdk): add fallback proof for malformed UTF-8 in live catalog
Adds a focused test that constructs raw bytes with an invalid 0xFE byte
inside a JSON string value and validates that buildLiveModelProviderConfig
catches the Typeerror and falls back to the static catalog rows.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(pr): classify auto-merge failures without ripgrep
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(plugins): mirror local coding sessions to a remote Beam receiver
* fix(plugins): satisfy static gates for the Beam mirror seam
* fix(plugins): import the config type from the narrow contracts subpath
* fix(plugins): require catalog consent and loopback-only plaintext for the Beam mirror
* fix(config): compaction.enabled is rejected as invalid config
Setting agents.defaults.compaction.enabled in openclaw.json made the
whole config fail to load with "agents.defaults.compaction: Invalid
input", so auto-compaction could not be turned off.
The runtime already reads the field (SettingsManager.getCompactionEnabled
returns settings.compaction?.enabled ?? true, and setCompactionEnabled
writes it), and the documented config example in
docs/reference/session-management-compaction.md already shows
"enabled: true". Only the zod schema was missing the key, and the
compaction object is .strict(), so the unknown key rejected the config.
Adds enabled to AgentDefaultsSchema and to AgentCompactionConfig.
Closes#110065
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(config): wire compaction enablement through runtime
Align the accepted compaction.enabled contract across runtime precedence, help, labels, docs, and generated schema baselines.
Co-authored-by: Zakaria Rahali <zakariarahali288@gmail.com>
* test(config): preserve omitted compaction setting
Document the reload lifecycle and lock in project-setting precedence when the OpenClaw config key is absent.
Co-authored-by: Zakaria Rahali <zakariarahali288@gmail.com>
* refactor(config): centralize compaction runtime override
* fix(config): preserve compaction safety guards
* fix(scripts): avoid ripgrep in merge fallback
* docs(config): refresh current main baseline
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* test(ui): make channel settings renderable in the mock dev server
* fix(ui): hide advanced channel settings behind the shared advanced tier
* fix(ui): widen the tier renderer callback to lit's nothing
* fix(ui): keep the advanced collapse control on advanced-only channel forms
* docs: note the shared advanced tier on channel settings
* feat(ui): serve DM pairing fixtures from the mock dev harness
* refactor(ui): align channels DM access requests with the settings design language and drop the channel-health JSON card
* fix(agents): make global loop breaker reachable
Record loop-detector vetoes as typed no-progress outcomes so repeated blocked calls continue the existing streak without colliding with plugin or approval denials. Extract streak accounting into its own owner module and keep completed veto records out of argument reconciliation.\n\nFixes #109435.
* test: harden isolated project routing
Route registry-sensitive UI tests through the isolated project for both focused and broad runs, centralize the isolated file list, and register the Codex prewarm test in the full extension shard.
* test(qa): prove global loop breaker runtime
Drive 31 identical read attempts through the real QA Gateway agent loop and verify the typed veto streak reaches the global circuit breaker before the turn returns a final marker.
* test: keep isolated UI files out of shared runs
Always exclude registry-sensitive files from the shared UI project and reject broad watch targets that would span shared and isolated projects.
* fix(ui): keep channel action labels on one line and surface QR next to actions
* fix(test): drop duplicate prewarm test from attempt-extra shard config
* perf(gateway): stop per-row store and registry rediscovery in sessions.list
* fix(gateway): satisfy kysely guardrail and knip on sessions.list rediscovery fix
* refactor(state): split registered agent database listing from the registry
* chore(state): allowlist the split registry listing module for raw sqlite probes
* perf(sessions): serve session entries from a data_version-validated memory store
* fix(sessions): pair data_version with total_changes for entry cache validity
* chore(sessions): keep the cache publication token type module-local
* fix(sessions): drop the entry cache when tracked publications cannot prove freshness
* fix(scripts): run changed checks locally when Blacksmith never ran them
AGENTS.md already says trusted-source work falls back to local execution when
the remote backend is unavailable, but the tooling did not implement it: a lease,
broker, DNS, or network failure surfaced as a plain exit 1, so the lanes were
reported red without ever having been evaluated. That is worse than slow — a run
that never happened looked the same as a run that failed.
Tee the wrapper output and use its run summary as the discriminator. The summary
only appears once the command reached the box, so a failure carrying
`command-exit` is a real verdict and propagates unchanged; anything else never
produced one and re-runs locally, with a loud note so the proof summary records
which machine produced it.
Deliberately a positive test for `command-exit` rather than a blocklist of
infrastructure errors. Guessing wrong toward "infrastructure" only re-runs the
checks locally; guessing wrong toward "real failure" would block on an outage.
It must never widen to "fall back on any non-zero exit" — prompt snapshots are
Linux-only truth and would pass locally on macOS, turning a red gate green.
The sparse-checkout path keeps no fallback: it exists precisely because the
checkout cannot resolve the diff refs, so there is nothing local to run.
* fix(scripts): require positive pre-dispatch evidence before falling back
A missing run summary does not prove the remote never started: a wrapper that
crashes or loses its output transport after dispatch looks identical. Reading
that absence as "never ran" would rerun locally and could turn an unknown or
failing Linux-only lane green, which is the exact masking this guard exists to
prevent.
Require a positive pre-dispatch signature instead, and keep the command-exit
veto. Also reapply backpressure on the tee: inherited stdio got it from the OS,
piping does not, so a verbose delegated run could buffer its whole output here.
* build(deps): drop stale root partial-json and dead ownership-manifest entries
partial-json is owned by packages/ai (which declares its own copy); glob and
markdown-it no longer exist as root dependencies. Audit follow-up to #114006.
* build(deps): keep root partial-json (openclaw/ai bundling contract); drop only dead manifest entries
* refactor(prompt): plain inbound context labels with a provenance marker
Replaces trust-worded inbound context labels ("(untrusted metadata)",
"(untrusted, for context)") with plain labels plus a fixed provenance
marker suffix appended to every OpenClaw-injected context header.
Detection keys on the marker, not label text, so strippers stay correct
across UI, TUI, replay, /trace segmentation, memory recall, and the Swift
chat preprocessor. Drops sanitizeInboundSystemTags in favor of the marker
boundary plus trusted system-prompt narration.
Renames the untrusted-named plugin SDK context identifiers to
channel-provenance names, keeping deprecated aliases registered for
removal after 2026-09-08.
Adds `openclaw doctor --fix` migrations that rewrite legacy inbound
labels in stored SQLite transcripts and purge legacy envelope-
contaminated LanceDB recall rows.
* fix(ci): resolve gate failures for plain inbound context labels
- doctor sqlite readers: open read-only connections via openNodeSqliteDatabase
so the Kysely connection-boundary guardrail holds; unexport the now-internal
transcript snapshot type (Knip unused-export gate).
- compat registry: split the record table into registry-records.ts and
plugin-sdk-subpath-records.ts. The new compat record pushed registry.ts past
the 700-line oxlint cap; suppressions are disallowed, so follow the existing
sibling record-module pattern. Public exports and PluginCompatCode literals
unchanged.
- acp-runtime test: assert current finalization behavior (newline normalization
only). The bracket de-fang and System: rewrite it expected were removed with
sanitizeInboundSystemTags; forged system lines are neutralized at the
system-event queue, the single chokepoint feeding the System:-per-line render.
- regenerate docs_map and the plugin SDK API baseline manifest.
* fix(prompt): harden inbound context label migration and drop in-band sanitizer
Review follow-ups on the plain-label + provenance-marker change:
- Remove src/security/system-tags.ts. Rewriting inbound text to neutralize
look-alike `System:`/`[System]` markers corrupted legitimate user text and is
not a real injection boundary; role separation plus external-content wrapping
is. Explicit product decision, recorded at the system-event queue.
- Narrow the LanceDB legacy-row purge so it cannot delete benign memories. It
now requires a complete known legacy sentinel line, a legacy label followed by
a fenced JSON body, or the complete legacy external-content header. The prior
predicates matched ordinary prose such as `Notes (untrusted metadata):`, and
deletion is irreversible.
- Make explicit-empty canonical ChannelStructuredContext win over the deprecated
alias via a present/absent result instead of collapsing `[]` to undefined.
- Keep `\r?` in the active-memory doctor rule. It is the only rule spanning the
header's line break, migrated assistant rows skip newline normalization, and
without it the marked-header replace wins and the body strips to empty. Added
a CRLF regression test.
- Fix stale comments that described removed behavior, and cover the Swift
prose-block strip path.
Claude-Session: https://claude.ai/code/session_01WNzsPddQmxy9Y7jKD4wAxH
* refactor(infra): move exec approvals into the shared SQLite state DB
Delete the file-runtime exec-approvals store (exec-approvals.json + .lock
sidecar machinery) on both runtimes and make the reserved
exec_approvals_config singleton row canonical. Doctor owns the one-time
import with claim/verify/receipt discipline; runtime fails closed with a
doctor instruction while un-migrated legacy state exists. The wire CAS
contract, socket semantics, and gateway auth-token derivations are
unchanged. Kills the #113929 lock-contention bug class structurally and
nets around -2.9k lines.
* fix(infra): green CI gates and retire file-era exec approvals tests
Break the migration-type import cycle with a leaf contract, regenerate the
plugin-SDK API and native i18n baselines for the intentional surface change,
drop unused exports, and replace the macOS file-era approvals test suite with
SQLite-backed behavior coverage per the obsolete-internals test policy.
* chore: green max-lines ratchet, native i18n baseline, and unused-export scan
* build(deps): remove npm shrinkwrap; mirror pnpm lock into transient package locks
npm 12 removed shrinkwrap (command + tarball/root loading). Delete all 82
committed npm-shrinkwrap.json files and stop publishing lockfiles; keep
pnpm-lock.yaml as the single reviewed dependency boundary. The generator
becomes scripts/generate-npm-package-lock.mjs and feeds plugin bundling via
a transient package-lock.json + npm ci (works on npm 11 and 12). Tarball
validation treats the published 2026.7.2 beta train as a shrinkwrap
transition; self-update npm detection now uses install topology instead of
the shipped shrinkwrap.
* fix(deps): repair lint, deadcode, and test-type lanes for the npm 12 migration
- sort integrity comparisons with an explicit comparator (oxlint)
- keep resolveBunGlobalNodeModules module-local (knip unused-export gate)
- model npm pack --json as npm<=11 array / npm 12 name-keyed object
- default calver destructuring in the tarball test fixture