searchMemoryWiki called sharedMemoryManager.search() and getMemoryWikiPage
called manager.readFile() with only a truthiness guard. A memory plugin
whose runtime returns a manager that does not implement the
MemorySearchManager contract crashed shared-backend wiki search with
"sharedMemoryManager.search is not a function" from inside the bundle --
@mem0/openclaw-mem0 <= 1.0.14 registered exactly such a partial manager
(status/probeEmbeddingAvailability/close only).
Guard both call sites and throw an actionable error instead: name the
missing contract method and point at search.backend="local" for wiki-only
access.
* fix(gateway): stop device pairing approval alert floods from retrying devices
A device retrying with a broken token minted a new pending requestId (and
approval alert broadcast) every 5-minute TTL window because refresh preserved
ts and expiry keyed on ts. Pending requests now stay alive via an internal
refreshedAtMs keepalive (ts still owns ordering/--latest), superseded device
requests broadcast device.pair.resolved like node pairing already did, and the
Mac app keeps one alert per device, closes superseded visible alerts, and
resyncs its queue when approving a stale request.
Closes#100974
* chore: satisfy swiftformat and refresh native i18n inventory for pairing prompter changes
* feat(ios): support per-gateway custom headers for proxied gateway connections
Per-gateway custom request headers (Cloudflare Access-style service tokens)
for gateways behind authenticating reverse proxies. Headers are credentials:
Keychain-stored per gateway stableID, masked in settings UI, injected on the
WebSocket upgrade at connect time via a provider so edits apply on the next
reconnect without re-pairing. Reserved handshake headers and control chars
are sanitized out. TLS pinning and pairing flows unchanged. Push relay and
other non-gateway destinations proven header-free by regression test.
Related: #100698
* chore(ios): sync native i18n inventory for custom header strings
* fix(ios): harden gateway custom headers
* chore(ios): resync native i18n inventory
* fix(ios): validate custom header names
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).