Two crashes when a memory plugin misbehaves in bridge mode:
- listActiveMemoryPublicArtifacts sorted plugin-returned artifacts
without
validating them; an artifact missing any of the string fields the
comparator dereferences (kind, workspaceDir, relativePath,
absolutePath,
contentType) crashed wiki status and every other bridge consumer with
"Cannot read properties of undefined (reading 'localeCompare')".
@mem0/openclaw-mem0 <= 1.0.14 shipped record-shaped artifacts with
none
of those fields, typed against a drifted SDK stub. Validate the shape,
drop malformed entries (and non-array listings), and warn once naming
the offending plugin -- the same treatment agentIds already got.
- resolveMemoryWikiStatus gated artifact counting on vaultMode/enabled
but
not bridge.readMemoryArtifacts, so the wiki.status gateway method
still
enumerated artifacts (and hit the crash above) with the flag off, even
though the sync path (bridge.ts) and CLI gateway routing honor it. The
documented workaround therefore never worked for the wiki_status agent
tool. Add the flag to the gate; the count reports null when imports
are
disabled, matching non-bridge modes.
* [AI] fix(sessions): exclude done sessions from transcript freshness rollover guard
Exclude status: "done" from resolveTerminalMainSessionTranscriptRegistryCheck alongside the already-excluded status: "failed". Successful main sessions should stay reusable for the next user message even when transcript mtime slightly exceeds registry updatedAt.
Updated test expectations in:
- src/gateway/server-methods/agent.test.ts (split it.each into done-reuse + endedAt-only-rotate)
- src/commands/agent.session.test.ts (split into done-reuse + endedAt-only-rotate)
- src/auto-reply/reply/session.test.ts (default-done case now expects reuse)
Related to #99964
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(ts): fix TypeScript error in endedAt-only test scenario type
Remove the conditional status spread that TS cannot resolve when
status is absent from the scenario type, and add 'as const' for
narrowed literal inference.
* [AI] fix(comment): correct mtime direction in done exclusion comment
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* review: separate failed/done rationale in comment
* fix(sessions): reuse completed main transcripts
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(telegram): stop local listener and bot on retry loop non-recoverable error
* fix(telegram): reuse webhook shutdown on setup failure
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(openai): strip status from replayed input items for custom openai-responses endpoints
* fix(openai): narrow replay status stripping
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
createNonExitingRuntime.exit() threw a generic Error, making it
impossible for upstream try-catch to distinguish a simulated process
exit from a real runtime crash. Added an ExitError class (internal-only,
not exported from SDK) that callers can check via instanceof.
ExitError is kept internal to avoid expanding the public plugin SDK
API surface without maintainer approval. Tests import directly from
./runtime.js.
Fixes#97796.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(node-host): handle stdout/stderr stream errors in runCommand
* chore(proof): add real behavior proof for node-host runCommand stream errors
* fix(node-host): wait for child exit after stdout/stderr stream errors
* fix(node-host): lint and type errors in proof script and test
* fix(node-host): bound stream-error command shutdown
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(cron): do not set delivery mode to announce when disabling best-effort on payload edits
* fix(cron-cli): update sibling test to expect undefined mode on no-best-effort-deliver
* fix(cron): preserve delivery mode when disabling best effort
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(crestodian): AI-only conversation with model-judged approvals
Every chat message now routes to the AI (agent loop, then planner); the
typed grammar is a single anchored command language used only as the
no-model deterministic fallback and for rescue/one-shot, so natural
language can no longer misparse into operations. Approval of pending
mutations is judged from the user's own words by a host-run classifier
(closed-list fast path, single-shot model judgment, fail-closed), never
by the conversation model. New connect_channel/open_agent ring-zero tool
directives let the AI start the hosted channel wizard and agent handoff.
Chat turns are serialized, stale host proposals are cleared once the
agent loop owns the conversation, sensitive config-set values are
redacted from AI-visible history, and Gemini CLI joins the overview,
models output, prompts, and local backend ladder.
Fixes#100604
* test(crestodian): cover AI-only turns, approval intents, directives, and anchored grammar
* docs(crestodian): AI-only conversation, natural approvals, Gemini fallback
* test(crestodian): classify decline via text classifier in agent-loop seam
* fix(crestodian): correct textResult arity and directive handoff type
* fix(crestodian): keep exact sensitive config-set commands off every model path
* fix(acp): catch unhandled rejection from handleGatewayEvent in event callback
* fix(acp): catch unhandled rejection from handleGatewayEvent
* test(acp): add regression test for handleGatewayEvent error boundary
Verifies that when handleChatEvent throws, handleGatewayEvent catches the
error, logs it via this.log(), and does not reject.
* chore: remove committed PR body artifact
* fix(acp): contain rejected gateway events
* chore: defer ACP release note
* refactor(acp): simplify gateway rejection boundary
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(ui): declutter the sidebar — pinned/chats groups, stable ordering, palette session search, titlebar brand in macOS app
- Sessions split into Pinned and Chats groups; pins are exempt from the
nine-row recency cap and the open session highlights in place instead of
hoisting to the top, so clicking a row no longer reshuffles the list.
- The command palette (Cmd+K) is the single search: it queries sessions
across agents server-side and lists matching chats next to nav commands;
the sidebar session-picker popover and its magnifier are removed, and the
topbar search pill shrinks to an icon button.
- The sidebar brand row is gone: web keeps the breadcrumb wordmark, and the
macOS app shows a compact brand mark in the native titlebar strip next to
the traffic lights (CSS-gated on the injected openclaw-native-macos class).
- The agent filter dropdown becomes a compact scope chip in the Chats group
header; the welcome badge drops its duplicate claw logo.
- Footer collapses to one icon row (status dot, settings, docs, pairing,
mobile theme switch); default pinned nav routes are now Overview-only and
the Recent-collapse preference is retired.
* test(ui): keep sidebar hover-reveal proof before the stability click; refresh locale metadata
Clicking the row leaves :focus-within on it, which keeps the management
actions visible; the hidden-actions assertion must run first.
* test(ui): archive an idle session in the sidebar e2e; assert running rows stay archive-disabled
* fix(ui): drop stale and hidden rows from palette session search
Invalidate the in-flight search and clear results on every query change so a
late-resolving previous-query request cannot leave stale rows selectable, and
run results through getVisibleSessionRows so archived/global/unknown/cron/
subagent rows stay hidden like the sidebar list. Codex review findings.
* fix(ui): over-fetch palette session search so hidden rows cannot starve visible matches
Exclude global/unknown rows server-side and fetch a full 50-row page before
applying the sidebar's hidden-row filter, keeping the 10-row display cap.
Codex review finding.
* chore(i18n): refresh sidebar locale metadata after rebase
* fix(ui): bound command palette session search
* docs: defer release notes to release generation
* fix(gateway): dedupe MCP schema conflict warnings
* fix(gateway): attribute MCP loopback schema warnings per tool
Key the loopback schema-warning dedupe on the full message (which now names
the tool as "<tool>.<field>") instead of the bare field name. Two different
tools that conflict on the same common field (action/id/type) each surface a
warning again, and the emitted line is attributable to a tool. Also route the
previously-un-deduped malformed-variant warning through the same helper so it
no longer floods on every rebuild.
* fix: preserve Bedrock live API providers
* fix: derive Bedrock smoke region from AWS config
* fix: honor Bedrock discovery region in smoke
* fix: keep Bedrock live smoke on Bedrock runtime
---------
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Second sweep: 13 cards from the openclaw.ai X showcase and Discord
maintainer channel — car negotiation, flight check-in, insurance
claim, Idealista skill, tokenomics cost tracker, Excalidraw
diagrams, GA4 skill, X post pipeline, lab results to Notion,
Pebble Ring voice, creator media studio, HomePod skill, holo cube,
macOS menu bar app — plus a Related link to the full X showcase.
oxfmt 0.57.0 --check passed; hook bypassed (no node_modules in
worktree).
Xcode syncs the iOS project supergraph pins (ElevenLabsKit, WebRTC, cmark,
swift-markdown) into the local swabble package's Package.resolved on every
resolve, leaving permanent dirty-worktree noise. Match the existing
OpenClawKit convention: ignore the file. Reproducibility is unaffected -
swabble's only runtime dependency is exact-pinned (Commander 0.2.2) and CI
cache keys hash Package.swift + Sources, not this file.
Adds 12 community cards sourced from Discord (#skills, #general,
shell-society) and the web: Dropage deploy, anti-scam URL checker,
product-design skill trio, miab-broker, lite-mode, ClawEval,
Music Craft, gardening back office, Obsidian second brain,
family history bot, Action Button walkie-talkie, and Clinch.
All links verified live; oxfmt 0.57.0 --check passed (worktree
lacks node_modules, hook bypassed after proof).
* fix(browser): preserve HTTP status in node-proxied browser errors
The node browser proxy (runBrowserProxyCommand) collapsed a >=400 browser-route
response into new Error(<body.error>), dropping the HTTP status. That error
crosses the node.invoke boundary as a plain string (Error properties are not
preserved over the RPC), so the gateway's stale-target retry classifier — which
keys off a leading <status>: token (msg.includes("404:") && msg.includes("tab
not found")) — never matches a node-proxied "tab not found". The drop-targetId
retry never fires and the stale-targetId error surfaces to the agent instead.
Prefix the status onto the message ("404: tab not found", "403: action
targetId must match request targetId") so the existing gateway classification
and retry work through the node proxy. Pure formatting change in the >=400
branch; validation/timeout error paths are untouched.
Tests: extensions/browser invoke-browser suite — 14/14 pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(browser): harden node proxy status errors
* docs(changelog): credit browser proxy status fix
* chore: defer browser proxy release note
---------
Co-authored-by: rhclaw <260109027+rhclaw@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>