* fix(auto-reply): warn when /export-session only contains user messages (backend-delegated)
Closes#90844 (minimum-fix option 2).
When a session is driven by a CLI/ACP backend, OpenClaw's own transcript only
records the inbound user messages. /export-session would still produce a
successful HTML export with empty summary stats and no assistant/tool/usage
data, which is misleading.
This change detects the backend-delegated case (a transcript whose message
entries are exclusively role: user) and surfaces a clear warning in the command
reply text and the exported HTML header.
* fix(auto-reply): correct template literal closure in export session HTML
* fix(auto-reply): warn on backend-delegated session exports
* fix(session): harden backend export warning
* fix(session): read persisted ACP export metadata
* fix(session): keep ACP metadata lookup best-effort
* fix(session): avoid shadowed export locals
* test(session): type export store mock
---------
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat: correlate native search outcomes in audit history
Metadata-only audit ledger for agent runs and tool actions in the shared
state DB: stable event identity, closed action/status/error vocabularies,
one-way-hashed tool-call ids, never-inferred terminal outcomes for native
web-search (explicit completed/failed only; otherwise unknown), bounded
retention, audit.list gateway RPC and openclaw audit CLI. Squashed from
the 82-commit audit stack for replay onto current main.
* feat(audit): add audit.enabled config gate (default on)
The metadata-only audit ledger records by default: an audit trail enabled
only after an incident cannot explain the incident, and the rows are
strictly less sensitive than the transcripts every install already
stores. audit.enabled=false stops new writes at the gateway subscription
seam; audit.list and openclaw audit keep serving existing records until
they expire. Documented in the configuration reference, protocol page,
and CLI reference.
* fix: repair full-matrix CI findings after rebase
- break the dynamic-tools/dynamic-tool-execution import cycle by
extracting resolveCodexToolAbortTerminalReason into a leaf module
- restore main's session-worktree protocol exports lost in the
index.ts auto-merge
- register the audit event writer worker as a knip entry point
- docs table formatting; subagent wait-cancellation test scoped to its
audit intent (outcome + timing) and advanced past main's new
lifecycle-timeout retry grace
* fix: release stale reply work during reset cleanup
* fix: release archived reply runs during reset
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* [AI] fix(memory): add batch completed and batch failed logs for embedding ops
The embedBatchWithRetry function logged 'batch start' but never logged
'batch completed' or 'batch failed' after the embedding batch call,
leaving operators with no post-request feedback. When batches hang or
time out, only 'batch start' appears in logs with no diagnostic signal.
Add 'batch completed' log after runEmbeddingOperationWithTimeout success
and 'batch failed' log in the catch handler. This is an observability
improvement, not a functional fix for the underlying hang (#93312).
Related to #93312
* [AI] fix(memory): use formatErrorMessage for embedding batch error log
Replace String(err) with formatErrorMessage(err) in the batch failed
catch handler to redact sensitive provider error text (e.g. API keys
and tokens embedded in error messages) before logging.
Related to #94732
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(openai): treat a config apiKey as configured for image generation
The openai image provider's isConfigured only consulted env vars / auth
profiles (isProviderApiKeyConfigured) and considered a config apiKey only
inside that env/profile-gated branch. A provider apiKey supplied directly in
config (models.providers.openai.apiKey) — e.g. an AI-gateway token alongside a
custom baseUrl — was reported as not-configured, even though the generate path
resolves exactly that credential via resolveApiKeyForProvider and honors the
config baseUrl. This made image generation behave differently from chat models,
which authenticate purely from provider config.
Recognize a config apiKey as configured so image generation works purely from
config, like chat, with no OPENAI_API_KEY env var or auth profile. Env/profile
and Codex/ChatGPT-OAuth branches are unchanged. Formatting verified with oxfmt
--check (no node_modules in this worktree); full tests run in CI/Testbox.
* fix(openai): require a non-empty config apiKey for image readiness
* fix(openai): normalize config apiKey readiness via hasConfiguredSecretInput
---------
Co-authored-by: Alex Knight <15041791+amknight@users.noreply.github.com>