Aligns the shared OpenClawMascotView with the website hero styling:
per-part bounding-box gradients (SVG objectBoundingBox semantics; the
claws were nearly flat-colored before), antenna stroke width 2, eye
glow r=2, float depth -8% of size, and a light-scheme palette from the
site theme variables. The float now offsets the whole canvas like the
site floats the hero container, so the antennae no longer clip at the
float peak.
macOS adopts the hero glow treatment: the About pane mascot gets the
coral silhouette drop-shadow with the teal hover glow and 1.1 hover
scale, and the onboarding hero replaces its hand-rolled breathing glow
circle with the same silhouette glow.
* feat(crestodian): AI-first conversational onboarding with typed-op guardrails
Interactive `openclaw onboard` (and bare `openclaw` on a fresh install) now
opens the Crestodian conversation: detection-backed first-run proposal
(Claude Code/Codex logins, API keys), persona AI turns for every free-form
message (configless local-runtime fallback, 60s deadline, deterministic
degradation), approval-gated typed operations, chat-hosted channel setup
(`connect <channel>`), config get/schema read ops with secret redaction, and
a post-write validation hook that feeds schema errors back for a self-fix
turn. Adds the additive gateway `crestodian.chat` method so app clients run
the same conversation. Classic wizard stays behind --classic/explicit flags;
non-interactive automation unchanged; `--modern` becomes a deprecated alias
for `openclaw crestodian`.
* feat(macos): Crestodian chat onboarding and importance-ordered permissions
Replace the gateway step-wizard page with a Crestodian chat over the new
crestodian.chat method (works before any model auth exists), sort the
permissions page by importance with no scrolling, drop the redundant manual
refresh, and bump the onboarding version.
* feat(crestodian): run the custodian on the real agent loop with a ring-zero tool
Crestodian conversations now execute through the same embedded agent runner
as regular agents: a persistent agent session with a single construction-gated
`crestodian` tool wrapping the typed operations (read actions free; mutations
require approved=true asserted from explicit user consent, audited, with
post-write config validation fed back into the loop). The engine prefers the
loop (configured models or the Codex app-server fallback) and degrades to the
single-turn planner, then to deterministic commands. Setup approval seeds the
crestodian exec approval so local model harnesses can run; the configless
Codex backend config now enables exec and direct tool loading (it was
dead-on-arrival behind tools.exec.mode=deny and the tool-search index).
* test(crestodian): type the engine mock signatures for the core test lane
* fix(crestodian): map the advertised create_agent tool action
* fix(crestodian): host-verified approval arming and a macOS setup completion gate
Review findings: the model-supplied approved flag alone could authorize
ring-zero mutations (prompt injection / model error), and removing the macOS
wizard gate let users Next past the Crestodian page with nothing configured.
Mutating tool actions now also require host-verified consent (the engine arms
approval only when the user's actual message is an explicit yes), and local
macOS onboarding blocks advancing until setup authored the config, using the
same signal the old step wizard checked.
* fix(crestodian): bind approval to the exact proposed operation and gate dot navigation
A generic yes no longer authorizes arbitrary mutations: denied mutating tool
calls register a canonical operation fingerprint (host-owned, per session),
and an armed turn executes only the identical call, once. The denial message
is arming-aware so the approved turn self-heals in one roundtrip, and the
agent protocol pre-registers proposals. macOS onboarding page dots now honor
the same setup-completion gate as the Next button.
* fix(crestodian): redact sensitive wizard answers, skip logged-out CLIs, gate programmatic advance
Sensitive channel-wizard answers (tokens, passwords) are redacted from the
AI-visible conversation history; setup and the onboarding welcome never pick
or advertise a definitively logged-out CLI as the model; and macOS
handleNext() honors the page gates for programmatic callers (chat handoff)
just like the Next button.
* fix(crestodian): align the onboarding welcome's configured predicate with the app gate
A valid config carrying only a default model (partial/hand-written) now still
gets the first-run proposal instead of the ready guide, so the macOS setup
gate can always be satisfied from the conversation.
* fix(crestodian): armed turns can never mint their own executable proposal
An approval-mismatched call inside an armed turn no longer re-registers and
invites a retry (which let the model swap the approved operation for another
in the same turn); it voids the approval entirely and requires a fresh yes.
Proposals register only in unarmed turns, which the agent protocol already
does when proposing.
* fix(onboard): route any explicit setup flag to the classic wizard
* fix(ci): satisfy new lint rules, tool-display guard, and generated artifacts for crestodian
* chore(i18n): refresh native inventory after permissions copy wrap
* fix(crestodian): harden conversational onboarding
* docs(crestodian): document conversational onboarding
* test(crestodian): type embedded runner mock
* fix(crestodian): close onboarding security gaps
* chore: retrigger ci
* fix(discord): reuse stored sessionId across voice (stt-tts) turns
Discord voice turns previously called agentCommandFromIngress without a
sessionId, so the core session resolver minted a fresh UUID per turn.
Because the codex app-server thread is bound beside the per-session
file, every voice turn produced a new codex thread, a brand-new
rollout-*.jsonl, and lost all prior context.
The fix reads the persisted session entry for the route's sessionKey
(the same supervisor entry already used for the route) and threads that
sessionId into agentCommandFromIngress, mirroring how voice-call's
response-generator and the gateway 'voice.transcript' dispatcher already
keep voice conversations stable.
Adds extensions/discord/src/voice/ingress.test.ts covering both
directions: reuses the stored id when present, and lets core mint a
fresh one when the store is empty.
Fixes#97688
* fix(discord): preserve voice session after rollover
* refactor(discord): keep voice session ownership in core
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(google-meet): bound JSON response body reads to prevent OOM
Replace raw response.json() with readProviderJsonResponse (16 MiB cap)
across meet.ts, calendar.ts, and oauth.ts to prevent unbounded memory
allocation from oversized HTTP response bodies.
Also extract duplicated hasConfig check in createGoogleMeetSpace.
Test: loopback HTTP server test with 4 MiB oversized body + valid body.
* fix(test): prove readProviderJsonResponse bounds the 200 success path
Replace loopback server with synthetic Response objects. The oversized
test now returns HTTP 200 with a 17 MiB body — readProviderJsonResponse
rejects it at its 16 MiB cap, proving the bounded read fires on the
success path (not just the already-bounded 500 error path the old test
exercised).
Ref. https://github.com/openclaw/openclaw/pull/98850
* fix(test): add real HTTP transport integration tests for bounded JSON reads
- Add two vitest integration tests that start a real local TCP server,
fetch through native HTTP transport, and verify readProviderJsonResponse
correctly rejects oversized responses and passes normal ones.
- Existing synthetic-boundary tests unchanged.
Ref. https://github.com/openclaw/openclaw/pull/98850
* fix(test): wrap server.listen in promise executor to satisfy oxlint no-promise-executor-return
* fix(google-meet): add real SSRF-guard transport integration tests for bounded JSON reads
Adds two new integration tests that route meet.googleapis.com requests
through a local TCP server via spyOnGoogleMeetFetch, exercising the
actual fetchGoogleMeetSpace function end-to-end through the SSRF guard
mock. This addresses ClawSweeper review feedback requesting real
transport-level proof of the bounded read contract.
* fix(google-meet): preserve original fetch reference in spyOnGoogleMeetFetch to avoid recursion
The mockImplementation replaced globalThis.fetch, so calling fetch() inside
the mock called the mock itself. Capture globalThis.fetch before spying and
use the saved reference inside the implementation to avoid infinite recursion.
* fix(google-meet): add console.log debug output to loopback proof tests
Add structured console.log evidence markers to the four real HTTP
transport integration tests so that reviewers can see the bounded-read
contract working through actual TCP connections. Format matches the
established pattern from Alix-007's approved bound-read PRs.
* Revert "fix(google-meet): add console.log debug output to loopback proof tests"
This reverts commit e64c26d17e867f3b18ca0ad3a5d95af9d9809efc.
* fix(google-meet): tighten response body limits
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Ports the animated OpenClawMascotView to the macOS app: the About pane
icon and the onboarding hero now render the Canvas+TimelineView mascot
(float/blink/antenna-wiggle/claw-snap, still under Reduce Motion)
instead of the static app icon.
The view moves from apps/ios into the shared OpenClawChatUI target of
OpenClawKit so iOS and macOS consume one copy; the iOS lint filelist
tracks the moved file.
Quick Settings read as competing panels and the Appearance card wrapped/clipped its segmented controls in a narrow side column. Switching to Advanced also had no visible way back — the only return was a "Quick Settings" link buried in the accordion nav.
- Replace the one-way "Advanced" button and buried accordion back link with a persistent Simple/Advanced switch in the page header, mirrored into an in-body row at the mobile breakpoint where the header is hidden.
- Give Appearance a full-width auto-fit grid so Theme/Mode/Roundness/Text size never wrap or clip.
- Drop the duplicate Context Profile side panel for a compact summary line, and only show "Pending" when a change is actually staged (pass savedConfigObject).
- Remove the redundant in-page "Quick Settings" heading and unused preset copy.
Presentation-only within ui/src/pages/config; net -354 non-test LOC. Closes#100145.
* refactor: extract reusable AI runtime package
* refactor: complete AI provider relocation
* refactor: keep llm core internal
* refactor(ai): make @openclaw/ai self-contained with host policy ports
Move pure transport helpers (tool projections, strict-schema normalization,
prompt-cache boundary, stream guards, anthropic/openai compat, request
activity) from src into packages/ai; move utf16-slice into
normalization-core. Inject host policy (guarded fetch, redaction,
strict-tool defaults, diagnostics logging) through AiTransportHost with
inert library defaults installed by src/llm/stream.ts. Narrow the public
barrel to instance-scoped createApiRegistry/createLlmRuntime; the
process-default runtime moves behind internal/ and
registerBuiltInApiProviders takes an explicit registry. Delete the
src/llm/api-registry re-export facade.
* fix(ai): teach node, jiti, and vite resolvers the @openclaw/ai and utf16-slice subpaths
The workspace alias tables in root-alias.cjs, plugin-sdk-native-resolver,
sdk-alias, the shared vitest config, and the Control UI vite config only
knew @openclaw/llm-core; Node-side plugin loading resolved @openclaw/ai
through the pnpm symlink to the unbuilt dist (checks-node-compact CI
failures), and the Control UI build broke on the new
normalization-core/utf16-slice subpath.
* chore(ui): drop leftover service-worker debug logging
* build(release): ship @openclaw/ai with its own shrinkwrap and honest dependency set
packages/ai declares only its six real runtime deps (kysely, chalk, json5,
tslog, zod, fs-safe, and proxyline were never imported); orphaned root deps
removed. generate-npm-shrinkwrap now treats publishable packages/* like
publishable plugins so the AI tarball pins its transitive tree even though
workspace deps are omitted from the root shrinkwrap. knip learns the
package entry points; the tsdown dts neverBundle option moves to its
documented deps.dts home; the README documents the no-semver internal/*
contract and host ports.
* docs(ai): add minimal external-consumer example app
examples/ai-chat consumes only the public @openclaw/ai surface (built dist
via the workspace link): isolated runtime, built-in provider registration,
one streamed completion. Supports Anthropic/OpenAI via env keys and a
keyless local Ollama target; live-verified against Ollama.
* docs(ai): document the @openclaw/ai package and workspace shrinkwrap boundary
* chore(check): include examples/ in duplicate-scan targets
* fix: emit normalization package subpaths
* fix: complete AI package boundary artifacts
* fix: align AI package boundary contracts
* fix(ci): stabilize package release contracts
* test: align documentation contract checks
* test: keep cron docs guard aligned
* test: align restored docs contract guards
* test: follow upstream docs contracts
* docs: drop superseded talk wording
The /set/media control route accepts dark|light|no-preference|none, but the
CLI rejected no-preference, so prefers-color-scheme: no-preference could not
be emulated from the CLI. Align the CLI argument, validation, and error text
with the route contract and update the browser-control docs media line.
* fix(plugins): stage git plugin clone on target filesystem to avoid EXDEV
Git plugin installs cloned into os.tmpdir() then atomically renamed the
staged repo into ~/.openclaw/git/. When /tmp and the state dir live on
different filesystems (common in Docker with bind-mounted volumes), the
rename failed with EXDEV: cross-device link not permitted.
Stage the clone under the managed git root (same filesystem as the
destination) so the final rename never crosses devices. Falls back to
os.tmpdir() when the managed root cannot be prepared, preserving prior
behavior.
Closes#99885
* fix(plugins): isolate git clone staging per install
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Ports the openclaw.ai hero mascot animation (body float 4s, eye blink 3s,
antenna wiggle 2s, staggered claw snaps 4s) to every product surface that
shows the logo:
- web: ui/public/favicon.svg now carries SMIL animations, so every <img>
usage (login gate, sidebar brand, agent avatar fallback) animates with
no markup changes; CSS animations do not run in <img>-loaded SVGs.
- iOS: new OpenClawMascotView renders the canonical 120x120 vector via
Canvas + TimelineView (30fps, honors Reduce Motion); OpenClawProMark
uses it in onboarding, sidebar, and command center. Static PNG imageset
deleted (watch app keeps its own copy).
- Android: new OpenClawMascot composable (PathParser + infinite
transitions, honors the OS animator scale) replaces the static vector
drawable in onboarding and the chat/shell/voice headers, with tint
support for the monochrome header silhouettes. Drawable deleted.