* feat(apps): Android chat adopts the system-notice family
Decode internal_system provenance and __openclaw markers in parseHistory,
classify into SystemNotice/SystemDivider timeline variants with web-parity
kinds, render minimal Compose rows, round-trip metadata through the
transcript cache textPartsJson envelope (no Room change), and extend the
deterministic screenshot fixture.
* fix(apps): keep generated locale artifacts out of the source PR
The native locale refresh workflow owns NativeStringResources.kt and
values-*/strings.xml; ship source strings plus the regenerated
apps/.i18n/native-source.json inventory only.
* fix(cron): stop advertising trigger-gated fields when cron triggers are disabled
When cron.triggers.enabled is off (the documented default posture), the
cron tool still advertised job.trigger/patch.trigger, schedule.kind
"stream" with its stream-only fields, and payload.kind "script" — all of
which the scheduler unconditionally rejects. Schema-eager models attach
junk triggers to plain reminder requests, get rejected, and retry-loop
without ever creating the job.
Gate those surfaces on the resolved config so they are only advertised
when the runtime can actually accept them, and swap the trigger guidance
for a short unavailable notice so the model tells the user instead of
polling or silently degrading the request. Config-less construction
paths keep the full surface.
Related: #119455
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(cron): restore config type import and regenerate schema contracts
Repairs rebase fallout and CI-contract consequences of the trigger gating:
- Re-import OpenClawConfig from config/types.openclaw.js; the rebase crossed
#121768 which removed the re-export from config/config.js, stranding
resolveCronTriggersEnabled's signature.
- Guard the destructured tool in the MCP gating test for
noUncheckedIndexedAccess.
- Collapse the hookContext merge object to one line: the added config
pass-through tipped openclaw-tools.ts over the max-lines ratchet (701/700).
- Regenerate prompt snapshots (cron description narrowed under
triggers-disabled harness config; ~3k chars per prompt) and the plugin-sdk
API baseline (CronToolOptions gained the optional config member, visible
via codex-mcp-projection).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* chore(cron): align schema contracts with current main
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Josh Lehman <550978+jalehman@users.noreply.github.com>
* feat(apps): adopt the system-notice family in Apple chat (OpenClawChatUI)
Decode internal_system provenance and __openclaw history markers, classify
once into message/notice/divider rows (web-parity kinds), render minimal
hairline notice/divider SwiftUI rows, preserve metadata through history
reconciliation, live transport, and the transcript cache, and align the
transcript exporter with visible classification instead of leaking raw
[System] prompts.
* fix(apps): satisfy OpenClawKit periphery gate
Remove the dead visibleMessages projection (rows path replaced its
consumers) and annotate the provenance test-fixture initializer with the
repo-standard periphery:ignore rationale.
* fix(apps): refresh native i18n inventory after periphery cleanup
* fix(logging): publish diagnostics ZIP through staged atomic overwrite
A named support export wrote the in-memory ZIP buffer straight to the
final path, so a failed or interrupted overwrite truncated the previous
archive, and an existing file kept its pre-existing (potentially
permissive) mode since writeFile only applies mode on creation.
Route publication through writeExternalFileWithinRoot's sibling staging:
the new archive is written to a private sibling, fsynced, and atomically
renamed over the final path. A failed publication leaves the previous
ZIP untouched with no staging residue, and the rename always installs
the staged file's 0o600 mode. The command now returns the actual
published path.
Fixes#122064
* test(logging): prove failed zip replacement stays atomic
* test(sqlite): create private reliability fixture
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Per-requester OAuth for HTTP MCP servers: in shared channels each trusted sender connects their own account. New config: mcp.servers.<name>.oauth.identity ("shared" default, byte-identical behavior) and gateway.publicOrigin (HTTPS except loopback) for the new GET /oauth/mcp/callback served on the Gateway HTTP server. Requester tokens are isolated per (channel, account, sender) in mcp_oauth_stores rows (no schema bump); callbacks correlate through a durable state-keyed one-time index with a 10-minute TTL; per-requester servers are fail-closed out of static/scheduled runtimes; unauthenticated senders get a sign-in link with a portable URL button; MCP config mutations clear requester credentials at the canonical owner. mcp status --json keeps the legacy authStatus fields additively. Proven by a two-user self-hosted-Executor E2E through the real gateway callback. Part of #122034.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
* fix(ui): align effort picker after model switches
Publish ordered per-model effort profiles from the Gateway and reconcile new-session effort overrides so labels, slider positions, and create payloads stay consistent.
* fix(ci): align model profile contracts
* feat(ui): show session-reset dividers and fix boundary markers on DB-backed sessions
/reset now leaves a durable 'Session reset' divider at the transcript
boundary in the Control UI. Root-cause fix underneath: the SQLite
transcript projection only selected message events, so compaction (and
now reset) markers never reached clients for DB-backed sessions; marker
synthesis now has one owner (session-transcript-message.ts) consumed by
both storage backends across full/recent/paged/by-id/anchor reads.
Additive __openclaw marker kind 'reset' documented in clients.md.
* fix(gateway): keep history readers out of the plugin SDK barrel and fix CI gates
Direct imports for the sqlite history readers (the session-accessor barrel
is SDK-reachable via session-transcript-lock-runtime); reset marker added
to the kept-tail chat.history expectation; lint naming fixes; marker tests
split into session-transcript-readers.markers.test.ts.
Stage transitional plugin install-record writes without plugin-schema validation, then require fresh doctor and strict validation for every aggregate plugin change.
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Attachment MIME gating and extraction dispatch used nine separate byte policies across five files; the allowlist could approve one MIME while dispatch observed another after a second sniff. @openclaw/media-core/attachment-classify now owns byte sniffing, text heuristics, extension mapping, and the seven attachment classes; the cache stores one classification that gates and dispatch share. Channel-declared MIME leads with the transport Content-Type as a byte-arbitrated hint, normalizeMimeType folds YAML/XML synonyms so existing configured allowlists keep matching, and input_file API callers keep declared-MIME precedence over cosmetic filenames. Net -135 production LOC.
Proof: 2,153 focused tests across media-core/media-understanding/media plus boundary-contract and deadcode-mirror suites; ClawSweeper local review clean (round 5, zero findings); live Telegram default-turn probe green.
Co-authored-by: Ayaan Zaidi <hi@obviy.us>