Anthropic's API rejects `anyOf` in `input_schema`, causing all Claude
requests to fail when the image tool is registered. Replace
`Type.Union([Type.String(), Type.Array(Type.String())])` with
`Type.String()` — the execute handler already normalizes both string
and array inputs, so this is schema-only.
Fixes#18551
Make before_agent_start override merging preserve the first defined
model/provider override so higher-priority hooks cannot be overwritten by
lower-priority handlers, and align the corresponding test title and
expectation with the intended precedence behavior.
Co-authored-by: Cursor <cursoragent@cursor.com>
Layer 1: Hook merger tests verify modelOverride/providerOverride are
correctly propagated through the before_agent_start merger with
priority ordering, backward compatibility, and field isolation.
Layer 2: Pipeline wiring tests verify the earlyHookResult passthrough
contract between run.ts and attempt.ts, graceful error degradation,
and that overrides correctly modify provider/model variables.
19 tests total across 2 test files.
Enable plugins to override the model and provider for agent runs by
returning modelOverride/providerOverride from the before_agent_start
hook. The hook is now invoked early in run.ts (before resolveModel)
so overrides take effect. The result is passed to attempt.ts via
earlyHookResult to prevent double-firing.
This enables security-critical use cases like routing PII-containing
prompts to local models instead of cloud providers.
Add transcript size monitoring to /status and session_status tool.
Displays file size and message count (e.g. '📄 Transcript: 1.2 MB,
627 messages'). Shows ⚠️ warning when transcript exceeds 1 MB, which
helps catch sessions approaching the compaction death spiral described
in #13624.
- getTranscriptInfo() reads JSONL file stat + line count
- Wired into both /status command and session_status tool
- 8 new tests covering file reading, formatting, and edge cases
Add optional urlAllowlist config at tools.web level that restricts which
URLs can be accessed by web tools:
- Config types (types.tools.ts): Add urlAllowlist?: string[] to tools.web
- Zod schema: Add urlAllowlist field to ToolsWebSchema
- Schema help: Add help text for the new config fields
- web_search: Filter Brave search results by allowlist (provider=brave)
- web_fetch: Block URLs not matching allowlist before fetching
- ssrf.ts: Export normalizeHostnameAllowlist and matchesHostnameAllowlist
URL matching supports:
- Exact domain match (example.com)
- Wildcard patterns (*.github.com)
When urlAllowlist is not configured, all URLs are allowed (backwards compatible).
Tests: Add web-tools.url-allowlist.test.ts with 23 tests covering:
- URL allowlist resolution from config
- Wildcard pattern matching
- web_fetch error response format
- Brave search result filtering
Previously, the synchronization of credentials to the agent's file was limited to OAuth profiles. This prevented other providers and credential types from being correctly registered for agent use.
This update expands the synchronization to include , (mappedto ), and credentials for all configured providers.
It ensures the agent's accurately reflects available credentials, enabling proper authentication and model discovery.
The synchronization now:
- Converts all supported credential types.
- Skips profiles with empty keys.
- Preserves unrelated entries in the target .
- Only writes to disk when actual changes are detected.
When streaming providers (GLM, OpenRouter, etc.) return 'stop reason: abort'
due to stream interruption, OpenClaw's failover mechanism did not recognize
this as a timeout condition. This prevented fallback models from being
triggered, leaving users with failed requests instead of graceful failover.
Changes:
- Add abort patterns to ERROR_PATTERNS.timeout in pi-embedded-helpers/errors.ts
- Extend TIMEOUT_HINT_RE regex to include abort patterns in failover-error.ts
Fixes#18453
Co-authored-by: James <james@openclaw.ai>
Fixes flashing conhost.exe windows on Windows when exec module spawns
child processes. The windowsHide: true option prevents orphaned conhost.exe
processes and eliminates disruptive terminal window flashing.
Closes#18613
Process trees (pty sessions, tool exec) were being SIGKILL'd immediately
without any grace period for cleanup. This prevented child processes from:
- Flushing buffers and closing files cleanly
- Closing network connections
- Terminating their own child processes
- Removing temporary files
Changes:
- Send SIGTERM to process group first (Unix)
- Wait configurable grace period (default 3s)
- Then SIGKILL if process still alive
- Windows: taskkill without /F first, then with /F after grace period
- Use unref() on timeout to not block event loop exit
Fixes#18619
Co-authored-by: James <james@openclaw.ai>
When /new rotates <session>.jsonl to <session>.jsonl.reset.*, the session-memory hook may read an empty active transcript and write header-only memory entries.
Add fallback logic to read the latest .jsonl.reset.* sibling when the primary file has no usable content.
Also add a unit test covering the rotated transcript path.
Fixes#18088
Refs #17563
* CLI: clarify config vs configure descriptions
* CLI: improve top-level command descriptions
* CLI: make direct command help more descriptive
* CLI: add commands hint to root help
* CLI: show root help hint in implicit help output
* CLI: add help example for command-specific help
* CLI: tweak root subcommand marker spacing
* CLI: mark clawbot as subcommand root in help
* CLI: derive subcommand markers from registry metadata
* CLI: escape help regex CLI name
Bare numeric Discord IDs (e.g. '1470130713209602050') in cron
delivery.to caused 'Ambiguous Discord recipient' errors and silent
delivery failures.
Adds normalizeDiscordOutboundTarget() to the existing Discord
normalize module (channels/plugins/normalize/discord.ts) alongside
normalizeDiscordMessagingTarget. Defaults bare numeric IDs to
'channel:<id>', matching existing behavior.
Both the Discord extension plugin and standalone outbound adapter
use the shared helper via a one-liner resolveTarget.
Fixes#14753. Related: #13927
The test verifies that cooldownUntil IS cleared when it equals exactly
`now` (>= comparison), but the test name said "does not clear". Fixed
the name to match the actual assertion behavior.
When an auth profile hits a rate limit, `errorCount` is incremented and
`cooldownUntil` is set with exponential backoff. After the cooldown
expires, the time-based check correctly returns false — but `errorCount`
persists. The next transient failure immediately escalates to a much
longer cooldown because the backoff formula uses the stale count:
60s × 5^(errorCount-1), max 1h
This creates a positive feedback loop where profiles appear permanently
stuck after rate limits, requiring manual JSON editing to recover.
Add `clearExpiredCooldowns()` which sweeps all profiles on every call to
`resolveAuthProfileOrder()` and clears expired `cooldownUntil` /
`disabledUntil` values along with resetting `errorCount` and
`failureCounts` — giving the profile a fair retry window (circuit-breaker
half-open → closed transition).
Key design decisions:
- `cooldownUntil` and `disabledUntil` handled independently (a profile
can have both; only the expired one is cleared)
- `errorCount` reset only when ALL unusable windows have expired
- `lastFailureAt` preserved for the existing failureWindowMs decay logic
- In-memory mutation; disk persistence happens lazily on the next store
write, matching the existing save pattern
Fixes#3604
Related: #13623, #15851, #11972, #8434