Commit Graph

9320 Commits

Author SHA1 Message Date
Rob Dunn
ddea5458d0 cron: log model+token usage per run + add usage report script 2026-02-16 23:58:38 +01:00
tian Xiao
edbc68e9f1 feat: support Z.AI tool_stream for real-time tool call streaming
Add support for Z.AI's native tool_stream parameter to enable real-time
visibility into model reasoning and tool call execution.

- Automatically inject tool_stream=true for zai/z-ai providers
- Allow disabling via params.tool_stream: false in model config
- Follows existing pattern of OpenRouter and OpenAI wrappers

This enables Z.AI API features described in:
https://docs.z.ai/api-reference#streaming

AI-assisted: Claude (OpenClaw agent) helped write this implementation.
Testing: lightly tested (code review + pattern matching existing wrappers)

Closes #18135
2026-02-16 23:58:35 +01:00
ranausmanai
c529e6005a fix(gateway): set explicit chat timeouts for mesh gateway calls 2026-02-16 23:58:23 +01:00
ranausmanai
16e59b26a6 Add mesh auto-planning with chat command UX and hardened auth/session behavior 2026-02-16 23:58:23 +01:00
ranausmanai
83990ed542 Add mesh orchestration gateway methods with DAG execution and retry 2026-02-16 23:58:23 +01:00
Parker Todd Brooks
15fe87e6b7 feat: add before_message_write plugin hook
Synchronous hook that lets plugins inspect and optionally block messages
before they are written to the session JSONL file. Primary use case is
private mode... when enabled, the plugin returns { block: true } and the
message never gets persisted.

The hook runs on the hot path (synchronous, like tool_result_persist).
Handlers execute sequentially in priority order. If any handler returns
{ block: true }, the write is skipped immediately. Handlers can also
return a modified message to write instead of the original.

Changes:
- src/plugins/types.ts: add hook name, event/result types, handler map entry
- src/plugins/hooks.ts: add runBeforeMessageWrite() following tool_result_persist pattern
- src/agents/session-tool-result-guard.ts: invoke hook before every originalAppend() call
- src/agents/session-tool-result-guard-wrapper.ts: wire hook runner to the guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:58:12 +01:00
Winston
94eecaa446 fix: atomic session store writes to prevent context loss on Windows
On Windows, fs.promises.writeFile truncates the target file to 0 bytes
before writing. Since loadSessionStore reads the file synchronously
without holding the write lock, a concurrent read can observe the empty
file, fail to parse it, and fall through to an empty store — causing the
agent to lose its session context.

Changes:
- saveSessionStoreUnlocked (Windows path): write to a temp file first,
  then rename it onto the target. If rename fails due to file locking,
  retry 3 times with backoff, then fall back to copyFile (which
  overwrites in-place without truncating to 0 bytes).
- loadSessionStore: on Windows, retry up to 3 times with 50ms
  synchronous backoff (via Atomics.wait) when the file is empty or
  unparseable, giving the writer time to finish. SharedArrayBuffer is
  allocated once and reused across retry attempts.
2026-02-16 23:57:21 +01:00
Rain
1bef2fc68b fix(whatsapp): allow per-message link preview override\n\nWhatsApp messages default to enabling link previews for URLs. This adds\nsupport for overriding this behavior per-message via the \nparameter (e.g. from tool options), consistent with Telegram.\n\nFix: Updated internal WhatsApp Web API layers to pass option\ndown to Baileys . 2026-02-16 23:57:09 +01:00
misterdas
312a7f7880 fix: make tool exit code handling less aggressive
Treat normal process exits (even with non-zero codes) as completed tool results.
This prevents standard exit codes (like grep exit 1) from being surfaced
as 'Tool Failure' warnings in the UI. The exit code is still appended
to the tool output for assistant awareness.
2026-02-16 23:56:56 +01:00
Buddy (AI)
91903bac15 fix: include OPENCLAW_SERVICE_VERSION in system presence version detection
The gateway's system-presence.ts was not detecting the version when
OpenClaw is run as a launchd service, because the daemon-runtime.ts
sets OPENCLAW_SERVICE_VERSION but system-presence.ts only checked
OPENCLAW_VERSION and npm_package_version.

This caused 'openclaw status' to show 'unknown' for the version.

Issue: #18456

🤖 AI-assisted (lightly tested)
2026-02-16 23:56:10 +01:00
Rick Qian
5d9a026a9e gateway: hard-cap chat.history oversized payloads 2026-02-16 23:56:05 +01:00
Peter Steinberger
97e0f8d551 fix(onboarding): keep wildcard allowFrom helper string-typed 2026-02-16 22:55:59 +00:00
Peter Steinberger
64f5e4a424 refactor(onboarding): reuse allowlist merge across channels 2026-02-16 22:55:59 +00:00
Peter Steinberger
486b7379d4 refactor(test): dedupe doctor harness mock payload factories 2026-02-16 22:55:59 +00:00
Peter Steinberger
230e1d9962 refactor(auth): share profile id dedupe helper 2026-02-16 22:55:59 +00:00
Peter Steinberger
ff7a735115 refactor(onboarding): share allowlist merge helpers 2026-02-16 22:55:59 +00:00
Echo
1dfacd4dd1 fix(status): avoid bot+app token warning for mattermost 2026-02-16 23:55:56 +01:00
Tom Peri
b57d29d833 fix(slack): extract text and media from forwarded message attachments 2026-02-16 23:55:34 +01:00
SK Heavy Industries
4928717b92 fix: handle Qwen 3 reasoning field in Ollama responses
Qwen 3 (and potentially other reasoning-capable models served via Ollama)
returns its final answer in a `reasoning` field with an empty `content`
field. This causes blank/empty responses since OpenClaw only reads `content`.

Changes:
- Add `reasoning?` to OllamaChatResponse message type
- Fall back to `reasoning` when `content` is empty in buildAssistantMessage
- Accumulate `reasoning` chunks during streaming when `content` is empty

This allows Qwen 3 to work correctly both with and without /no_think mode.
2026-02-16 23:55:31 +01:00
Ty Sabs
46bf210e04 fix: always drop orphaned OpenAI reasoning blocks in session history
downgradeOpenAIReasoningBlocks was only called on model change, but
orphaned reasoning items (e.g. from an aborted stream) can exist without
a model switch and cause a 400 from the OpenAI Responses API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:55:28 +01:00
Krish
0a02b91638 Handle Telegram poll vote updates for agent context 2026-02-16 23:54:56 +01:00
Krish
5cbfaf5cc7 Add Telegram polls action to config typing 2026-02-16 23:54:56 +01:00
Krish
b2fe44b1ee Fix lint in telegram poll action handler 2026-02-16 23:54:56 +01:00
Krish
c43e95e011 Default Telegram polls to public 2026-02-16 23:54:56 +01:00
Krish
556b531a14 Fix Telegram poll action wiring 2026-02-16 23:54:56 +01:00
Mitsuyuki Osabe
afd354c482 fix: add catalog validation to models set command
`models set` accepts any syntactically valid model ID without checking
the catalog, allowing typos to silently persist in config and fail at
runtime. It also unconditionally adds an empty `{}` entry to
`agents.defaults.models`, bypassing any provider routing constraints.

This commit:
- Validates the model ID against the catalog (skipped when catalog is
  empty during initial setup)
- Warns when a new entry is added with empty config (no provider routing)

Closes openclaw/openclaw#17183

✍️ Author: Claude Code with @carrotRakko (AI-written, human-approved)
2026-02-16 23:54:52 +01:00
Rami Abdelrazzaq
0b8b95f2c9 fix(update): prevent gateway crash loop after failed self-update
The gateway unconditionally scheduled a SIGUSR1 restart after every
update.run call, even when the update itself failed (broken deps,
build errors, etc.). This left the process restarting into a broken
state — corrupted node_modules, partial builds — causing a crash loop
that required manual intervention.

Three fixes:

1. Only restart on success: scheduleGatewaySigusr1Restart is now
   gated on result.status === "ok". Failed or skipped updates still
   write the restart sentinel (so the status can be reported back to
   the user) but the running gateway stays alive.

2. Early bail on step failure: deps install, build, and ui:build now
   check exit codes immediately (matching the preflight section) so a
   failed deps install no longer cascades into a broken build and
   ui:build.

3. Auto-repair config during update: the doctor step now runs with
   --fix alongside --non-interactive, so unknown config keys left over
   from schema changes between versions are stripped automatically
   instead of causing a startup validation crash.
2026-02-16 23:54:49 +01:00
wu-tian807
671f913123 feat: support per-model thinkingDefault override in models config
The global `agents.defaults.thinkingDefault` forces a single thinking
level for all models.  Users running multiple models with different
reasoning capabilities (e.g. Claude with extended thinking, GPT-4o
without, Gemini Flash with lightweight reasoning) cannot optimise the
thinking level per model.

Add an optional `thinkingDefault` field to `AgentModelEntryConfig` so
each entry under `agents.defaults.models` can declare its own default.
Resolution priority: per-model → global → catalog auto-detect.

Example config:

    "models": {
      "anthropic/claude-sonnet-4-20250514": { "thinkingDefault": "high" },
      "openai/gpt-4o":                      { "thinkingDefault": "off" }
    }

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-16 23:54:45 +01:00
Ocean Vael
e368c36503 feat: add llms.txt discovery as default agent behavior
Add automatic llms.txt awareness so agents check for /llms.txt or
/.well-known/llms.txt when exploring new domains.

Changes:
- System prompt: new 'llms.txt Discovery' section (full mode only,
  when web_fetch is available) instructing agents to check for llms.txt
  files when visiting new domains
- web_fetch tool: updated description to mention llms.txt discovery

llms.txt is an emerging standard (like robots.txt for AI) that helps
site owners describe how AI agents should interact with their content.
Making this a default behavior helps the ecosystem adopt agent-native
web experiences.

Ref: https://llmstxt.org
2026-02-16 23:54:40 +01:00
artale
4df970d711 fix: improve error for unconfigured local providers (ollama/vllm) (#17328)
When a user sets `agents.defaults.model.primary: "ollama/gemma3:4b"`
but forgets to set OLLAMA_API_KEY, the error is a confusing
"unknown model: ollama/gemma3:4b". The Ollama provider requires any
dummy API key to register (the local server doesn't actually check it),
but this isn't obvious from the error.

Add `buildUnknownModelError()` that detects known local providers
(ollama, vllm) and appends an actionable hint with the env var name
and a link to the relevant docs page.

Before: Unknown model: ollama/gemma3:4b
After:  Unknown model: ollama/gemma3:4b. Ollama requires authentication
        to be registered as a provider. Set OLLAMA_API_KEY="ollama-local"
        (any value works) or run "openclaw configure".
        See: https://docs.openclaw.ai/providers/ollama

Closes #17328
2026-02-16 23:54:31 +01:00
OpenClaw Bot
b2d622cfa3 fix: clear stale device-auth token on token mismatch
When the gateway connection fails due to device token mismatch (e.g., after
re-pairing the device), clear the stored device-auth token so that
subsequent connection attempts can obtain a fresh token.

This fixes the cron tool failing with 'device token mismatch' error
after running 'openclaw configure' to re-pair the device.

Fixes #18175
2026-02-16 23:54:23 +01:00
Mahsum Aktas
0ee3480690 fix(cron): preserve model fallbacks when agent overrides primary
When an agent config specifies `model: { primary: "..." }` without
an explicit `fallbacks` array, the existing code replaced the entire
model object from `agents.defaults`—discarding the default fallbacks.

This caused cron jobs (and agent sessions) to have only one model
candidate (the pinned model) plus the global primary as a final
fallback, skipping all intermediate fallback models.

The fix merges the agent model override into the existing defaults
model object using spread, so that keys like `fallbacks` survive
when the agent only overrides `primary`. Agents can still explicitly
override or clear fallbacks by providing their own `fallbacks` array.

Reproduction scenario:
- `agents.defaults.model = { primary: "codex", fallbacks: ["opus", "flash", "deepseek"] }`
- Agent config: `model: { primary: "codex" }`
- Cron job pins: `model: "flash"`
- Before fix: fallback candidates = [flash, codex] (3 models lost)
- After fix: fallback candidates = [flash, opus, deepseek, ..., codex]
2026-02-16 23:54:17 +01:00
Joshua Mitchell
5a3a448bc4 feat(commands): add /subagents spawn command
Add a `spawn` action to the /subagents command handler that invokes
spawnSubagentDirect() to deterministically launch a named subagent.

Usage: /subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]

Also includes the shared subagent-spawn module extraction (same as the
refactor/extract-shared-subagent-spawn branch) since it hasn't merged yet.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 23:54:14 +01:00
Saurabh.Chopade
bb5ce3b02f CLI: preserve message send components payload 2026-02-16 23:54:08 +01:00
Sriram Naidu Thota
63fb998074 fix: address code review feedback
- Use stricter regex: /^[A-Za-z0-9+/]*={0,2}$/ ensures = only at end
- Normalize URL-safe base64 to standard (- → +, _ → /)
- Added tests for padding in wrong position and URL-safe normalization

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 23:53:54 +01:00
Sriram Naidu Thota
38c96bc53e fix: validate base64 image data before API submission
Adds explicit base64 format validation in sanitizeContentBlocksImages()
to prevent invalid image data from being sent to the Anthropic API.

The Problem:
- Node's Buffer.from(str, "base64") silently ignores invalid characters
- Invalid base64 passes local validation but fails at Anthropic's stricter API
- Once corrupted data persists in session history, every API call fails

The Fix:
- Add validateAndNormalizeBase64() function that:
  - Strips data URL prefixes (e.g., "data:image/png;base64,...")
  - Validates base64 character set with regex
  - Checks for valid padding (0-2 '=' chars)
  - Validates length is proper for base64 encoding
- Invalid images are replaced with descriptive text blocks
- Prevents permanent session corruption

Tests:
- Rejects invalid base64 characters
- Strips data URL prefixes correctly
- Rejects invalid padding
- Rejects invalid length
- Handles empty data gracefully

Closes #18212

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 23:53:54 +01:00
yinghaosang
aeec95f870 fix(gateway): include deliveryContext in update.run restart sentinel (#18239) 2026-02-16 23:53:50 +01:00
Ignacio
d43c11c76d test: update tests and comments to reflect new autoSelectFamily default
- Update test expectation: 'defaults to enable on Node 22'
- Update comment in fetch.ts to explain IPv4 fallback rationale
- Addresses greptile review feedback
2026-02-16 23:53:44 +01:00
Ignacio
c762bf71f6 fix(telegram): enable autoSelectFamily by default for Node.js 22+
Fixes issue where Telegram fails to send messages when IPv6 is configured
but not functional on the network.

Problem:
- Many networks (especially in Latin America) have IPv6 configured but
  not properly routed by ISP/router
- Node.js tries IPv6 first, gets 'Network is unreachable' error
- With autoSelectFamily=false, Node doesn't fallback to IPv4
- Result: All Telegram API calls fail

Solution:
- Change default from false to true for Node.js 22+
- This enables automatic IPv4 fallback when IPv6 fails
- Config option channels.telegram.network.autoSelectFamily still available
  for users who need to override

Symptoms fixed:
- Health check: Telegram | WARN | failed (unknown) - fetch failed
- Logs: Network request for 'sendMessage' failed
- Bot receives messages but cannot send replies

Tested on:
- macOS 26.2 (Sequoia)
- Node.js v22.15.0
- OpenClaw 2026.2.12
- Network with IPv6 configured but not routed
2026-02-16 23:53:44 +01:00
Yao
3ec936d1b4 fix(daemon): prefer current node and add macOS version manager paths to service PATH 2026-02-16 23:53:41 +01:00
Yao
1a8548df18 fix(daemon): prefer current node (process.execPath) and add macOS version manager paths to service PATH
On macOS, `openclaw gateway install` hardcodes the system node
(/opt/homebrew/bin/node) in the launchd plist, ignoring the node from
version managers (fnm/nvm/volta). This causes the Gateway to run a
different node version than the user's shell environment.

Two fixes:

1. `resolvePreferredNodePath` now checks `process.execPath` first.
   If the currently running node is a supported version, use it directly.
   This respects the user's active version manager selection.

2. `buildMinimalServicePath` now includes version manager bin directories
   on macOS (fnm, nvm, volta, pnpm, bun), matching the existing Linux
   behavior.

Fixes #18090
Related: #6061, #6064
2026-02-16 23:53:41 +01:00
David Szarzynski
30ee12e40a gateway: wire channel health monitor into startup with configurable interval 2026-02-16 23:53:35 +01:00
David Szarzynski
497e2d76ad feat(gateway): add channel health monitor with auto-restart 2026-02-16 23:53:35 +01:00
David Szarzynski
68489a213f gateway: expose isManuallyStopped and resetRestartAttempts on ChannelManager 2026-02-16 23:53:35 +01:00
Xinhua Gu
ae0b110e44 fix(security): set 0o600 on remaining session file write paths
Follow-up to #18066 — three session file write sites were missed:

- auto-reply/reply/session.ts: forked session transcript header
- pi-embedded-runner/session-manager-init.ts: session file reset
- gateway/server-methods/sessions.ts: compacted transcript rewrite

All now use mode 0o600 consistent with transcript.ts and chat.ts.
2026-02-16 23:53:28 +01:00
Artemii
d4c057f8c1 feat(inbound-meta): expose sender_id in trusted system metadata
Add sender_id (ctx.SenderId) to the openclaw.inbound_meta.v1 payload
so agents can reference it for moderation actions (delete, ban, etc.)
without relying on user-controlled text fields.

message_id and chat_id were already present; sender_id was the missing
piece needed for complete group moderation workflows.
2026-02-16 23:53:24 +01:00
康熙
bcab2469de feat: LLM-based query expansion for FTS mode
When searching in FTS-only mode (no embedding provider), extract meaningful
keywords from conversational queries using LLM to improve search results.

Changes:
- New query-expansion module with keyword extraction
- Supports English and Chinese stop word filtering
- Null safety guards for FTS-only mode (provider can be null)
- Lint compliance fixes for string iteration

This helps users find relevant memory entries even with vague queries.
2026-02-16 23:53:21 +01:00
康熙
65aedac20e fix: enable FTS fallback when no embedding provider available (#17725)
When no embedding provider is available (e.g., OAuth mode without API keys),
memory_search now falls back to FTS-only mode instead of returning disabled: true.

Changes:
- embeddings.ts: return null provider with reason instead of throwing
- manager.ts: handle null provider, use FTS-only search mode
- manager-search.ts: allow searching all models when provider is undefined
- memory-tool.ts: expose search mode in results

The search results now include a 'mode' field indicating 'hybrid' or 'fts-only'.
2026-02-16 23:53:21 +01:00
康熙
153794080e fix: support OAuth for Gemini media understanding
Extract parseGeminiAuth() to shared infra module and use it in both
embeddings-gemini.ts and inline-data.ts.

Previously, inline-data.ts directly set x-goog-api-key header without
handling OAuth JSON format. Now it properly supports both traditional
API keys and OAuth tokens.
2026-02-16 23:53:21 +01:00
康熙
3379b9d341 fix: support OAuth for Gemini embedding API
Add parseGeminiAuth() to detect OAuth JSON format ({"token": "...", "projectId": "..."})
and use Bearer token authentication instead of x-goog-api-key header.

This allows OAuth users (using gemini-cli-auth extension) to use memory_search
with Gemini embedding API.
2026-02-16 23:53:21 +01:00