Skip to content

fix(errors): show clear billing error instead of cryptic API response (#8136)#8391

Merged
quotentiroler merged 2 commits intoopenclaw:mainfrom
Glucksberg:fix/8136-api-credits-error
Feb 5, 2026
Merged

fix(errors): show clear billing error instead of cryptic API response (#8136)#8391
quotentiroler merged 2 commits intoopenclaw:mainfrom
Glucksberg:fix/8136-api-credits-error

Conversation

@Glucksberg
Copy link
Contributor

@Glucksberg Glucksberg commented Feb 4, 2026

Fixes #8136

When an LLM API provider returns a billing/credits error (HTTP 402, insufficient credits, etc.), OpenClaw passed through the raw cryptic error text.

Changes:

  • Wired existing isBillingErrorMessage() into formatAssistantErrorText() and sanitizeUserFacingText()
  • Both now return: "⚠️ API provider returned a billing error — your API key has run out of credits or has an insufficient balance. Check your provider's billing dashboard and top up or switch to a different API key."
  • Added billing-specific message in the profile-exhaustion failover path via isBillingAssistantError
  • 3 new tests covering credit balance errors, HTTP 402, and insufficient credits

Greptile Overview

Greptile Summary

This PR improves user-facing error handling for LLM provider billing/credits failures by routing billing-ish messages through isBillingErrorMessage() and returning a consistent friendly explanation instead of raw API payloads. It updates both formatAssistantErrorText() and sanitizeUserFacingText(), and adds a billing-specific message in the auth-profile failover path. New Vitest coverage exercises common billing indicators (credit balance, HTTP 402, "insufficient credits").

Main things to watch are consistency and maintainability of the new copy: there are now multiple hardcoded variants of the billing message across helpers and the runner, and the tests currently assert only broad substrings rather than the full intended message.

Confidence Score: 4/5

  • This PR is safe to merge with low functional risk.
  • Changes are localized to error-message formatting and a failover message branch, with added unit tests covering the new billing classification. The main concerns are UX consistency/duplication rather than correctness regressions.
  • src/agents/pi-embedded-runner/run.ts (hardcoded billing message variant), src/agents/pi-embedded-helpers/errors.ts (duplicated copy)

Context used:

  • Context from dashboard - CLAUDE.md (source)
  • Context from dashboard - AGENTS.md (source)

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 609 to 613
? "LLM request timed out."
: rateLimitFailure
? "LLM request rate limited."
: authFailure
? "LLM request unauthorized."
: "LLM request failed.");
: billingFailure
? "⚠️ API provider returned a billing error — check your API key credits/balance."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Duplicate / inconsistent billing message text.

formatAssistantErrorText() and sanitizeUserFacingText() now return a specific long billing message, but the failover fallback here hardcodes a shorter, different variant ("check your API key credits/balance"). That can lead to inconsistent UX depending on which path is hit. Consider reusing the same formatted message (e.g., via formatAssistantErrorText(lastAssistant, ...) or a shared constant) so billing errors are consistently worded.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/run.ts
Line: 609:613

Comment:
[P2] Duplicate / inconsistent billing message text.

`formatAssistantErrorText()` and `sanitizeUserFacingText()` now return a specific long billing message, but the failover fallback here hardcodes a shorter, different variant ("check your API key credits/balance"). That can lead to inconsistent UX depending on which path is hit. Consider reusing the same formatted message (e.g., via `formatAssistantErrorText(lastAssistant, ...)` or a shared constant) so billing errors are consistently worded.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 371 to 376
if (isBillingErrorMessage(raw)) {
return (
"⚠️ API provider returned a billing error — your API key has run out of credits " +
"or has an insufficient balance. Check your provider's billing dashboard and top up or " +
"switch to a different API key."
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Billing message duplicated in two helpers.

The same long billing string is duplicated in both formatAssistantErrorText() and sanitizeUserFacingText(), which makes future edits/error-copy changes easy to miss. If you expect this copy to evolve, consider extracting it to a shared constant/function so both paths stay in sync.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers/errors.ts
Line: 371:376

Comment:
[P3] Billing message duplicated in two helpers.

The same long billing string is duplicated in both `formatAssistantErrorText()` and `sanitizeUserFacingText()`, which makes future edits/error-copy changes easy to miss. If you expect this copy to evolve, consider extracting it to a shared constant/function so both paths stay in sync.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 56 to 73
it("returns a friendly billing message for credit balance errors", () => {
const msg = makeAssistantError("Your credit balance is too low to access the Anthropic API.");
const result = formatAssistantErrorText(msg);
expect(result).toContain("billing error");
expect(result).toContain("credits");
});
it("returns a friendly billing message for HTTP 402 errors", () => {
const msg = makeAssistantError("HTTP 402 Payment Required");
const result = formatAssistantErrorText(msg);
expect(result).toContain("billing error");
expect(result).toContain("credits");
});
it("returns a friendly billing message for insufficient credits", () => {
const msg = makeAssistantError("insufficient credits");
const result = formatAssistantErrorText(msg);
expect(result).toContain("billing error");
expect(result).toContain("balance");
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P3] Tests assert substrings but not exact copy.

These new tests only check for a couple substrings (e.g. "billing error", "credits"), so they would still pass if the function accidentally returned a different error classification that happens to include those words. If the goal is to lock in the user-facing copy for billing errors, asserting the full expected string (or at least a more specific prefix) would make the tests more regression-resistant.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-helpers.formatassistanterrortext.test.ts
Line: 56:73

Comment:
[P3] Tests assert substrings but not exact copy.

These new tests only check for a couple substrings (e.g. "billing error", "credits"), so they would still pass if the function accidentally returned a different error classification that happens to include those words. If the goal is to lock in the user-facing copy for billing errors, asserting the full expected string (or at least a more specific prefix) would make the tests more regression-resistant.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Feb 4, 2026
@Glucksberg Glucksberg force-pushed the fix/8136-api-credits-error branch from 4f4e198 to 94fe23a Compare February 4, 2026 05:11
@Glucksberg
Copy link
Contributor Author

[P2] Duplicate billing message text: Addressed — all three call sites (formatAssistantErrorText, sanitizeUserFacingText, and run.ts) use the single BILLING_ERROR_USER_MESSAGE constant exported from errors.ts. No inline string duplication.

[P3] Billing message in two helpers: Both formatAssistantErrorText and sanitizeUserFacingText need the billing check because they serve different code paths (agent error formatting vs general text sanitization). Using the same constant ensures consistency.

[P3] Tests assert exact copy: The tests already use .toBe(BILLING_ERROR_USER_MESSAGE) (not substring matching), which fails if the message drifts.

…w error (openclaw#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits
@Glucksberg Glucksberg force-pushed the fix/8136-api-credits-error branch from 2308906 to b2ec5cf Compare February 5, 2026 21:37
Copy link
Contributor

@quotentiroler quotentiroler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean implementation using a single constant for the billing message across all paths. Tests cover HTTP 402, credit balance, and "insufficient credits" cases.

@quotentiroler quotentiroler merged commit d4c5608 into openclaw:main Feb 5, 2026
17 of 23 checks passed
slathrop referenced this pull request in slathrop/openclaw-js Feb 6, 2026
… (#8391)

- Add BILLING_ERROR_USER_MESSAGE constant
- Detect billing errors in formatAssistantErrorText and sanitizeUserFacingText
- Add billingFailure detection in run loop failover path
- Add 3 test cases for credit balance, HTTP 402, and insufficient credits
@Glucksberg Glucksberg deleted the fix/8136-api-credits-error branch February 7, 2026 14:45
batao9 pushed a commit to batao9/openclaw that referenced this pull request Feb 7, 2026
…openclaw#8391)

* fix(errors): return clear billing error message instead of cryptic raw error (openclaw#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits

* fix: extract billing error message to shared constant
hughdidit pushed a commit to hughdidit/DAISy-Agency that referenced this pull request Feb 8, 2026
…openclaw#8391)

* fix(errors): return clear billing error message instead of cryptic raw error (openclaw#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits

* fix: extract billing error message to shared constant

(cherry picked from commit d4c5608)
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 11, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 12, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 12, 2026
heybeaux pushed a commit to heybeaux/openclaw that referenced this pull request Feb 12, 2026
…openclaw#8391)

* fix(errors): return clear billing error message instead of cryptic raw error (openclaw#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits

* fix: extract billing error message to shared constant
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 13, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 14, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 14, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 15, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 15, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 15, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 16, 2026
park285 added a commit to park285/openclaw that referenced this pull request Feb 16, 2026
Cryptic API 응답 대신 명확한 billing error 메시지 표시.
Upstream: openclaw/openclaw@d4c560853
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 16, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 17, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 17, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 17, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 18, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 18, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 18, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 19, 2026
maxtongwang added a commit to maxtongwang/openclaw that referenced this pull request Feb 19, 2026
* fix(control-ui): resolve header logo when gateway.controlUi.basePath is set (#7178)

* fix(control-ui): resolve header logo when gateway.controlUi.basePath is set

* refactor(control-ui): header logo under basePath; normalize logo URL with normalizeBasePath

* docs: add changelog for #7178 (thanks @Yeom-JinHo)

* docs: document secure DM mode preset (#7872)

* docs: document secure DM mode preset

* fix: resolve merge conflict in resizable-divider

* fix(security): separate untrusted channel metadata from system prompt (thanks @KonstantinMirin)

* docs: update Feishu plugin docs

* feat: Add Docs Chat Widget with RAG-powered Q&A (#7908)

* feat: add docs chat prototype and related scripts

- Introduced a minimal documentation chatbot that builds a search index from markdown files and serves responses via an API.
- Added scripts for building the index and serving the chat API.
- Updated package.json with new commands for chat index building and serving.
- Created a new Vercel configuration file for deployment.
- Added a README for the docs chat prototype detailing usage and integration.

* feat: enhance docs chat with vector-based RAG pipeline

- Added vector index building and serving capabilities to the docs chat.
- Introduced new scripts for generating embeddings and serving the chat API using vector search.
- Updated package.json with new commands for vector index operations.
- Enhanced README with instructions for the new RAG pipeline and legacy keyword pipeline.
- Removed outdated Vercel configuration file.

* feat: enhance chat widget with markdown rendering and style updates

- Integrated dynamic loading of markdown rendering for chat responses.
- Implemented a fallback for markdown rendering to ensure consistent display.
- Updated CSS variables for improved theming and visual consistency.
- Enhanced chat bubble and input styles for better user experience.
- Added new styles for markdown content in chat bubbles, including code blocks and lists.

* feat: add copy buttons to chat widget for enhanced user interaction

- Implemented copy buttons for chat responses and code blocks in the chat widget.
- Updated CSS styles for improved visibility and interaction of copy buttons.
- Adjusted textarea height for better user experience.
- Enhanced functionality to allow users to easily copy text from chat bubbles and code snippets.

* feat: update chat widget styles for improved user experience

- Changed accent color for better visibility.
- Enhanced preformatted text styles for code blocks, including padding and word wrapping.
- Adjusted positioning and styles of copy buttons for chat responses and code snippets.
- Improved hover effects for copy buttons to enhance interactivity.

* feat: enhance chat widget styles for better responsiveness and scrollbar design

- Updated chat panel dimensions for improved adaptability on various screen sizes.
- Added custom scrollbar styles for better aesthetics and usability.
- Adjusted chat bubble styles for enhanced visibility and interaction.
- Improved layout for expanded chat widget on smaller screens.

* feat: refine chat widget code block styles and copy button functionality

- Adjusted padding and margin for preformatted text in chat responses for better visual consistency.
- Introduced a compact style for single-line code blocks to enhance layout.
- Updated copy button logic to skip short code blocks, improving user experience when copying code snippets.

* feat: add resize handle functionality to chat widget for adjustable panel width

- Implemented a draggable resize handle for the chat widget's sidebar, allowing users to adjust the panel width.
- Added CSS styles for the resize handle, including hover effects and responsive behavior.
- Integrated drag-to-resize logic to maintain user-set width across interactions.
- Ensured the panel resets to default width when closed, enhancing user experience.

* feat: implement rate limiting and error handling in chat API

- Added rate limiting functionality to the chat API, allowing a maximum number of requests per IP within a specified time window.
- Implemented error handling for rate limit exceeded responses, including appropriate headers and retry instructions.
- Enhanced error handling for other API errors, providing user-friendly messages for various failure scenarios.
- Updated README to include new environment variables for rate limiting configuration.

* feat: integrate Upstash Vector for enhanced document retrieval in chat API

- Implemented Upstash Vector as a cloud-based storage solution for document chunks, replacing the local LanceDB option.
- Added auto-detection of storage mode based on environment variables for seamless integration.
- Updated the chat API to utilize the new retrieval mechanism, enhancing response accuracy and performance.
- Enhanced README with setup instructions for Upstash and updated environment variable requirements.
- Introduced new scripts and configurations for managing the vector index and API interactions.

* feat: add create-markdown-preview.js for markdown rendering

- Introduced a new script for framework-agnostic HTML rendering of markdown content.
- The script includes various parsing functions to handle different markdown elements.
- Updated the chat widget to load the vendored version of @create-markdown/preview for improved markdown rendering.

* docs: update README for Upstash Vector index setup and environment variables

- Enhanced instructions for creating a Vector index in Upstash, including detailed settings and important notes.
- Clarified environment variable requirements for both Upstash and LanceDB modes.
- Improved formatting and organization of setup steps for better readability.
- Added health check and API endpoint details for clearer usage guidance.

* feat: add TRUST_PROXY environment variable for IP address handling

- Introduced the TRUST_PROXY variable to control the trust of X-Forwarded-For headers when behind a reverse proxy.
- Updated the README to document the new environment variable and its default value.
- Enhanced the getClientIP function to conditionally trust proxy headers based on the TRUST_PROXY setting.

* feat: add ALLOWED_ORIGINS environment variable for CORS configuration

- Introduced the ALLOWED_ORIGINS variable to specify allowed origins for CORS, enhancing security and flexibility.
- Updated the README to document the new environment variable and its usage.
- Refactored CORS handling in the server code to utilize the ALLOWED_ORIGINS setting for dynamic origin control.

* fix: ensure complete markdown rendering in chat widget

- Added logic to flush any remaining buffered bytes from the decoder, ensuring that all text is rendered correctly in the assistant bubble.
- Updated the assistant bubble's innerHTML to reflect the complete markdown content after streaming completes.

* feat: enhance DocsStore with improved vector handling and similarity conversion

- Added a constant for the distance metric used in vector searches, clarifying the assumption of L2 distance.
- Updated the createTable method to ensure all chunk properties are correctly mapped during table creation.
- Improved the similarity score calculation by providing a clear explanation of the conversion from L2 distance, ensuring accurate ranking of results.

* chore: fix code formatting

* Revert "chore: fix code formatting"

This reverts commit 6721f5b0b7bf60b76c519ccadfa41742f19ecf87.

* chore: format code for improved readability

- Reformatted code in serve.ts to enhance readability by adjusting indentation and line breaks.
- Ensured consistent style for function return types and object properties throughout the file.

* feat: Update API URL selection logic in chat widget

- Enhanced the API URL configuration to prioritize explicit settings, defaulting to localhost for development and using a production URL otherwise.
- Improved clarity in the code by adding comments to explain the logic behind the API URL selection.

* chore: Update documentation structure for improved organization

- Changed the path for the "Start Here" page to "start/index" for better clarity.
- Reformatted the "Web & Interfaces" and "Help" groups to use multi-line arrays for improved readability.

* feat: Enhance markdown preview integration and improve chat widget asset loading

- Wrapped the markdown preview functionality in an IIFE to expose a global API for easier integration.
- Updated the chat widget to load the markdown preview library dynamically, checking for existing instances to avoid duplicate loads.
- Adjusted asset paths in the chat widget to ensure correct loading based on the environment (local or production).
- Added CORS headers in the Vercel configuration for improved API accessibility.

* fix: Update chat API URL to include '/api' for correct endpoint access

- Modified the chat configuration and widget files to append '/api' to the API URL, ensuring proper endpoint usage in production and local environments.

* refactor: Simplify docs-chat configuration and remove unused scripts

- Removed outdated scripts and configurations related to the docs-chat feature, including build and serve scripts, as well as the associated package.json and README files.
- Streamlined the API URL configuration in the chat widget for better clarity and maintainability.
- Updated the package.json to remove unnecessary scripts related to the now-deleted functionality.

* refactor: Update documentation structure for improved clarity

- Changed the path for the "Start Here" page from "start/index" to "index" to enhance navigation and organization within the documentation.

* chore: Remove unused dependencies from package.json and pnpm-lock.yaml

- Deleted `@lancedb/lancedb`, `@upstash/vector`, and `openai` from both package.json and pnpm-lock.yaml to streamline the project and reduce bloat.

* chore: Clean up .gitignore by removing obsolete entries

- Deleted unused entries related to the docs-chat vector database from .gitignore to maintain a cleaner configuration.

* chore: Remove deprecated chat configuration and markdown preview script

- Deleted the `create-markdown-preview.js` script and the `docs-chat-config.js` file to eliminate unused assets and streamline the project.
- Updated the `docs-chat-widget.js` to directly reference the markdown library from a CDN, enhancing maintainability.

* chore: Update markdown rendering in chat widget to use marked library

- Replaced the deprecated `create-markdown-preview` library with the `marked` library for markdown rendering.
- Adjusted the script loading mechanism to fetch `marked` from a CDN, improving performance and maintainability.
- Enhanced the markdown rendering function to ensure security by disabling HTML pass-through and opening links in new tabs.

* Delete docs/start/index.md

* fix: harden voice-call webhook verification

* fix(cron): fix timeout, add timestamp validation, enable file sync

Fixes #7667

Task 1: Fix cron operation timeouts
- Increase default gateway tool timeout from 10s to 30s
- Increase cron-specific tool timeout to 60s
- Increase CLI default timeout from 10s to 30s
- Prevents timeouts when gateway is busy with long-running jobs

Task 2: Add timestamp validation
- New validateScheduleTimestamp() function in validate-timestamp.ts
- Rejects atMs timestamps more than 1 minute in the past
- Rejects atMs timestamps more than 10 years in the future
- Applied to both cron.add and cron.update operations
- Provides helpful error messages with current time and offset

Task 3: Enable file sync for manual edits
- Track file modification time (storeFileMtimeMs) in CronServiceState
- Check file mtime in ensureLoaded() and reload if changed
- Recompute next runs after reload to maintain accuracy
- Update mtime after persist() to prevent reload loop
- Dashboard now picks up manual edits to ~/.openclaw/cron/jobs.json

* feat(cron): introduce delivery modes for isolated jobs

- Added support for new delivery modes in cron jobs: `announce`, `deliver`, and `none`.
- Updated documentation to reflect changes in delivery options and usage examples.
- Enhanced the cron job schema to include delivery configuration.
- Refactored related CLI commands and UI components to accommodate the new delivery settings.
- Improved handling of legacy delivery fields for backward compatibility.

This update allows users to choose how output from isolated jobs is delivered, enhancing flexibility in job management.

* feat(cron): default isolated jobs to announce delivery and enhance scheduling options

- Updated isolated cron jobs to default to `announce` delivery mode, improving user experience.
- Enhanced scheduling options to accept ISO 8601 timestamps for `schedule.at`, while still supporting epoch milliseconds.
- Refined documentation to clarify delivery modes and scheduling formats.
- Adjusted related CLI commands and UI components to reflect these changes, ensuring consistency across the platform.
- Improved handling of legacy delivery fields for backward compatibility.

This update streamlines the configuration of isolated jobs, making it easier for users to manage job outputs and schedules.

* feat(cron): enhance one-shot job behavior and CLI options

- Default one-shot jobs to delete after success, improving job management.
- Introduced `--keep-after-run` CLI option to allow users to retain one-shot jobs post-execution.
- Updated documentation to clarify default behaviors and new options for one-shot jobs.
- Adjusted cron job creation logic to ensure consistent handling of delete options.
- Enhanced tests to validate new behaviors and ensure reliability.

This update streamlines the handling of one-shot jobs, providing users with more control over job persistence and execution outcomes.

* feat(cron): enhance delivery modes and job configuration

- Updated isolated cron jobs to support new delivery modes: `announce` and `none`, improving output management.
- Refactored job configuration to remove legacy fields and streamline delivery settings.
- Enhanced the `CronJobEditor` UI to reflect changes in delivery options, including a new segmented control for delivery mode selection.
- Updated documentation to clarify the new delivery configurations and their implications for job execution.
- Improved tests to validate the new delivery behavior and ensure backward compatibility with legacy settings.

This update provides users with greater flexibility in managing how isolated jobs deliver their outputs, enhancing overall usability and clarity in job configurations.

* feat(cron): set default enabled state for cron jobs

- Added logic to default the `enabled` property to `true` if not explicitly set as a boolean in the cron job input.
- Updated job creation and store functions to ensure consistent handling of the `enabled` state across the application.
- Enhanced input normalization to improve job configuration reliability.

This update ensures that cron jobs are enabled by default, enhancing user experience and reducing potential misconfigurations.

* refactor(cron): update delivery instructions for isolated agent

- Revised the delivery instructions in the isolated agent's command body to clarify that summaries should be returned as plain text and will be delivered by the main agent.
- Removed the previous directive regarding messaging tools to streamline communication guidelines.

This change enhances clarity in the delivery process for isolated agent tasks.

* feat(cron): enhance delivery handling and testing for isolated jobs

- Introduced new properties for explicit message targeting and message tool disabling in the EmbeddedRunAttemptParams type.
- Updated cron job tests to validate best-effort delivery behavior and handling of delivery failures.
- Added logic to clear delivery settings when switching session targets in cron jobs.
- Improved the resolution of delivery failures and best-effort logic in the isolated agent's run function.

This update enhances the flexibility and reliability of delivery mechanisms in isolated cron jobs, ensuring better handling of message delivery scenarios.

* refactor(cron): improve delivery configuration handling in CronJobEditor and CLI

- Enhanced the delivery configuration logic in CronJobEditor to explicitly set the bestEffort property based on job settings.
- Refactored the CLI command to streamline delivery object creation, ensuring proper handling of optional fields like channel and to.
- Improved code readability and maintainability by restructuring delivery assignment logic.

This update clarifies the delivery configuration process, enhancing the reliability of job settings in both the editor and CLI.

* feat(cron): enhance legacy delivery handling in job patches

- Introduced logic to map legacy payload delivery updates onto the delivery object for `agentTurn` jobs, ensuring backward compatibility with legacy clients.
- Added tests to validate the correct application of legacy delivery settings in job patches, improving reliability in job configuration.
- Refactored delivery handling functions to streamline the merging of legacy delivery fields into the current job structure.

This update enhances the flexibility of delivery configurations, ensuring that legacy settings are properly handled in the context of new job patches.

* fix(cron): fix test failures and regenerate protocol files

- Add forceReload option to ensureLoaded to avoid stat I/O in normal
  paths while still detecting cross-service writes in the timer path
- Post isolated job summary back to main session (restores the old
  isolation.postToMainPrefix behavior via delivery model)
- Update legacy migration tests to check delivery.channel instead of
  payload.channel (normalization now moves delivery fields to top-level)
- Remove legacy deliver/channel/to/bestEffortDeliver from payload schema
- Update protocol conformance test for delivery modes
- Regenerate GatewayModels.swift (isolation -> delivery)

* UI: handle future timestamps in formatAgo

* Changelog: move cron entries to 2026.2.3

* fix: cron announce delivery path (#8540) (thanks @tyler6204)

* Telegram: use Grammy types directly, add typed Probe/Audit to plugin interface (#8403)

* Telegram: replace duplicated types with Grammy imports, add Probe/Audit generics to plugin interface

* Telegram: remove legacy forward metadata (deprecated in Bot API 7.0), simplify required-field checks

* Telegram: clean up remaining legacy references and unnecessary casts

* Telegram: keep RequestInit parameter type in proxy fetch (addresses review feedback)

* Telegram: add exhaustiveness guard to resolveForwardOrigin switch

* fix(telegram): include forward_from_chat metadata in forwarded message context (#8133)

Extract missing metadata from forwarded Telegram messages:

- Add fromChatType to TelegramForwardedContext, capturing the original
  chat type (channel/supergroup/group) from forward_from_chat.type
  and forward_origin.chat/sender_chat.type
- Add fromMessageId to capture the original message ID from channel forwards
- Read author_signature from forward_origin objects (modern API),
  preferring it over the deprecated forward_signature field
- Pass ForwardedFromChatType and ForwardedFromMessageId through to
  the inbound context payload
- Add test coverage for forward_origin channel/chat types, including
  author_signature extraction and fromChatType propagation

* fix: trim legacy signature fallback, type fromChatType as union

* fix: telegram forward metadata + cron delivery guard (#8392) (thanks @Glucksberg)

* fix(imessage): unify timeout configuration with configurable probeTimeoutMs

- Add probeTimeoutMs config option to channels.imessage
- Export DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS constant (10s) from probe.ts
- Propagate timeout config through all iMessage probe/RPC operations
- Fix hardcoded 2000ms timeouts that were too short for SSH connections

Closes: timeout issues when using SSH wrapper scripts (imsg-ssh)

* fix: address review comments

- Use optional timeoutMs parameter (undefined = use config/default)
- Extract DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS to shared constants.ts
- Import constant in client.ts instead of hardcoding
- Re-export constant from probe.ts for backwards compatibility

* fix(imessage): detect self-chat echoes to prevent infinite loops (#8680)

* fix: align proxy fetch typing

* feat: add cloudflare ai gateway provider

* fix: force reload cron store

* Revert "feat: Add Docs Chat Widget with RAG-powered Q&A (#7908)" (#8834)

This reverts commit fa4b28d7af7464b07271bfef6c028e4135548f44.

* fix(web ui): agent model selection

* Docs: landing page revamp (#8885)

* Docs: refresh landing page

* Docs: add landing page companion pages

* Docs: drop legacy Jekyll assets

* Docs: remove legacy terminal css test

* Docs: restore terminal css assets

* Docs: remove terminal css assets

* fix(app-render): handle optional model in renderApp function

* chore: replace landpr prompt with end-to-end landing workflow (#8916)

* 🤖 docs: mirror landing revamp for zh-CN

What:
- add zh-CN versions of landing revamp pages (features, quickstart, docs directory, network model, credits)
- refresh zh-CN index and hubs, plus glossary entries

Why:
- keep Chinese docs aligned with the new English landing experience
- ensure navigation surfaces the new entry points

Tests:
- pnpm build && pnpm check && pnpm test

* 🤖 docs: note zh-CN landing revamp (#8994) (thanks @joshp123)

What:
- add changelog entry for the zh-CN landing revamp docs

Why:
- record the doc update and thank the contributor

Tests:
- pnpm lint && pnpm build && pnpm test

* feat: add shell completion installation prompt to CLI update command

* feat: add shell completion test script for installation verification

* completion: export cache utilities and require cached file for installation

- Export `resolveCompletionCachePath` and `completionCacheExists` for external use
- Update `installCompletion` to require cache existence (never use slow dynamic pattern)
- Add `usesSlowDynamicCompletion` to detect old `source <(...)` patterns
- Add `getShellProfilePath` helper for consistent profile path resolution
- Update `formatCompletionSourceLine` to always use cached file

* doctor: add shell completion check module

- Add `checkShellCompletionStatus` to get profile/cache/slow-pattern status
- Add `ensureCompletionCacheExists` for silent cache regeneration
- Add `doctorShellCompletion` to check and fix completion issues:
  - Auto-upgrade old slow dynamic patterns to cached version
  - Auto-regenerate cache if profile exists but cache is missing
  - Prompt to install if no completion is configured

* doctor: integrate shell completion check into doctor command

- Import and call `doctorShellCompletion` during doctor run
- Checks/fixes completion issues before gateway health check

* update: use shared completion helpers for shell completion setup

- Replace inline completion logic with `checkShellCompletionStatus` and `ensureCompletionCacheExists`
- Auto-upgrade old slow dynamic patterns silently during update
- Auto-regenerate cache if profile exists but cache is missing
- Prompt to install if no completion is configured

* onboard: use shared completion helpers for shell completion setup

- Replace inline completion logic with `checkShellCompletionStatus` and `ensureCompletionCacheExists`
- Auto-upgrade old slow dynamic patterns silently during onboarding
- Auto-regenerate cache if profile exists but cache is missing
- Prompt to install if no completion is configured

* scripts: update test-shell-completion to use shared helpers

- Use `checkShellCompletionStatus` and `ensureCompletionCacheExists` from doctor-completion
- Display "Uses slow pattern" status in output
- Simulate doctor/update/onboard behavior for all completion scenarios
- Remove duplicated utility functions

* changelog: add shell completion auto-fix entry

* feat: per-channel responsePrefix override (#9001)

* feat: per-channel responsePrefix override

Add responsePrefix field to all channel config types and Zod schemas,
enabling per-channel and per-account outbound response prefix overrides.

Resolution cascade (most specific wins):
  L1: channels.<ch>.accounts.<id>.responsePrefix
  L2: channels.<ch>.responsePrefix
  L3: (reserved for channels.defaults)
  L4: messages.responsePrefix (existing global)

Semantics:
  - undefined -> inherit from parent level
  - empty string -> explicitly no prefix (stops cascade)
  - "auto" -> derive [identity.name] from routed agent

Changes:
  - Core logic: resolveResponsePrefix() in identity.ts accepts
    optional channel/accountId and walks the cascade
  - resolveEffectiveMessagesConfig() passes channel context through
  - Types: responsePrefix added to WhatsApp, Telegram, Discord, Slack,
    Signal, iMessage, Google Chat, MS Teams, Feishu, BlueBubbles configs
  - Zod schemas: responsePrefix added for config validation
  - All channel handlers wired: telegram, discord, slack, signal,
    imessage, line, heartbeat runner, route-reply, native commands
  - 23 new tests covering backward compat, channel/account levels,
    full cascade, auto keyword, empty string stops, unknown fallthrough

Fully backward compatible - no existing config is affected.
Fixes #8857

* fix: address CI lint + review feedback

- Replace Record<string, any> with proper typed helpers (no-explicit-any)
- Add curly braces to single-line if returns (eslint curly)
- Fix JSDoc: 'Per-channel' → 'channel/account' on shared config types
- Extract getChannelConfig() helper for type-safe dynamic key access

* fix: finish responsePrefix overrides (#9001) (thanks @mudrii)

* fix: normalize prefix wiring and types (#9001) (thanks @mudrii)

---------

Co-authored-by: Gustavo Madeira Santana <[email protected]>

* Discord: allow disabling thread starter context

* feat(heartbeat): add accountId config option for multi-agent routing (#8702)

* feat(heartbeat): add accountId config option for multi-agent routing

Add optional accountId field to heartbeat configuration, allowing
multi-agent setups to explicitly specify which Telegram account
should be used for heartbeat delivery.

Previously, heartbeat delivery would use the accountId from the
session's deliveryContext. When a session had no prior conversation
history, heartbeats would default to the first/primary account
instead of the agent's intended bot.

Changes:
- Add accountId to HeartbeatSchema (zod-schema.agent-runtime.ts)
- Use heartbeat.accountId with fallback to session accountId (targets.ts)

Backward compatible: if accountId is not specified, behavior is unchanged.

Closes #8695

* fix: improve heartbeat accountId routing (#8702) (thanks @lsh411)

* fix: harden heartbeat accountId routing (#8702) (thanks @lsh411)

* fix: expose heartbeat accountId in status (#8702) (thanks @lsh411)

* chore: format status + heartbeat tests (#8702) (thanks @lsh411)

---------

Co-authored-by: m1 16 512 <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>

* TUI/Gateway: fix pi streaming + tool routing + model display + msg updating (#8432)

* TUI/Gateway: fix pi streaming + tool routing

* Tests: clarify verbose tool output expectation

* fix: avoid seq gaps for targeted tool events (#8432) (thanks @gumadeiras)

* Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)

* Telegram: remove @ts-nocheck from bot.ts and bot-message-dispatch.ts

- bot/types.ts: TelegramContext.me uses UserFromGetMe (Grammy) instead of manual inline type
- bot.ts: remove 6 unsafe casts (as any, as unknown, as object), use Grammy types directly
- bot.ts: remove dead message_thread_id access on reactions (not in Telegram Bot API)
- bot.ts: remove resolveThreadSessionKeys import (no longer needed for reactions)
- bot-message-dispatch.ts: replace ': any' with DispatchTelegramMessageParams type
- bot-message-dispatch.ts: add sticker.fileId guard before cache access
- bot.test.ts: update reaction tests, remove dead DM thread-reaction test

* Telegram: remove duplicate bot.catch handler (only the last one runs in Grammy)

* Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)

* Security: Prevent gateway credential exfiltration via URL override (#9179)

* Gateway: require explicit auth for url overrides

* Gateway: scope credential blocking to non-local URLs only

Address review feedback: the previous fix blocked credential fallback for
ALL URL overrides, which was overly strict and could break workflows that
use --url to switch between loopback/tailnet without passing credentials.

Now credential fallback is only blocked for non-local URLs (public IPs,
external hostnames). Local addresses (127.0.0.1, localhost, private IPs
like 192.168.x.x, 10.x.x.x, tailnet 100.x.x.x) still get credential
fallback as before.

This maintains the security fix (preventing credential exfiltration to
attacker-controlled URLs) while preserving backward compatibility for
legitimate local URL overrides.

* Security: require explicit credentials for gateway url overrides (#8113) (thanks @victormier)

* Gateway: reuse explicit auth helper for url overrides (#8113) (thanks @victormier)

* Tests: format gateway chat test (#8113) (thanks @victormier)

* Tests: require explicit auth for gateway url overrides (#8113) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Tests: restore TUI gateway env

* Security: harden sandboxed media handling (#9182)

* Message: enforce sandbox for media param

* fix: harden sandboxed media handling (#8780) (thanks @victormier)

* chore: format message action runner (#8780) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Telegram: remove @ts-nocheck from bot-message.ts (#9180)

* Telegram: remove @ts-nocheck from bot-message.ts, type deps via Omit<BuildTelegramMessageContextParams>

* Telegram: widen allMedia to TelegramMediaRef[] so stickerMetadata flows through

* Telegram: remove @ts-nocheck from bot-message.ts (#9180)

* fix: cover anonymous voice allowlist callers (#8104) (thanks @victormier) (#9188)

* Security: owner-only tools + command auth hardening (#9202)

* Security: gate whatsapp_login by sender auth

* Security: treat undefined senderAuthorized as unauthorized (opt-in)

* fix: gate whatsapp_login to owner senders (#8768) (thanks @victormier)

* fix: add explicit owner allowlist for tools (#8768) (thanks @victormier)

* fix: normalize escaped newlines in send actions (#8768) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)

* Telegram: remove @ts-nocheck from bot-handlers.ts, use Grammy types directly, deduplicate StickerMetadata

* Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)

* Message: clarify media schema + fix MEDIA newline

* fix: enforce owner allowlist for commands

* fix: infer --auth-choice from API key flags during non-interactive onboarding (#9241)

* fix: infer --auth-choice from API key flags during non-interactive onboarding

When --anthropic-api-key (or other provider key flags) is passed without
an explicit --auth-choice, the auth choice defaults to "skip", silently
discarding the API key. This means the gateway starts without credentials
and fails on every inbound message with "No API key found for provider".

Add inferAuthChoiceFromFlags() to derive the correct auth choice from
whichever provider API key flag was supplied, so credentials are persisted
to auth-profiles.json as expected.

Fixes #8481

* fix: infer auth choice from API key flags (#8484) (thanks @f-trycua)

* refactor: centralize auth choice inference flags (#8484) (thanks @f-trycua)

---------

Co-authored-by: f-trycua <[email protected]>

* chore: sync plugin versions to 2026.2.3

* fix(mac): resolve cron schedule formatters

* chore(mac): update appcast for 2026.2.3

* chore: update 2026.2.3 notes

* fix: gracefully downgrade xhigh thinking level in cron isolated agent (#9363)

When thinkingDefault is set to "xhigh" but the configured model does not
support it (e.g. Claude), the cron isolated-agent path throws a hard error
causing the job to fail. The interactive chat path already handles this by
silently downgrading to "high".

Apply the same graceful downgrade in the cron path: log a warning and
fall back to "high" instead of crashing.

Co-authored-by: hyf0-agent <[email protected]>

* fix: restore discord owner hint from allowlists

* fix: remove unused cron import

* fix(cli): resolve bundled chrome extension path

* test(cli): use unique temp dir for extension install

* fix(cli): support bundled extension path in dist root

* style(cli): satisfy lint rules in extension path resolver

* fix: resolve bundled chrome extension assets (#8914) (thanks @kelvinCB)

* Tests: add test coverage for security/windows-acl.ts

Adds comprehensive unit tests for Windows ACL inspection utilities:
- resolveWindowsUserPrincipal: username resolution with fallback
- parseIcaclsOutput: icacls output parsing
- summarizeWindowsAcl: ACL entry classification (trusted/world/group)
- inspectWindowsAcl: async ACL inspection with mocked exec
- formatWindowsAclSummary: summary string formatting
- formatIcaclsResetCommand: reset command string generation
- createIcaclsResetCommand: structured reset command generation

All 26 tests passing.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: stabilize windows acl tests and command auth registry (#9335) (thanks @M00N7682)

* test: register discord plugin in allowlist test

* chore: bump version to 2026.2.4

* fix: resolve discord owner allowFrom matches

* fix(telegram): preserve DM topic threadId in deliveryContext

When receiving messages in Telegram DM topics (Topics in Private Chats),
the threadId was not saved in the session's deliveryContext, causing
replies to go to General chat instead of the topic.

Now we pass threadId to updateLastRoute for DM topics.

Fixes #8891

* test(telegram): add DM topic threadId deliveryContext test for #8891

Verifies that threadId is passed to updateLastRoute for DM topics.
Test fails on main branch, passes with the fix.

* fix: preserve telegram DM topic threadId (#9039) (thanks @lailoo)

* Update deps.

* chore: Typecheck test helper files.

* Docs: streamline start and install docs (#9648)

* docs(start): streamline getting started flow

* docs(nav): reorganize start and install sections

* docs(style): move custom css to style.css

* docs(navigation): align zh-CN ordering

* docs(navigation): localize zh-Hans labels

* docs(install): rename install overview page

* CLI: sort commands alphabetically in help output

Fixes #7964

Added sortSubcommands: true to configureHelp() to display
commands in alphabetical order when running 'openclaw --help'.

* fix: update changelog for help sorting (#8068) (thanks @deepsoumya617)

* docs(onboarding): add bootstrapping page (#9767)

* docs: fix onboarding rendering issues

* chore: reset appcast to 2026.2.3

* fix(telegram): pass parentPeer for forum topic binding inheritance (#9789)

Fixes #9545 and #9351.

When a message comes from a Telegram forum topic, the peer ID includes
the topic suffix (e.g., `-1001234567890:topic:99`). Users configure
bindings with the base group ID, which previously did not match.

This adds `parentPeer` to `resolveAgentRoute()` calls for forum groups,
enabling binding inheritance from the parent group to all topics.

- Extract `buildTelegramParentPeer()` helper in bot/helpers.ts
- Pass parentPeer in bot-message-context.ts, bot-handlers.ts,
  bot-native-commands.ts, and bot.ts (reaction handler)
- Add tests for forum topic routing and topic precedence

* docs(onboarding): streamline CLI onboarding docs (#9830)

* fix: auto-inject Telegram forum topic threadId in message tool

When using Telegram DM topics (forum topics), messages sent via the
message tool (media, buttons, etc.) land in General Topic instead of
the user's current topic. This happens because Slack has
resolveSlackAutoThreadId for auto-threading but Telegram had no
equivalent.

Add resolveTelegramAutoThreadId that mirrors the Slack pattern:
- When channel is telegram and no explicit threadId is provided
- Check if toolContext.currentThreadTs (the topic ID) is set
- Verify the target matches the originating chat
- Inject the threadId into params so the Telegram plugin action
  handler picks it up for sendMessage/sendMedia

The subagent announce path already correctly passes threadId via
requesterOrigin (set from agentThreadId in sessions-spawn-tool),
so no changes needed there.

* test: cover telegram topic threadId auto-injection and subagent origin threading

* fix: pass threadId/to/accountId from parent to subagent gateway call

When spawning a subagent, the requesterOrigin's threadId, to, and
accountId were not forwarded to the callGateway({method:'agent'}) params.
This meant the subagent's runContext had no currentThreadTs or
currentChannelId, so resolveTelegramAutoThreadId could not auto-inject
the forum topic thread ID when the subagent used the message tool.

Changes:
- sessions-spawn-tool: pass to, accountId, threadId from requesterOrigin
- run-context: populate currentChannelId from opts.to as fallback

Fixes subagent messages landing in General Topic instead of the correct
Telegram DM topic thread.

* fix: telegram topic auto-threading — use parseTelegramTarget, add tests (#7235) (thanks @Lukavyi)

* update handle

* docs: fix incorrect model.fallback to model.fallbacks in Ollama config (#9384) (#9749)

Both English and Chinese documentation had incorrect configuration template
using 'fallback' instead of 'fallbacks' in agents.defaults.model config.

Co-authored-by: damaozi <[email protected]>

* fix(cli): avoid NODE_OPTIONS for --disable-warning (#9691) (thanks @18-RAJAT)

Fixes npm pack failing on modern Node where --disable-warning is disallowed in NODE_OPTIONS.

* feat: add Claude Opus 4.6 to built-in model catalog (#9853)

* feat: add Claude Opus 4.6 to built-in model catalog

- Update default model from claude-opus-4-5 to claude-opus-4-6
- Add opus-4.6 model ID normalization
- Add claude-opus-4-6 to live model filter prefixes
- Update image tool to prefer claude-opus-4-6 for vision
- Add CLI backend alias for opus-4.6
- Update onboard auth default selections to include opus-4.6
- Update model picker placeholder

Closes #9811

* test: update tests for claude-opus-4-6 default

- Fix model-alias-defaults test to use claude-opus-4-6
- Fix image-tool test to expect claude-opus-4-6 in fallbacks

* feat: support claude-opus-4-6

* docs: update changelog for opus 4.6 (#9853) (thanks @TinyTb)

* chore: bump pi to 0.52.0

---------

Co-authored-by: Slurpy <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>

* 🤖 Feishu: expand channel support

What:
- add post parsing, doc link extraction, routing, replies, reactions, typing, and user lookup
- fix media download/send flows and make doc fetches domain-aware
- update Feishu docs and clawtributor credits

Why:
- raise Feishu parity with other channels and avoid dropped group messages
- keep replies threaded while supporting Lark domains
- document new configuration and credit the contributor

Tests:
- pnpm build
- pnpm check
- pnpm test (gateway suite timed out; reran pnpm vitest run --config vitest.gateway.config.ts)

Co-authored-by: 九灵云 <[email protected]>

* 🤖 Feishu: tighten mention gating

What:
- require the bot open_id match for group mention detection when available

Why:
- prevent replies when other users are mentioned and the bot id is known

Tests:
- pnpm test

* fix: remove orphaned tool_results during compaction pruning

When pruneHistoryForContextShare drops chunks of messages, it could drop
an assistant message with tool_use blocks while leaving corresponding
tool_result messages in the kept portion. These orphaned tool_results
cause Anthropic's API to reject the session with 'unexpected tool_use_id'.

Fix by calling repairToolUseResultPairing after each chunk drop to clean
up any orphaned tool_results. This reuses existing battle-tested code
from session-transcript-repair.ts.

Fixes #9769, #9724, #9672

* fix cron scheduling and reminder delivery regressions (#9733)

* fix(cron): prevent timer from allowing process exit (fixes #9694)

The cron timer was using .unref(), which caused the Node.js event
loop to exit or sleep if no other handles were active. This prevented
cron jobs from firing in some environments.

* fix(cron): infer delivery target for isolated jobs (fixes #9683)

When creating isolated agentTurn jobs (e.g. reminders) without explicit
delivery options, the job would default to 'announce' but fail to
resolve the target conversation. Now, we infer the channel and
recipient from the agent's current session key.

* fix(cron): enhance delivery inference for threaded sessions and null inputs (#9733)

Improves the delivery inference logic in the cron tool to correctly handle threaded session keys and cases where delivery is explicitly set to null. This ensures that the appropriate delivery mode and target are inferred based on the agent's session key, enhancing the reliability of job execution.

* fix: preserve telegram topic delivery inference (#9733) (thanks @tyler6204)

* fix: simplify cron delivery merge spread (#9733) (thanks @tyler6204)

* chore: add agent credentials to gitignore (#9874)

Protect sensitive files from accidental commit:
- memory/ (moltbook credentials, session data)
- .agent/*.json (agent config, moltbook.json)

Workflows in .agent/workflows/ remain tracked.

Co-authored-by: Claude Opus 4.5 <[email protected]>

* Docs: escape hash symbol in help channel names in issue template (#9695)

* feat(skills): add QR code skill (#8817)

feat(skills): add QR code generation and reading skill

Adds qr-code skill with:
- qr_generate.py - Generate QR codes with customizable size/error correction
- qr_read.py - Decode QR codes from images
- SKILL.md documentation

Co-authored-by: Omar-Khaleel

* chore(agentsmd): add tsgo command to AGENTS.md (#9894)

Add `pnpm tsgo` command to AGENTS.md development reference

Co-authored-by: vincentkoc <[email protected]>

* fix(runtime): bump minimum Node.js version to 22.12.0 (#5370)

* fix(runtime): bump minimum Node.js version to 22.12.0

Aligns the runtime guard with the declared package.json engines requirement.

The Matrix plugin (and potentially others) requires Node >= 22.12.0,
but the runtime guard previously allowed 22.0.0+. This caused confusing
errors like 'Cannot find module @vector-im/matrix-bot-sdk' when the real
issue was an unsupported Node version.

- Update MIN_NODE from 22.0.0 to 22.12.0
- Update error message to reflect the correct version
- Update tests to use 22.12.0 as the minimum valid version

Fixes #5292

* fix: update test versions to match MIN_NODE=22.12.0

---------

Co-authored-by: Markus Glucksberg <[email protected]>

* fix: clear stale token metrics on /new and /reset (#8929)

When starting a new session via /new or /reset, the token usage fields
(totalTokens, inputTokens, outputTokens, contextTokens) survived from the
previous session via the spread pattern in session init. This caused /status
to display misleading context usage from the old session.

Clear all four token metrics explicitly in the isNewSession block, alongside
the existing compactionCount reset. Also add diagnostic logging for session
forking via ParentSessionKey to help trace context inheritance.

* chore: apply local workspace updates (#9911)

* chore: apply local workspace updates

* fix: resolve prep findings after rebase (#9898) (thanks @gumadeiras)

* refactor: centralize model allowlist normalization (#9898) (thanks @gumadeiras)

* fix: guard model allowlist initialization (#9911)

* docs: update changelog scope for #9911

* docs: remove model names from changelog entry (#9911)

* fix: satisfy type-aware lint in model allowlist (#9911)

* fix: allow multiple compaction retries on context overflow (#8928)

Previously, overflowCompactionAttempted was a boolean flag set once, preventing
recovery when a single compaction wasn't enough. Change to a counter allowing up
to 3 attempts before giving up. Also add diagnostic logging on overflow events to
help debug early-overflow issues.

Fixes sessions that hit context overflow during long agentic turns with many tool
calls, where one compaction round isn't sufficient to bring context below limits.

* fix(errors): show clear billing error instead of cryptic API response (#8391)

* fix(errors): return clear billing error message instead of cryptic raw error (#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits

* fix: extract billing error message to shared constant

* Revert "feat(skills): add QR code skill (#8817)"

This reverts commit ad13c265ba1fd22dadfe30325ed998d9a3d95e5c.

* docs: improve DM security guidance with concrete example

Add a more prominent security warning for multi-user DM setups:
- Add blockquote security warning about context leakage
- Include concrete example showing the privacy risk
- Add "When to enable this" checklist
- Clarify that default is fine for single-user setups

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* docs: tighten secure DM example

* docs: note secure DM guidance update (#9377) (thanks @Shrinija17)

* Agents: bump pi-mono to 0.52.5 (#9949)

* Agents: bump pi-mono to 0.52.5

* Changelog: add PR reference for pi bump

* docs: restructure Get Started tab and improve onboarding flow (#9950)

* docs: restructure Get Started tab and improve onboarding flow

- Flatten nested Onboarding group into linear First Steps flow
- Add 'What is OpenClaw?' narrative section to landing page
- Split wizard.md into streamlined overview + full reference (reference/wizard.md)
- Move Pairing to Channels > Configuration
- Move Bootstrapping to Agents > Fundamentals
- Move macOS app onboarding to Platforms > macOS companion app
- Move Lore to Help > Community
- Remove duplicate install instructions from openclaw.md
- Mirror navigation changes in zh-CN tabs
- No content deleted — all detail preserved or relocated

* docs: move deployment pages to install/, fix Platforms tab routing, clarify onboarding paths

- Move deployment guides (fly, hetzner, gcp, macos-vm, exe-dev, railway, render,
  northflank) from platforms/ and root to install/
- Add 'Hosting and deployment' group to Install tab
- Slim Gateway & Ops 'Remote access and deployment' down to 'Remote access'
- Swap Platforms tab before Gateway & Ops to fix path-prefix routing
- Move macOS app onboarding into First steps (parallel to CLI wizard)
- Rename sidebar titles to 'Onboarding: CLI' / 'Onboarding: macOS App'
- Add redirects for all moved paths
- Update all internal links (en + zh-CN)
- Fix img tag syntax in onboarding.md

* fix(telegram): accept messages from group members in allowlisted groups (#9775)

* fix(telegram): accept messages from group members in allowlisted groups

Issue #4559: Telegram bot was silently dropping messages from non-paired users
in allowlisted group chats due to overly strict sender filtering.

The fix adds a check to distinguish between:
1. Group itself is allowlisted → accept messages from any member
2. Group is NOT allowlisted → only accept from allowlisted senders

Changes:
- Check if group ID is in the allowlist (or allowlist is wildcard)
- Only reject sender if they're not in allowlist AND group is not allowlisted
- Improved logging to indicate the actual reason for rejection

This preserves security controls while fixing the UX issue where group members
couldn't participate unless individually allowlisted.

Backwards compatible: existing allowlists continue to work as before.

* style: format telegram fix for oxfmt compliance

* refactor(telegram): clarify group allowlist semantics in fix for #4559

Changes:
- Rename 'isGroupInAllowlist' to 'isGroupChatIdInAllowlist' for clarity
- Expand comments to explain the semantic distinction:
  * Group chat ID in allowlist -> accept any group member (fixes #4559)
  * Group chat ID NOT in allowlist -> enforce sender allowlist (preserves security)
- This addresses concerns about config semantics raised in code review

The fix maintains backward compatibility:
- 'groupAllowFrom' with group chat IDs now correctly acts as group enablement
- 'groupAllowFrom' with sender IDs continues to work as sender allowlist
- Operators should use group chat IDs for group enablement, sender IDs for sender control

Note: If operators were using 'groupAllowFrom' with group IDs expecting sender-level
filtering, they should migrate to a separate sender allowlist config. This is the
intended behavior per issue #4559.

* Telegram: allow per-group groupPolicy overrides

* Telegram: support per-group groupPolicy overrides (#9775) (thanks @nicolasstanley)

---------

Co-authored-by: George Pickett <[email protected]>

* chore: remove tracked .DS_Store files

* Fix: Enable scrolling on the dashboard config page (#1822)

* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard

* feat: add xAI Grok provider support

* fix(onboard): align xAI default model to grok-4

* chore: changelog for xAI onboarding (#9885) (thanks @grp06)

* fix(cron): prevent recomputeNextRuns from skipping due jobs in onTimer (#9823)

* fix(cron): prevent recomputeNextRuns from skipping due jobs in onTimer

ensureLoaded(forceReload) called recomputeNextRuns before runDueJobs,
which recalculated nextRunAtMs to a strictly future time. Since
setTimeout always fires a few ms late, the due check (now >= nextRunAtMs)
always failed and every/cron jobs never executed. Fixes #9788.

* docs: add changelog entry for cron timer race fix (#9823) (thanks @pycckuu)

---------

Co-authored-by: Tyler Yust <[email protected]>

* fix(cron): re-arm timer in finally to survive transient errors (#9948)

* fix(cron): handle legacy atMs field in schedule when computing next run (#9932)

* fix(cron): handle legacy atMs field in schedule when computing next run

The cron scheduler only checked for `schedule.at` (string) but legacy jobs
may have `schedule.atMs` (number) from before the schema migration.

This caused nextRunAtMs to stay null because:
1. Store migration runs on load but may not persist immediately
2. Race conditions or file mtime issues can skip migration
3. computeJobNextRunAtMs/computeNextRunAtMs only checked `at`, not `atMs`

Fix: Make both functions defensive by checking `atMs` first (number),
then `atMs` (string, for edge cases), then falling back to `at` (string).

This ensures jobs fire correctly even if:
- Migration hasn't run yet
- Old data was written by a previous version
- The store was manually edited

Fixes #9930

* fix: validate numeric atMs to prevent NaN/Infinity propagation

Addresses review feedback - numeric atMs values are now validated with
Number.isFinite() && atMs > 0 before use. This prevents corrupted or
manually edited stores from causing hot timer loops via setTimeout(..., NaN).

* fix(exec-approvals): coerce bare string allowlist entries to objects (#9790)

* fix(exec-approvals): coerce bare string allowlist entries (#9903) (thanks @mcaxtr)

* security: add skill/plugin code safety scanner (#9806)

* security: add skill/plugin code safety scanner module

* security: integrate skill scanner into security audit

* security: add pre-install code safety scan for plugins

* style: fix curly brace lint errors in skill-scanner.ts

* docs: add changelog entry for skill code safety scanner

* style: append ellipsis to truncated evidence strings

* fix(security): harden plugin code safety scanning

* fix: scan skills on install and report code-safety details

* fix: dedupe audit-extra import

* fix(security): make code safety scan failures observable

* fix(test): stabilize smoke + gateway timeouts (#9806) (thanks @abdelsfane)

---------

Co-authored-by: Darshil <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: George Pickett <[email protected]>

* Thinking: accept extra-high alias and sync Codex FAQ wording

* Changelog: note #9976 thinking alias + Codex 5.3 docs sync

* fix: normalize xhigh aliases and docs sync (#9976)

* fix(agents): skip tool extraction for aborted/errored assistant messages (#4598)

Fixes tool call/tool_result pairing issues that cause permanent session corruption when assistant messages have stopReason "error" or "aborted". Includes 4 unit tests.

* fix(cron): handle undefined sessionTarget in list output (#9649) (#9752)

* fix(cron): handle undefined sessionTarget in list output (#9649)

When sessionTarget is undefined, pad() would crash with 'Cannot read
properties of undefined (reading trim)'. Use '-' as fallback value.

* test(cron): add regression test for undefined sessionTarget (#9649)

Verifies that printCronList handles jobs with undefined sessionTarget
without crashing. Test fails on main branch, passes with the fix.

* fix: use correct CronSchedule format in tests (#9752) (thanks @lailoo)

Tests were using { kind: 'at', atMs: number } but the CronSchedule type
requires { kind: 'at', at: string } where 'at' is an ISO date string.

---------

Co-authored-by: damaozi <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>

* chore: Update deps.

* Model: add strict gpt-5.3-codex fallback for OpenAI Codex (fixes #9989) (#9995)

* Model: allow forward-compatible OpenAI Codex GPT-5 IDs

* Model: scope Codex fallback to gpt-5.3-codex

* fix: reorder codex fallback before providerCfg, add ordering test, changelog (#9989) (thanks @w1kke)

---------

Co-authored-by: Robin <[email protected]>

* fix(nextcloud-talk): sign message text instead of JSON body (#2092)

Nextcloud Talk's ChecksumVerificationService verifies HMAC against the
extracted message/reaction text, not the full JSON body. This fixes 401
authentication errors when sending messages via the bot API.

- sendMessageNextcloudTalk: sign 'message' text only
- sendReactionNextcloudTalk: sign 'reaction' string only

* fix(slack): add mention stripPatterns for /new and /reset commands (#9971)

* fix(slack): add mention stripPatterns for /new and /reset commands

Fixes #9937

The Slack dock was missing mentions.stripPatterns that Discord has.
This caused /new and /reset to fail when sent with a mention
(e.g. @bot /reset) because <@USERID> wasn't stripped before matching.

* fix(slack): strip mentions for /new and /reset (#9971) (thanks @ironbyte-rgb)

---------

Co-authored-by: ironbyte-rgb <[email protected]>
Co-authored-by: George Pickett <[email protected]>

* feat(feishu): replace built-in SDK with community plugin

Replace the built-in Feishu SDK with the community-maintained
clawdbot-feishu plugin by @m1heng.

Changes:
- Remove src/feishu/ directory (19 files)
- Remove src/channels/plugins/outbound/feishu.ts
- Remove src/channels/plugins/normalize/feishu.ts
- Remove src/config/types.feishu.ts
- Remove feishu exports from plugin-sdk/index.ts
- Remove FeishuConfig from types.channels.ts

New features in community plugin:
- Document tools (read/create/edit Feishu docs)
- Wiki tools (navigate/manage knowledge base)
- Drive tools (folder/file management)
- Bitable tools (read/write table records)
- Permission tools (collaborator management)
- Emoji reactions support
- Typing indicators
- Rich media support (bidirectional image/file transfer)
- @mention handling
- Skills for feishu-doc, feishu-wiki, feishu-drive, feishu-perm

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(feishu): add targeted eslint-disable comments for SDK integration

Add line-specific eslint-disable-next-line comments for SDK type casts
and union type issues, rather than file-level disables.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(feishu): fix webhook mode silent exit and receive_id_type default

- monitor.ts: throw error for webhook mode instead of silently returning,
  so gateway properly marks channel as failed
- targets.ts: default receive_id_type to "user_id" instead of "open_id"
  for non-prefixed IDs, fixing message delivery for enterprise user IDs

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: update pnpm-lock.yaml for feishu extension deps

Add lockfile entries for:
- @larksuiteoapi/node-sdk@^1.56.1
- @sinclair/[email protected]
- zod@^4.3.6

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* feat(feishu): sync with clawdbot-feishu #137 (multi-account support)

- Sync latest changes from clawdbot-feishu including multi-account support
- Add eslint-disable comments for SDK-related any types
- Remove unused imports
- Fix no-floating-promises in monitor.ts

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* security: redact credentials from config.get gateway responses (#9858)

* security: add skill/plugin code safety scanner module

* security: integrate skill scanner into security audit

* security: add pre-install code safety scan for plugins

* style: fix curly brace lint errors in skill-scanner.ts

* docs: add changelog entry for skill code safety scanner

* security: redact credentials from config.get gateway responses

The config.get gateway method returned the full config snapshot
including channel credentials (Discord tokens, Slack botToken/appToken,
Telegram botToken, Feishu appSecret, etc.), model provider API keys,
and gateway auth tokens in plaintext.

Any WebSocket client—including the unauthenticated Control UI when
dangerouslyDisableDeviceAuth is set—could read every secret.

This adds redactConfigSnapshot() which:
- Deep-walks the config object and masks any field whose key matches
  token, password, secret, or apiKey patterns
- Uses the existing redactSensitiveText() to scrub the raw JSON5 source
- Preserves the hash for change detection
- Includes 15 test cases covering all channel types

* security: make gateway config writes return redacted values

* test: disable control UI by default in gateway server tests

* fix: redact credentials in gateway config APIs (#9858) (thanks @abdelsfane)

---------

Co-authored-by: George Pickett <[email protected]>

* fix: release session locks on process termination (#1962)

Adds cleanup handlers to release held file locks when the process
terminates via SIGTERM, SIGINT, or normal exit. This prevents orphaned
lock files that would block future sessions.

Fixes #1951

* fix(ollama): add streaming config and fix OLLAMA_API_KEY env var support (#9870)

* fix(ollama): add streaming config and fix OLLAMA_API_KEY env var support

Adds configurable streaming parameter to model configuration and sets streaming
to false by default for Ollama models. This addresses the corrupted response
issue caused by upstream SDK bug badlogic/pi-mono#1205 where interleaved
content/reasoning deltas in streaming responses cause garbled output.

Changes:
- Add streaming param to AgentModelEntryConfig type
- Set streaming: false default for Ollama models
- Add OLLAMA_API_KEY to envMap (was missing, preventing env var auth)
- Document streaming configuration in Ollama provider docs
- Add tests for Ollama model configuration

Users can now configure streaming per-model and Ollama authentication
via OLLAMA_API_KEY environment variable works correctly.

Fixes #8839
Related: badlogic/pi-mono#1205

* docs(ollama): use gpt-oss:20b as primary example

Updates documentation to use gpt-oss:20b as the primary example model
since it supports tool calling. The model examples now show:

- gpt-oss:20b as the primary recommended model (tool-capable)
- llama3.3 and qwen2.5-coder:32b as additional options

This provides users with a clear, working example that supports
OpenClaw's tool calling features.

* chore: remove unused vi import from ollama test

* fix: untrack dist/control-ui build artifacts (#1856)

The dist/control-ui/ files were committed before the dist/ gitignore
rule was effective. These build artifacts get regenerated during
builds, causing dirty repo errors that block the auto-update mechanism.

Removes the files from git tracking while keeping them locally and
respecting the existing dist/ gitignore entry.

Fixes #1838

Co-authored-by: Claude <[email protected]>

* chore: add weekly upstream sync workflow

Merges openclaw/openclaw main on a weekly schedule using -X ours
so our fork patches always take priority. Opens a PR for visibility.

Co-Authored-By: Claude <[email protected]>

---------

Co-authored-by: Yeom-JinHo <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Lucas Kim <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>
Co-authored-by: Christian Klotz <[email protected]>
Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Yudong Han <[email protected]>
Co-authored-by: Iranb <[email protected]>
Co-authored-by: Seb Slight <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: Shakker <[email protected]>
Co-authored-by: Josh Palmer <[email protected]>
Co-authored-by: mudrii <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: lsh411 <[email protected]>
Co-authored-by: m1 16 512 <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: Victor Mier <[email protected]>
Co-authored-by: f-trycua <[email protected]>
Co-authored-by: hyf0-agent <[email protected]>
Co-authored-by: hyf0-agent <[email protected]>
Co-authored-by: Kelvin Calcano <[email protected]>
Co-authored-by: M00N7682 <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
Co-authored-by: damaozi <[email protected]>
Co-authored-by: cpojer <[email protected]>
Co-authored-by: sebslight <[email protected]>
Co-authored-by: Soumyadeep Ghosh <[email protected]>
Co-authored-by: Clawdbot <[email protected]>
Co-authored-by: 大猫子 <[email protected]>
Co-authored-by: Rajat Joshi <[email protected]>
Co-authored-by: Michael Lee <[email protected]>
Co-authored-by: Slurpy <[email protected]>
Co-authored-by: 九灵云 <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>
Co-authored-by: Caelum <[email protected]>
Co-authored-by: MattQ <[email protected]>
Co-authored-by: Omar Khaleel <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: vincentkoc <[email protected]>
Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Markus Glucksberg <[email protected]>
Co-authored-by: Shrinija Kummari <[email protected]>
Co-authored-by: George Pickett <[email protected]>
Co-authored-by: nicolasstanley <[email protected]>
Co-authored-by: Daijiro Miyazawa <[email protected]>
Co-authored-by: Igor Markelov <[email protected]>
Co-authored-by: Maksym Brashchenko <[email protected]>
Co-authored-by: fujiwara-tofu-shop <[email protected]>
Co-authored-by: Marcus Castro <[email protected]>
Co-authored-by: Abdel Sy Fane <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: slonce70 <[email protected]>
Co-authored-by: Aisling Cahill <[email protected]>
Co-authored-by: Robin <[email protected]>
Co-authored-by: wangai-studio <[email protected]>
Co-authored-by: ironbyte-rgb <[email protected]>
Co-authored-by: ironbyte-rgb <[email protected]>
Co-authored-by: Yifeng Wang <[email protected]>
Co-authored-by: Sash Zats <[email protected]>
Co-authored-by: Raphael Borg Ellul Vincenti <[email protected]>
Co-authored-by: zerone0x <[email protected]>
maxtongwang added a commit to maxtongwang/openclaw that referenced this pull request Feb 19, 2026
…or command support (#2)

* fix(control-ui): resolve header logo when gateway.controlUi.basePath is set (#7178)

* fix(control-ui): resolve header logo when gateway.controlUi.basePath is set

* refactor(control-ui): header logo under basePath; normalize logo URL with normalizeBasePath

* docs: add changelog for #7178 (thanks @Yeom-JinHo)

* docs: document secure DM mode preset (#7872)

* docs: document secure DM mode preset

* fix: resolve merge conflict in resizable-divider

* fix(security): separate untrusted channel metadata from system prompt (thanks @KonstantinMirin)

* docs: update Feishu plugin docs

* feat: Add Docs Chat Widget with RAG-powered Q&A (#7908)

* feat: add docs chat prototype and related scripts

- Introduced a minimal documentation chatbot that builds a search index from markdown files and serves responses via an API.
- Added scripts for building the index and serving the chat API.
- Updated package.json with new commands for chat index building and serving.
- Created a new Vercel configuration file for deployment.
- Added a README for the docs chat prototype detailing usage and integration.

* feat: enhance docs chat with vector-based RAG pipeline

- Added vector index building and serving capabilities to the docs chat.
- Introduced new scripts for generating embeddings and serving the chat API using vector search.
- Updated package.json with new commands for vector index operations.
- Enhanced README with instructions for the new RAG pipeline and legacy keyword pipeline.
- Removed outdated Vercel configuration file.

* feat: enhance chat widget with markdown rendering and style updates

- Integrated dynamic loading of markdown rendering for chat responses.
- Implemented a fallback for markdown rendering to ensure consistent display.
- Updated CSS variables for improved theming and visual consistency.
- Enhanced chat bubble and input styles for better user experience.
- Added new styles for markdown content in chat bubbles, including code blocks and lists.

* feat: add copy buttons to chat widget for enhanced user interaction

- Implemented copy buttons for chat responses and code blocks in the chat widget.
- Updated CSS styles for improved visibility and interaction of copy buttons.
- Adjusted textarea height for better user experience.
- Enhanced functionality to allow users to easily copy text from chat bubbles and code snippets.

* feat: update chat widget styles for improved user experience

- Changed accent color for better visibility.
- Enhanced preformatted text styles for code blocks, including padding and word wrapping.
- Adjusted positioning and styles of copy buttons for chat responses and code snippets.
- Improved hover effects for copy buttons to enhance interactivity.

* feat: enhance chat widget styles for better responsiveness and scrollbar design

- Updated chat panel dimensions for improved adaptability on various screen sizes.
- Added custom scrollbar styles for better aesthetics and usability.
- Adjusted chat bubble styles for enhanced visibility and interaction.
- Improved layout for expanded chat widget on smaller screens.

* feat: refine chat widget code block styles and copy button functionality

- Adjusted padding and margin for preformatted text in chat responses for better visual consistency.
- Introduced a compact style for single-line code blocks to enhance layout.
- Updated copy button logic to skip short code blocks, improving user experience when copying code snippets.

* feat: add resize handle functionality to chat widget for adjustable panel width

- Implemented a draggable resize handle for the chat widget's sidebar, allowing users to adjust the panel width.
- Added CSS styles for the resize handle, including hover effects and responsive behavior.
- Integrated drag-to-resize logic to maintain user-set width across interactions.
- Ensured the panel resets to default width when closed, enhancing user experience.

* feat: implement rate limiting and error handling in chat API

- Added rate limiting functionality to the chat API, allowing a maximum number of requests per IP within a specified time window.
- Implemented error handling for rate limit exceeded responses, including appropriate headers and retry instructions.
- Enhanced error handling for other API errors, providing user-friendly messages for various failure scenarios.
- Updated README to include new environment variables for rate limiting configuration.

* feat: integrate Upstash Vector for enhanced document retrieval in chat API

- Implemented Upstash Vector as a cloud-based storage solution for document chunks, replacing the local LanceDB option.
- Added auto-detection of storage mode based on environment variables for seamless integration.
- Updated the chat API to utilize the new retrieval mechanism, enhancing response accuracy and performance.
- Enhanced README with setup instructions for Upstash and updated environment variable requirements.
- Introduced new scripts and configurations for managing the vector index and API interactions.

* feat: add create-markdown-preview.js for markdown rendering

- Introduced a new script for framework-agnostic HTML rendering of markdown content.
- The script includes various parsing functions to handle different markdown elements.
- Updated the chat widget to load the vendored version of @create-markdown/preview for improved markdown rendering.

* docs: update README for Upstash Vector index setup and environment variables

- Enhanced instructions for creating a Vector index in Upstash, including detailed settings and important notes.
- Clarified environment variable requirements for both Upstash and LanceDB modes.
- Improved formatting and organization of setup steps for better readability.
- Added health check and API endpoint details for clearer usage guidance.

* feat: add TRUST_PROXY environment variable for IP address handling

- Introduced the TRUST_PROXY variable to control the trust of X-Forwarded-For headers when behind a reverse proxy.
- Updated the README to document the new environment variable and its default value.
- Enhanced the getClientIP function to conditionally trust proxy headers based on the TRUST_PROXY setting.

* feat: add ALLOWED_ORIGINS environment variable for CORS configuration

- Introduced the ALLOWED_ORIGINS variable to specify allowed origins for CORS, enhancing security and flexibility.
- Updated the README to document the new environment variable and its usage.
- Refactored CORS handling in the server code to utilize the ALLOWED_ORIGINS setting for dynamic origin control.

* fix: ensure complete markdown rendering in chat widget

- Added logic to flush any remaining buffered bytes from the decoder, ensuring that all text is rendered correctly in the assistant bubble.
- Updated the assistant bubble's innerHTML to reflect the complete markdown content after streaming completes.

* feat: enhance DocsStore with improved vector handling and similarity conversion

- Added a constant for the distance metric used in vector searches, clarifying the assumption of L2 distance.
- Updated the createTable method to ensure all chunk properties are correctly mapped during table creation.
- Improved the similarity score calculation by providing a clear explanation of the conversion from L2 distance, ensuring accurate ranking of results.

* chore: fix code formatting

* Revert "chore: fix code formatting"

This reverts commit 6721f5b0b7bf60b76c519ccadfa41742f19ecf87.

* chore: format code for improved readability

- Reformatted code in serve.ts to enhance readability by adjusting indentation and line breaks.
- Ensured consistent style for function return types and object properties throughout the file.

* feat: Update API URL selection logic in chat widget

- Enhanced the API URL configuration to prioritize explicit settings, defaulting to localhost for development and using a production URL otherwise.
- Improved clarity in the code by adding comments to explain the logic behind the API URL selection.

* chore: Update documentation structure for improved organization

- Changed the path for the "Start Here" page to "start/index" for better clarity.
- Reformatted the "Web & Interfaces" and "Help" groups to use multi-line arrays for improved readability.

* feat: Enhance markdown preview integration and improve chat widget asset loading

- Wrapped the markdown preview functionality in an IIFE to expose a global API for easier integration.
- Updated the chat widget to load the markdown preview library dynamically, checking for existing instances to avoid duplicate loads.
- Adjusted asset paths in the chat widget to ensure correct loading based on the environment (local or production).
- Added CORS headers in the Vercel configuration for improved API accessibility.

* fix: Update chat API URL to include '/api' for correct endpoint access

- Modified the chat configuration and widget files to append '/api' to the API URL, ensuring proper endpoint usage in production and local environments.

* refactor: Simplify docs-chat configuration and remove unused scripts

- Removed outdated scripts and configurations related to the docs-chat feature, including build and serve scripts, as well as the associated package.json and README files.
- Streamlined the API URL configuration in the chat widget for better clarity and maintainability.
- Updated the package.json to remove unnecessary scripts related to the now-deleted functionality.

* refactor: Update documentation structure for improved clarity

- Changed the path for the "Start Here" page from "start/index" to "index" to enhance navigation and organization within the documentation.

* chore: Remove unused dependencies from package.json and pnpm-lock.yaml

- Deleted `@lancedb/lancedb`, `@upstash/vector`, and `openai` from both package.json and pnpm-lock.yaml to streamline the project and reduce bloat.

* chore: Clean up .gitignore by removing obsolete entries

- Deleted unused entries related to the docs-chat vector database from .gitignore to maintain a cleaner configuration.

* chore: Remove deprecated chat configuration and markdown preview script

- Deleted the `create-markdown-preview.js` script and the `docs-chat-config.js` file to eliminate unused assets and streamline the project.
- Updated the `docs-chat-widget.js` to directly reference the markdown library from a CDN, enhancing maintainability.

* chore: Update markdown rendering in chat widget to use marked library

- Replaced the deprecated `create-markdown-preview` library with the `marked` library for markdown rendering.
- Adjusted the script loading mechanism to fetch `marked` from a CDN, improving performance and maintainability.
- Enhanced the markdown rendering function to ensure security by disabling HTML pass-through and opening links in new tabs.

* Delete docs/start/index.md

* fix: harden voice-call webhook verification

* fix(cron): fix timeout, add timestamp validation, enable file sync

Fixes #7667

Task 1: Fix cron operation timeouts
- Increase default gateway tool timeout from 10s to 30s
- Increase cron-specific tool timeout to 60s
- Increase CLI default timeout from 10s to 30s
- Prevents timeouts when gateway is busy with long-running jobs

Task 2: Add timestamp validation
- New validateScheduleTimestamp() function in validate-timestamp.ts
- Rejects atMs timestamps more than 1 minute in the past
- Rejects atMs timestamps more than 10 years in the future
- Applied to both cron.add and cron.update operations
- Provides helpful error messages with current time and offset

Task 3: Enable file sync for manual edits
- Track file modification time (storeFileMtimeMs) in CronServiceState
- Check file mtime in ensureLoaded() and reload if changed
- Recompute next runs after reload to maintain accuracy
- Update mtime after persist() to prevent reload loop
- Dashboard now picks up manual edits to ~/.openclaw/cron/jobs.json

* feat(cron): introduce delivery modes for isolated jobs

- Added support for new delivery modes in cron jobs: `announce`, `deliver`, and `none`.
- Updated documentation to reflect changes in delivery options and usage examples.
- Enhanced the cron job schema to include delivery configuration.
- Refactored related CLI commands and UI components to accommodate the new delivery settings.
- Improved handling of legacy delivery fields for backward compatibility.

This update allows users to choose how output from isolated jobs is delivered, enhancing flexibility in job management.

* feat(cron): default isolated jobs to announce delivery and enhance scheduling options

- Updated isolated cron jobs to default to `announce` delivery mode, improving user experience.
- Enhanced scheduling options to accept ISO 8601 timestamps for `schedule.at`, while still supporting epoch milliseconds.
- Refined documentation to clarify delivery modes and scheduling formats.
- Adjusted related CLI commands and UI components to reflect these changes, ensuring consistency across the platform.
- Improved handling of legacy delivery fields for backward compatibility.

This update streamlines the configuration of isolated jobs, making it easier for users to manage job outputs and schedules.

* feat(cron): enhance one-shot job behavior and CLI options

- Default one-shot jobs to delete after success, improving job management.
- Introduced `--keep-after-run` CLI option to allow users to retain one-shot jobs post-execution.
- Updated documentation to clarify default behaviors and new options for one-shot jobs.
- Adjusted cron job creation logic to ensure consistent handling of delete options.
- Enhanced tests to validate new behaviors and ensure reliability.

This update streamlines the handling of one-shot jobs, providing users with more control over job persistence and execution outcomes.

* feat(cron): enhance delivery modes and job configuration

- Updated isolated cron jobs to support new delivery modes: `announce` and `none`, improving output management.
- Refactored job configuration to remove legacy fields and streamline delivery settings.
- Enhanced the `CronJobEditor` UI to reflect changes in delivery options, including a new segmented control for delivery mode selection.
- Updated documentation to clarify the new delivery configurations and their implications for job execution.
- Improved tests to validate the new delivery behavior and ensure backward compatibility with legacy settings.

This update provides users with greater flexibility in managing how isolated jobs deliver their outputs, enhancing overall usability and clarity in job configurations.

* feat(cron): set default enabled state for cron jobs

- Added logic to default the `enabled` property to `true` if not explicitly set as a boolean in the cron job input.
- Updated job creation and store functions to ensure consistent handling of the `enabled` state across the application.
- Enhanced input normalization to improve job configuration reliability.

This update ensures that cron jobs are enabled by default, enhancing user experience and reducing potential misconfigurations.

* refactor(cron): update delivery instructions for isolated agent

- Revised the delivery instructions in the isolated agent's command body to clarify that summaries should be returned as plain text and will be delivered by the main agent.
- Removed the previous directive regarding messaging tools to streamline communication guidelines.

This change enhances clarity in the delivery process for isolated agent tasks.

* feat(cron): enhance delivery handling and testing for isolated jobs

- Introduced new properties for explicit message targeting and message tool disabling in the EmbeddedRunAttemptParams type.
- Updated cron job tests to validate best-effort delivery behavior and handling of delivery failures.
- Added logic to clear delivery settings when switching session targets in cron jobs.
- Improved the resolution of delivery failures and best-effort logic in the isolated agent's run function.

This update enhances the flexibility and reliability of delivery mechanisms in isolated cron jobs, ensuring better handling of message delivery scenarios.

* refactor(cron): improve delivery configuration handling in CronJobEditor and CLI

- Enhanced the delivery configuration logic in CronJobEditor to explicitly set the bestEffort property based on job settings.
- Refactored the CLI command to streamline delivery object creation, ensuring proper handling of optional fields like channel and to.
- Improved code readability and maintainability by restructuring delivery assignment logic.

This update clarifies the delivery configuration process, enhancing the reliability of job settings in both the editor and CLI.

* feat(cron): enhance legacy delivery handling in job patches

- Introduced logic to map legacy payload delivery updates onto the delivery object for `agentTurn` jobs, ensuring backward compatibility with legacy clients.
- Added tests to validate the correct application of legacy delivery settings in job patches, improving reliability in job configuration.
- Refactored delivery handling functions to streamline the merging of legacy delivery fields into the current job structure.

This update enhances the flexibility of delivery configurations, ensuring that legacy settings are properly handled in the context of new job patches.

* fix(cron): fix test failures and regenerate protocol files

- Add forceReload option to ensureLoaded to avoid stat I/O in normal
  paths while still detecting cross-service writes in the timer path
- Post isolated job summary back to main session (restores the old
  isolation.postToMainPrefix behavior via delivery model)
- Update legacy migration tests to check delivery.channel instead of
  payload.channel (normalization now moves delivery fields to top-level)
- Remove legacy deliver/channel/to/bestEffortDeliver from payload schema
- Update protocol conformance test for delivery modes
- Regenerate GatewayModels.swift (isolation -> delivery)

* UI: handle future timestamps in formatAgo

* Changelog: move cron entries to 2026.2.3

* fix: cron announce delivery path (#8540) (thanks @tyler6204)

* Telegram: use Grammy types directly, add typed Probe/Audit to plugin interface (#8403)

* Telegram: replace duplicated types with Grammy imports, add Probe/Audit generics to plugin interface

* Telegram: remove legacy forward metadata (deprecated in Bot API 7.0), simplify required-field checks

* Telegram: clean up remaining legacy references and unnecessary casts

* Telegram: keep RequestInit parameter type in proxy fetch (addresses review feedback)

* Telegram: add exhaustiveness guard to resolveForwardOrigin switch

* fix(telegram): include forward_from_chat metadata in forwarded message context (#8133)

Extract missing metadata from forwarded Telegram messages:

- Add fromChatType to TelegramForwardedContext, capturing the original
  chat type (channel/supergroup/group) from forward_from_chat.type
  and forward_origin.chat/sender_chat.type
- Add fromMessageId to capture the original message ID from channel forwards
- Read author_signature from forward_origin objects (modern API),
  preferring it over the deprecated forward_signature field
- Pass ForwardedFromChatType and ForwardedFromMessageId through to
  the inbound context payload
- Add test coverage for forward_origin channel/chat types, including
  author_signature extraction and fromChatType propagation

* fix: trim legacy signature fallback, type fromChatType as union

* fix: telegram forward metadata + cron delivery guard (#8392) (thanks @Glucksberg)

* fix(imessage): unify timeout configuration with configurable probeTimeoutMs

- Add probeTimeoutMs config option to channels.imessage
- Export DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS constant (10s) from probe.ts
- Propagate timeout config through all iMessage probe/RPC operations
- Fix hardcoded 2000ms timeouts that were too short for SSH connections

Closes: timeout issues when using SSH wrapper scripts (imsg-ssh)

* fix: address review comments

- Use optional timeoutMs parameter (undefined = use config/default)
- Extract DEFAULT_IMESSAGE_PROBE_TIMEOUT_MS to shared constants.ts
- Import constant in client.ts instead of hardcoding
- Re-export constant from probe.ts for backwards compatibility

* fix(imessage): detect self-chat echoes to prevent infinite loops (#8680)

* fix: align proxy fetch typing

* feat: add cloudflare ai gateway provider

* fix: force reload cron store

* Revert "feat: Add Docs Chat Widget with RAG-powered Q&A (#7908)" (#8834)

This reverts commit fa4b28d7af7464b07271bfef6c028e4135548f44.

* fix(web ui): agent model selection

* Docs: landing page revamp (#8885)

* Docs: refresh landing page

* Docs: add landing page companion pages

* Docs: drop legacy Jekyll assets

* Docs: remove legacy terminal css test

* Docs: restore terminal css assets

* Docs: remove terminal css assets

* fix(app-render): handle optional model in renderApp function

* chore: replace landpr prompt with end-to-end landing workflow (#8916)

* 🤖 docs: mirror landing revamp for zh-CN

What:
- add zh-CN versions of landing revamp pages (features, quickstart, docs directory, network model, credits)
- refresh zh-CN index and hubs, plus glossary entries

Why:
- keep Chinese docs aligned with the new English landing experience
- ensure navigation surfaces the new entry points

Tests:
- pnpm build && pnpm check && pnpm test

* 🤖 docs: note zh-CN landing revamp (#8994) (thanks @joshp123)

What:
- add changelog entry for the zh-CN landing revamp docs

Why:
- record the doc update and thank the contributor

Tests:
- pnpm lint && pnpm build && pnpm test

* feat: add shell completion installation prompt to CLI update command

* feat: add shell completion test script for installation verification

* completion: export cache utilities and require cached file for installation

- Export `resolveCompletionCachePath` and `completionCacheExists` for external use
- Update `installCompletion` to require cache existence (never use slow dynamic pattern)
- Add `usesSlowDynamicCompletion` to detect old `source <(...)` patterns
- Add `getShellProfilePath` helper for consistent profile path resolution
- Update `formatCompletionSourceLine` to always use cached file

* doctor: add shell completion check module

- Add `checkShellCompletionStatus` to get profile/cache/slow-pattern status
- Add `ensureCompletionCacheExists` for silent cache regeneration
- Add `doctorShellCompletion` to check and fix completion issues:
  - Auto-upgrade old slow dynamic patterns to cached version
  - Auto-regenerate cache if profile exists but cache is missing
  - Prompt to install if no completion is configured

* doctor: integrate shell completion check into doctor command

- Import and call `doctorShellCompletion` during doctor run
- Checks/fixes completion issues before gateway health check

* update: use shared completion helpers for shell completion setup

- Replace inline completion logic with `checkShellCompletionStatus` and `ensureCompletionCacheExists`
- Auto-upgrade old slow dynamic patterns silently during update
- Auto-regenerate cache if profile exists but cache is missing
- Prompt to install if no completion is configured

* onboard: use shared completion helpers for shell completion setup

- Replace inline completion logic with `checkShellCompletionStatus` and `ensureCompletionCacheExists`
- Auto-upgrade old slow dynamic patterns silently during onboarding
- Auto-regenerate cache if profile exists but cache is missing
- Prompt to install if no completion is configured

* scripts: update test-shell-completion to use shared helpers

- Use `checkShellCompletionStatus` and `ensureCompletionCacheExists` from doctor-completion
- Display "Uses slow pattern" status in output
- Simulate doctor/update/onboard behavior for all completion scenarios
- Remove duplicated utility functions

* changelog: add shell completion auto-fix entry

* feat: per-channel responsePrefix override (#9001)

* feat: per-channel responsePrefix override

Add responsePrefix field to all channel config types and Zod schemas,
enabling per-channel and per-account outbound response prefix overrides.

Resolution cascade (most specific wins):
  L1: channels.<ch>.accounts.<id>.responsePrefix
  L2: channels.<ch>.responsePrefix
  L3: (reserved for channels.defaults)
  L4: messages.responsePrefix (existing global)

Semantics:
  - undefined -> inherit from parent level
  - empty string -> explicitly no prefix (stops cascade)
  - "auto" -> derive [identity.name] from routed agent

Changes:
  - Core logic: resolveResponsePrefix() in identity.ts accepts
    optional channel/accountId and walks the cascade
  - resolveEffectiveMessagesConfig() passes channel context through
  - Types: responsePrefix added to WhatsApp, Telegram, Discord, Slack,
    Signal, iMessage, Google Chat, MS Teams, Feishu, BlueBubbles configs
  - Zod schemas: responsePrefix added for config validation
  - All channel handlers wired: telegram, discord, slack, signal,
    imessage, line, heartbeat runner, route-reply, native commands
  - 23 new tests covering backward compat, channel/account levels,
    full cascade, auto keyword, empty string stops, unknown fallthrough

Fully backward compatible - no existing config is affected.
Fixes #8857

* fix: address CI lint + review feedback

- Replace Record<string, any> with proper typed helpers (no-explicit-any)
- Add curly braces to single-line if returns (eslint curly)
- Fix JSDoc: 'Per-channel' → 'channel/account' on shared config types
- Extract getChannelConfig() helper for type-safe dynamic key access

* fix: finish responsePrefix overrides (#9001) (thanks @mudrii)

* fix: normalize prefix wiring and types (#9001) (thanks @mudrii)

---------

Co-authored-by: Gustavo Madeira Santana <[email protected]>

* Discord: allow disabling thread starter context

* feat(heartbeat): add accountId config option for multi-agent routing (#8702)

* feat(heartbeat): add accountId config option for multi-agent routing

Add optional accountId field to heartbeat configuration, allowing
multi-agent setups to explicitly specify which Telegram account
should be used for heartbeat delivery.

Previously, heartbeat delivery would use the accountId from the
session's deliveryContext. When a session had no prior conversation
history, heartbeats would default to the first/primary account
instead of the agent's intended bot.

Changes:
- Add accountId to HeartbeatSchema (zod-schema.agent-runtime.ts)
- Use heartbeat.accountId with fallback to session accountId (targets.ts)

Backward compatible: if accountId is not specified, behavior is unchanged.

Closes #8695

* fix: improve heartbeat accountId routing (#8702) (thanks @lsh411)

* fix: harden heartbeat accountId routing (#8702) (thanks @lsh411)

* fix: expose heartbeat accountId in status (#8702) (thanks @lsh411)

* chore: format status + heartbeat tests (#8702) (thanks @lsh411)

---------

Co-authored-by: m1 16 512 <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>

* TUI/Gateway: fix pi streaming + tool routing + model display + msg updating (#8432)

* TUI/Gateway: fix pi streaming + tool routing

* Tests: clarify verbose tool output expectation

* fix: avoid seq gaps for targeted tool events (#8432) (thanks @gumadeiras)

* Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)

* Telegram: remove @ts-nocheck from bot.ts and bot-message-dispatch.ts

- bot/types.ts: TelegramContext.me uses UserFromGetMe (Grammy) instead of manual inline type
- bot.ts: remove 6 unsafe casts (as any, as unknown, as object), use Grammy types directly
- bot.ts: remove dead message_thread_id access on reactions (not in Telegram Bot API)
- bot.ts: remove resolveThreadSessionKeys import (no longer needed for reactions)
- bot-message-dispatch.ts: replace ': any' with DispatchTelegramMessageParams type
- bot-message-dispatch.ts: add sticker.fileId guard before cache access
- bot.test.ts: update reaction tests, remove dead DM thread-reaction test

* Telegram: remove duplicate bot.catch handler (only the last one runs in Grammy)

* Telegram: remove @ts-nocheck from bot.ts, fix duplicate error handler, harden sticker caching (#9077)

* Security: Prevent gateway credential exfiltration via URL override (#9179)

* Gateway: require explicit auth for url overrides

* Gateway: scope credential blocking to non-local URLs only

Address review feedback: the previous fix blocked credential fallback for
ALL URL overrides, which was overly strict and could break workflows that
use --url to switch between loopback/tailnet without passing credentials.

Now credential fallback is only blocked for non-local URLs (public IPs,
external hostnames). Local addresses (127.0.0.1, localhost, private IPs
like 192.168.x.x, 10.x.x.x, tailnet 100.x.x.x) still get credential
fallback as before.

This maintains the security fix (preventing credential exfiltration to
attacker-controlled URLs) while preserving backward compatibility for
legitimate local URL overrides.

* Security: require explicit credentials for gateway url overrides (#8113) (thanks @victormier)

* Gateway: reuse explicit auth helper for url overrides (#8113) (thanks @victormier)

* Tests: format gateway chat test (#8113) (thanks @victormier)

* Tests: require explicit auth for gateway url overrides (#8113) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Tests: restore TUI gateway env

* Security: harden sandboxed media handling (#9182)

* Message: enforce sandbox for media param

* fix: harden sandboxed media handling (#8780) (thanks @victormier)

* chore: format message action runner (#8780) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Telegram: remove @ts-nocheck from bot-message.ts (#9180)

* Telegram: remove @ts-nocheck from bot-message.ts, type deps via Omit<BuildTelegramMessageContextParams>

* Telegram: widen allMedia to TelegramMediaRef[] so stickerMetadata flows through

* Telegram: remove @ts-nocheck from bot-message.ts (#9180)

* fix: cover anonymous voice allowlist callers (#8104) (thanks @victormier) (#9188)

* Security: owner-only tools + command auth hardening (#9202)

* Security: gate whatsapp_login by sender auth

* Security: treat undefined senderAuthorized as unauthorized (opt-in)

* fix: gate whatsapp_login to owner senders (#8768) (thanks @victormier)

* fix: add explicit owner allowlist for tools (#8768) (thanks @victormier)

* fix: normalize escaped newlines in send actions (#8768) (thanks @victormier)

---------

Co-authored-by: Victor Mier <[email protected]>

* Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)

* Telegram: remove @ts-nocheck from bot-handlers.ts, use Grammy types directly, deduplicate StickerMetadata

* Telegram: remove last @ts-nocheck from bot-handlers.ts (#9206)

* Message: clarify media schema + fix MEDIA newline

* fix: enforce owner allowlist for commands

* fix: infer --auth-choice from API key flags during non-interactive onboarding (#9241)

* fix: infer --auth-choice from API key flags during non-interactive onboarding

When --anthropic-api-key (or other provider key flags) is passed without
an explicit --auth-choice, the auth choice defaults to "skip", silently
discarding the API key. This means the gateway starts without credentials
and fails on every inbound message with "No API key found for provider".

Add inferAuthChoiceFromFlags() to derive the correct auth choice from
whichever provider API key flag was supplied, so credentials are persisted
to auth-profiles.json as expected.

Fixes #8481

* fix: infer auth choice from API key flags (#8484) (thanks @f-trycua)

* refactor: centralize auth choice inference flags (#8484) (thanks @f-trycua)

---------

Co-authored-by: f-trycua <[email protected]>

* chore: sync plugin versions to 2026.2.3

* fix(mac): resolve cron schedule formatters

* chore(mac): update appcast for 2026.2.3

* chore: update 2026.2.3 notes

* fix: gracefully downgrade xhigh thinking level in cron isolated agent (#9363)

When thinkingDefault is set to "xhigh" but the configured model does not
support it (e.g. Claude), the cron isolated-agent path throws a hard error
causing the job to fail. The interactive chat path already handles this by
silently downgrading to "high".

Apply the same graceful downgrade in the cron path: log a warning and
fall back to "high" instead of crashing.

Co-authored-by: hyf0-agent <[email protected]>

* fix: restore discord owner hint from allowlists

* fix: remove unused cron import

* fix(cli): resolve bundled chrome extension path

* test(cli): use unique temp dir for extension install

* fix(cli): support bundled extension path in dist root

* style(cli): satisfy lint rules in extension path resolver

* fix: resolve bundled chrome extension assets (#8914) (thanks @kelvinCB)

* Tests: add test coverage for security/windows-acl.ts

Adds comprehensive unit tests for Windows ACL inspection utilities:
- resolveWindowsUserPrincipal: username resolution with fallback
- parseIcaclsOutput: icacls output parsing
- summarizeWindowsAcl: ACL entry classification (trusted/world/group)
- inspectWindowsAcl: async ACL inspection with mocked exec
- formatWindowsAclSummary: summary string formatting
- formatIcaclsResetCommand: reset command string generation
- createIcaclsResetCommand: structured reset command generation

All 26 tests passing.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix: stabilize windows acl tests and command auth registry (#9335) (thanks @M00N7682)

* test: register discord plugin in allowlist test

* chore: bump version to 2026.2.4

* fix: resolve discord owner allowFrom matches

* fix(telegram): preserve DM topic threadId in deliveryContext

When receiving messages in Telegram DM topics (Topics in Private Chats),
the threadId was not saved in the session's deliveryContext, causing
replies to go to General chat instead of the topic.

Now we pass threadId to updateLastRoute for DM topics.

Fixes #8891

* test(telegram): add DM topic threadId deliveryContext test for #8891

Verifies that threadId is passed to updateLastRoute for DM topics.
Test fails on main branch, passes with the fix.

* fix: preserve telegram DM topic threadId (#9039) (thanks @lailoo)

* Update deps.

* chore: Typecheck test helper files.

* Docs: streamline start and install docs (#9648)

* docs(start): streamline getting started flow

* docs(nav): reorganize start and install sections

* docs(style): move custom css to style.css

* docs(navigation): align zh-CN ordering

* docs(navigation): localize zh-Hans labels

* docs(install): rename install overview page

* CLI: sort commands alphabetically in help output

Fixes #7964

Added sortSubcommands: true to configureHelp() to display
commands in alphabetical order when running 'openclaw --help'.

* fix: update changelog for help sorting (#8068) (thanks @deepsoumya617)

* docs(onboarding): add bootstrapping page (#9767)

* docs: fix onboarding rendering issues

* chore: reset appcast to 2026.2.3

* fix(telegram): pass parentPeer for forum topic binding inheritance (#9789)

Fixes #9545 and #9351.

When a message comes from a Telegram forum topic, the peer ID includes
the topic suffix (e.g., `-1001234567890:topic:99`). Users configure
bindings with the base group ID, which previously did not match.

This adds `parentPeer` to `resolveAgentRoute()` calls for forum groups,
enabling binding inheritance from the parent group to all topics.

- Extract `buildTelegramParentPeer()` helper in bot/helpers.ts
- Pass parentPeer in bot-message-context.ts, bot-handlers.ts,
  bot-native-commands.ts, and bot.ts (reaction handler)
- Add tests for forum topic routing and topic precedence

* docs(onboarding): streamline CLI onboarding docs (#9830)

* fix: auto-inject Telegram forum topic threadId in message tool

When using Telegram DM topics (forum topics), messages sent via the
message tool (media, buttons, etc.) land in General Topic instead of
the user's current topic. This happens because Slack has
resolveSlackAutoThreadId for auto-threading but Telegram had no
equivalent.

Add resolveTelegramAutoThreadId that mirrors the Slack pattern:
- When channel is telegram and no explicit threadId is provided
- Check if toolContext.currentThreadTs (the topic ID) is set
- Verify the target matches the originating chat
- Inject the threadId into params so the Telegram plugin action
  handler picks it up for sendMessage/sendMedia

The subagent announce path already correctly passes threadId via
requesterOrigin (set from agentThreadId in sessions-spawn-tool),
so no changes needed there.

* test: cover telegram topic threadId auto-injection and subagent origin threading

* fix: pass threadId/to/accountId from parent to subagent gateway call

When spawning a subagent, the requesterOrigin's threadId, to, and
accountId were not forwarded to the callGateway({method:'agent'}) params.
This meant the subagent's runContext had no currentThreadTs or
currentChannelId, so resolveTelegramAutoThreadId could not auto-inject
the forum topic thread ID when the subagent used the message tool.

Changes:
- sessions-spawn-tool: pass to, accountId, threadId from requesterOrigin
- run-context: populate currentChannelId from opts.to as fallback

Fixes subagent messages landing in General Topic instead of the correct
Telegram DM topic thread.

* fix: telegram topic auto-threading — use parseTelegramTarget, add tests (#7235) (thanks @Lukavyi)

* update handle

* docs: fix incorrect model.fallback to model.fallbacks in Ollama config (#9384) (#9749)

Both English and Chinese documentation had incorrect configuration template
using 'fallback' instead of 'fallbacks' in agents.defaults.model config.

Co-authored-by: damaozi <[email protected]>

* fix(cli): avoid NODE_OPTIONS for --disable-warning (#9691) (thanks @18-RAJAT)

Fixes npm pack failing on modern Node where --disable-warning is disallowed in NODE_OPTIONS.

* feat: add Claude Opus 4.6 to built-in model catalog (#9853)

* feat: add Claude Opus 4.6 to built-in model catalog

- Update default model from claude-opus-4-5 to claude-opus-4-6
- Add opus-4.6 model ID normalization
- Add claude-opus-4-6 to live model filter prefixes
- Update image tool to prefer claude-opus-4-6 for vision
- Add CLI backend alias for opus-4.6
- Update onboard auth default selections to include opus-4.6
- Update model picker placeholder

Closes #9811

* test: update tests for claude-opus-4-6 default

- Fix model-alias-defaults test to use claude-opus-4-6
- Fix image-tool test to expect claude-opus-4-6 in fallbacks

* feat: support claude-opus-4-6

* docs: update changelog for opus 4.6 (#9853) (thanks @TinyTb)

* chore: bump pi to 0.52.0

---------

Co-authored-by: Slurpy <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>

* 🤖 Feishu: expand channel support

What:
- add post parsing, doc link extraction, routing, replies, reactions, typing, and user lookup
- fix media download/send flows and make doc fetches domain-aware
- update Feishu docs and clawtributor credits

Why:
- raise Feishu parity with other channels and avoid dropped group messages
- keep replies threaded while supporting Lark domains
- document new configuration and credit the contributor

Tests:
- pnpm build
- pnpm check
- pnpm test (gateway suite timed out; reran pnpm vitest run --config vitest.gateway.config.ts)

Co-authored-by: 九灵云 <[email protected]>

* 🤖 Feishu: tighten mention gating

What:
- require the bot open_id match for group mention detection when available

Why:
- prevent replies when other users are mentioned and the bot id is known

Tests:
- pnpm test

* fix: remove orphaned tool_results during compaction pruning

When pruneHistoryForContextShare drops chunks of messages, it could drop
an assistant message with tool_use blocks while leaving corresponding
tool_result messages in the kept portion. These orphaned tool_results
cause Anthropic's API to reject the session with 'unexpected tool_use_id'.

Fix by calling repairToolUseResultPairing after each chunk drop to clean
up any orphaned tool_results. This reuses existing battle-tested code
from session-transcript-repair.ts.

Fixes #9769, #9724, #9672

* fix cron scheduling and reminder delivery regressions (#9733)

* fix(cron): prevent timer from allowing process exit (fixes #9694)

The cron timer was using .unref(), which caused the Node.js event
loop to exit or sleep if no other handles were active. This prevented
cron jobs from firing in some environments.

* fix(cron): infer delivery target for isolated jobs (fixes #9683)

When creating isolated agentTurn jobs (e.g. reminders) without explicit
delivery options, the job would default to 'announce' but fail to
resolve the target conversation. Now, we infer the channel and
recipient from the agent's current session key.

* fix(cron): enhance delivery inference for threaded sessions and null inputs (#9733)

Improves the delivery inference logic in the cron tool to correctly handle threaded session keys and cases where delivery is explicitly set to null. This ensures that the appropriate delivery mode and target are inferred based on the agent's session key, enhancing the reliability of job execution.

* fix: preserve telegram topic delivery inference (#9733) (thanks @tyler6204)

* fix: simplify cron delivery merge spread (#9733) (thanks @tyler6204)

* chore: add agent credentials to gitignore (#9874)

Protect sensitive files from accidental commit:
- memory/ (moltbook credentials, session data)
- .agent/*.json (agent config, moltbook.json)

Workflows in .agent/workflows/ remain tracked.

Co-authored-by: Claude Opus 4.5 <[email protected]>

* Docs: escape hash symbol in help channel names in issue template (#9695)

* feat(skills): add QR code skill (#8817)

feat(skills): add QR code generation and reading skill

Adds qr-code skill with:
- qr_generate.py - Generate QR codes with customizable size/error correction
- qr_read.py - Decode QR codes from images
- SKILL.md documentation

Co-authored-by: Omar-Khaleel

* chore(agentsmd): add tsgo command to AGENTS.md (#9894)

Add `pnpm tsgo` command to AGENTS.md development reference

Co-authored-by: vincentkoc <[email protected]>

* fix(runtime): bump minimum Node.js version to 22.12.0 (#5370)

* fix(runtime): bump minimum Node.js version to 22.12.0

Aligns the runtime guard with the declared package.json engines requirement.

The Matrix plugin (and potentially others) requires Node >= 22.12.0,
but the runtime guard previously allowed 22.0.0+. This caused confusing
errors like 'Cannot find module @vector-im/matrix-bot-sdk' when the real
issue was an unsupported Node version.

- Update MIN_NODE from 22.0.0 to 22.12.0
- Update error message to reflect the correct version
- Update tests to use 22.12.0 as the minimum valid version

Fixes #5292

* fix: update test versions to match MIN_NODE=22.12.0

---------

Co-authored-by: Markus Glucksberg <[email protected]>

* fix: clear stale token metrics on /new and /reset (#8929)

When starting a new session via /new or /reset, the token usage fields
(totalTokens, inputTokens, outputTokens, contextTokens) survived from the
previous session via the spread pattern in session init. This caused /status
to display misleading context usage from the old session.

Clear all four token metrics explicitly in the isNewSession block, alongside
the existing compactionCount reset. Also add diagnostic logging for session
forking via ParentSessionKey to help trace context inheritance.

* chore: apply local workspace updates (#9911)

* chore: apply local workspace updates

* fix: resolve prep findings after rebase (#9898) (thanks @gumadeiras)

* refactor: centralize model allowlist normalization (#9898) (thanks @gumadeiras)

* fix: guard model allowlist initialization (#9911)

* docs: update changelog scope for #9911

* docs: remove model names from changelog entry (#9911)

* fix: satisfy type-aware lint in model allowlist (#9911)

* fix: allow multiple compaction retries on context overflow (#8928)

Previously, overflowCompactionAttempted was a boolean flag set once, preventing
recovery when a single compaction wasn't enough. Change to a counter allowing up
to 3 attempts before giving up. Also add diagnostic logging on overflow events to
help debug early-overflow issues.

Fixes sessions that hit context overflow during long agentic turns with many tool
calls, where one compaction round isn't sufficient to bring context below limits.

* fix(errors): show clear billing error instead of cryptic API response (#8391)

* fix(errors): return clear billing error message instead of cryptic raw error (#8136)

When an LLM API provider returns a credit/billing-related error (HTTP 402,
insufficient credits, low balance, etc.), OpenClaw now shows a clear,
actionable message instead of passing through the raw/cryptic error text:

  ⚠️ API provider returned a billing error — your API key has run out of
  credits or has an insufficient balance. Check your provider's billing
  dashboard and top up or switch to a different API key.

Changes:
- formatAssistantErrorText: detect billing errors via isBillingErrorMessage()
  and return a user-friendly message (placed before the generic HTTP/JSON
  error fallthrough)
- sanitizeUserFacingText: same billing detection for the sanitization path
- pi-embedded-runner/run.ts: add billingFailure detection in the profile
  exhaustion fallback, so the FailoverError message is billing-specific
- Added 3 new tests for credit balance, HTTP 402, and insufficient credits

* fix: extract billing error message to shared constant

* Revert "feat(skills): add QR code skill (#8817)"

This reverts commit ad13c265ba1fd22dadfe30325ed998d9a3d95e5c.

* docs: improve DM security guidance with concrete example

Add a more prominent security warning for multi-user DM setups:
- Add blockquote security warning about context leakage
- Include concrete example showing the privacy risk
- Add "When to enable this" checklist
- Clarify that default is fine for single-user setups

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* docs: tighten secure DM example

* docs: note secure DM guidance update (#9377) (thanks @Shrinija17)

* Agents: bump pi-mono to 0.52.5 (#9949)

* Agents: bump pi-mono to 0.52.5

* Changelog: add PR reference for pi bump

* docs: restructure Get Started tab and improve onboarding flow (#9950)

* docs: restructure Get Started tab and improve onboarding flow

- Flatten nested Onboarding group into linear First Steps flow
- Add 'What is OpenClaw?' narrative section to landing page
- Split wizard.md into streamlined overview + full reference (reference/wizard.md)
- Move Pairing to Channels > Configuration
- Move Bootstrapping to Agents > Fundamentals
- Move macOS app onboarding to Platforms > macOS companion app
- Move Lore to Help > Community
- Remove duplicate install instructions from openclaw.md
- Mirror navigation changes in zh-CN tabs
- No content deleted — all detail preserved or relocated

* docs: move deployment pages to install/, fix Platforms tab routing, clarify onboarding paths

- Move deployment guides (fly, hetzner, gcp, macos-vm, exe-dev, railway, render,
  northflank) from platforms/ and root to install/
- Add 'Hosting and deployment' group to Install tab
- Slim Gateway & Ops 'Remote access and deployment' down to 'Remote access'
- Swap Platforms tab before Gateway & Ops to fix path-prefix routing
- Move macOS app onboarding into First steps (parallel to CLI wizard)
- Rename sidebar titles to 'Onboarding: CLI' / 'Onboarding: macOS App'
- Add redirects for all moved paths
- Update all internal links (en + zh-CN)
- Fix img tag syntax in onboarding.md

* fix(telegram): accept messages from group members in allowlisted groups (#9775)

* fix(telegram): accept messages from group members in allowlisted groups

Issue #4559: Telegram bot was silently dropping messages from non-paired users
in allowlisted group chats due to overly strict sender filtering.

The fix adds a check to distinguish between:
1. Group itself is allowlisted → accept messages from any member
2. Group is NOT allowlisted → only accept from allowlisted senders

Changes:
- Check if group ID is in the allowlist (or allowlist is wildcard)
- Only reject sender if they're not in allowlist AND group is not allowlisted
- Improved logging to indicate the actual reason for rejection

This preserves security controls while fixing the UX issue where group members
couldn't participate unless individually allowlisted.

Backwards compatible: existing allowlists continue to work as before.

* style: format telegram fix for oxfmt compliance

* refactor(telegram): clarify group allowlist semantics in fix for #4559

Changes:
- Rename 'isGroupInAllowlist' to 'isGroupChatIdInAllowlist' for clarity
- Expand comments to explain the semantic distinction:
  * Group chat ID in allowlist -> accept any group member (fixes #4559)
  * Group chat ID NOT in allowlist -> enforce sender allowlist (preserves security)
- This addresses concerns about config semantics raised in code review

The fix maintains backward compatibility:
- 'groupAllowFrom' with group chat IDs now correctly acts as group enablement
- 'groupAllowFrom' with sender IDs continues to work as sender allowlist
- Operators should use group chat IDs for group enablement, sender IDs for sender control

Note: If operators were using 'groupAllowFrom' with group IDs expecting sender-level
filtering, they should migrate to a separate sender allowlist config. This is the
intended behavior per issue #4559.

* Telegram: allow per-group groupPolicy overrides

* Telegram: support per-group groupPolicy overrides (#9775) (thanks @nicolasstanley)

---------

Co-authored-by: George Pickett <[email protected]>

* chore: remove tracked .DS_Store files

* Fix: Enable scrolling on the dashboard config page (#1822)

* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard

* feat: add xAI Grok provider support

* fix(onboard): align xAI default model to grok-4

* chore: changelog for xAI onboarding (#9885) (thanks @grp06)

* fix(cron): prevent recomputeNextRuns from skipping due jobs in onTimer (#9823)

* fix(cron): prevent recomputeNextRuns from skipping due jobs in onTimer

ensureLoaded(forceReload) called recomputeNextRuns before runDueJobs,
which recalculated nextRunAtMs to a strictly future time. Since
setTimeout always fires a few ms late, the due check (now >= nextRunAtMs)
always failed and every/cron jobs never executed. Fixes #9788.

* docs: add changelog entry for cron timer race fix (#9823) (thanks @pycckuu)

---------

Co-authored-by: Tyler Yust <[email protected]>

* fix(cron): re-arm timer in finally to survive transient errors (#9948)

* fix(cron): handle legacy atMs field in schedule when computing next run (#9932)

* fix(cron): handle legacy atMs field in schedule when computing next run

The cron scheduler only checked for `schedule.at` (string) but legacy jobs
may have `schedule.atMs` (number) from before the schema migration.

This caused nextRunAtMs to stay null because:
1. Store migration runs on load but may not persist immediately
2. Race conditions or file mtime issues can skip migration
3. computeJobNextRunAtMs/computeNextRunAtMs only checked `at`, not `atMs`

Fix: Make both functions defensive by checking `atMs` first (number),
then `atMs` (string, for edge cases), then falling back to `at` (string).

This ensures jobs fire correctly even if:
- Migration hasn't run yet
- Old data was written by a previous version
- The store was manually edited

Fixes #9930

* fix: validate numeric atMs to prevent NaN/Infinity propagation

Addresses review feedback - numeric atMs values are now validated with
Number.isFinite() && atMs > 0 before use. This prevents corrupted or
manually edited stores from causing hot timer loops via setTimeout(..., NaN).

* fix(exec-approvals): coerce bare string allowlist entries to objects (#9790)

* fix(exec-approvals): coerce bare string allowlist entries (#9903) (thanks @mcaxtr)

* security: add skill/plugin code safety scanner (#9806)

* security: add skill/plugin code safety scanner module

* security: integrate skill scanner into security audit

* security: add pre-install code safety scan for plugins

* style: fix curly brace lint errors in skill-scanner.ts

* docs: add changelog entry for skill code safety scanner

* style: append ellipsis to truncated evidence strings

* fix(security): harden plugin code safety scanning

* fix: scan skills on install and report code-safety details

* fix: dedupe audit-extra import

* fix(security): make code safety scan failures observable

* fix(test): stabilize smoke + gateway timeouts (#9806) (thanks @abdelsfane)

---------

Co-authored-by: Darshil <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: George Pickett <[email protected]>

* Thinking: accept extra-high alias and sync Codex FAQ wording

* Changelog: note #9976 thinking alias + Codex 5.3 docs sync

* fix: normalize xhigh aliases and docs sync (#9976)

* fix(agents): skip tool extraction for aborted/errored assistant messages (#4598)

Fixes tool call/tool_result pairing issues that cause permanent session corruption when assistant messages have stopReason "error" or "aborted". Includes 4 unit tests.

* fix(cron): handle undefined sessionTarget in list output (#9649) (#9752)

* fix(cron): handle undefined sessionTarget in list output (#9649)

When sessionTarget is undefined, pad() would crash with 'Cannot read
properties of undefined (reading trim)'. Use '-' as fallback value.

* test(cron): add regression test for undefined sessionTarget (#9649)

Verifies that printCronList handles jobs with undefined sessionTarget
without crashing. Test fails on main branch, passes with the fix.

* fix: use correct CronSchedule format in tests (#9752) (thanks @lailoo)

Tests were using { kind: 'at', atMs: number } but the CronSchedule type
requires { kind: 'at', at: string } where 'at' is an ISO date string.

---------

Co-authored-by: damaozi <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>

* chore: Update deps.

* Model: add strict gpt-5.3-codex fallback for OpenAI Codex (fixes #9989) (#9995)

* Model: allow forward-compatible OpenAI Codex GPT-5 IDs

* Model: scope Codex fallback to gpt-5.3-codex

* fix: reorder codex fallback before providerCfg, add ordering test, changelog (#9989) (thanks @w1kke)

---------

Co-authored-by: Robin <[email protected]>

* fix(nextcloud-talk): sign message text instead of JSON body (#2092)

Nextcloud Talk's ChecksumVerificationService verifies HMAC against the
extracted message/reaction text, not the full JSON body. This fixes 401
authentication errors when sending messages via the bot API.

- sendMessageNextcloudTalk: sign 'message' text only
- sendReactionNextcloudTalk: sign 'reaction' string only

* fix(slack): add mention stripPatterns for /new and /reset commands (#9971)

* fix(slack): add mention stripPatterns for /new and /reset commands

Fixes #9937

The Slack dock was missing mentions.stripPatterns that Discord has.
This caused /new and /reset to fail when sent with a mention
(e.g. @bot /reset) because <@USERID> wasn't stripped before matching.

* fix(slack): strip mentions for /new and /reset (#9971) (thanks @ironbyte-rgb)

---------

Co-authored-by: ironbyte-rgb <[email protected]>
Co-authored-by: George Pickett <[email protected]>

* feat(feishu): replace built-in SDK with community plugin

Replace the built-in Feishu SDK with the community-maintained
clawdbot-feishu plugin by @m1heng.

Changes:
- Remove src/feishu/ directory (19 files)
- Remove src/channels/plugins/outbound/feishu.ts
- Remove src/channels/plugins/normalize/feishu.ts
- Remove src/config/types.feishu.ts
- Remove feishu exports from plugin-sdk/index.ts
- Remove FeishuConfig from types.channels.ts

New features in community plugin:
- Document tools (read/create/edit Feishu docs)
- Wiki tools (navigate/manage knowledge base)
- Drive tools (folder/file management)
- Bitable tools (read/write table records)
- Permission tools (collaborator management)
- Emoji reactions support
- Typing indicators
- Rich media support (bidirectional image/file transfer)
- @mention handling
- Skills for feishu-doc, feishu-wiki, feishu-drive, feishu-perm

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(feishu): add targeted eslint-disable comments for SDK integration

Add line-specific eslint-disable-next-line comments for SDK type casts
and union type issues, rather than file-level disables.

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* fix(feishu): fix webhook mode silent exit and receive_id_type default

- monitor.ts: throw error for webhook mode instead of silently returning,
  so gateway properly marks channel as failed
- targets.ts: default receive_id_type to "user_id" instead of "open_id"
  for non-prefixed IDs, fixing message delivery for enterprise user IDs

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* chore: update pnpm-lock.yaml for feishu extension deps

Add lockfile entries for:
- @larksuiteoapi/node-sdk@^1.56.1
- @sinclair/[email protected]
- zod@^4.3.6

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* feat(feishu): sync with clawdbot-feishu #137 (multi-account support)

- Sync latest changes from clawdbot-feishu including multi-account support
- Add eslint-disable comments for SDK-related any types
- Remove unused imports
- Fix no-floating-promises in monitor.ts

Co-Authored-By: Claude Opus 4.5 <[email protected]>

* security: redact credentials from config.get gateway responses (#9858)

* security: add skill/plugin code safety scanner module

* security: integrate skill scanner into security audit

* security: add pre-install code safety scan for plugins

* style: fix curly brace lint errors in skill-scanner.ts

* docs: add changelog entry for skill code safety scanner

* security: redact credentials from config.get gateway responses

The config.get gateway method returned the full config snapshot
including channel credentials (Discord tokens, Slack botToken/appToken,
Telegram botToken, Feishu appSecret, etc.), model provider API keys,
and gateway auth tokens in plaintext.

Any WebSocket client—including the unauthenticated Control UI when
dangerouslyDisableDeviceAuth is set—could read every secret.

This adds redactConfigSnapshot() which:
- Deep-walks the config object and masks any field whose key matches
  token, password, secret, or apiKey patterns
- Uses the existing redactSensitiveText() to scrub the raw JSON5 source
- Preserves the hash for change detection
- Includes 15 test cases covering all channel types

* security: make gateway config writes return redacted values

* test: disable control UI by default in gateway server tests

* fix: redact credentials in gateway config APIs (#9858) (thanks @abdelsfane)

---------

Co-authored-by: George Pickett <[email protected]>

* fix: release session locks on process termination (#1962)

Adds cleanup handlers to release held file locks when the process
terminates via SIGTERM, SIGINT, or normal exit. This prevents orphaned
lock files that would block future sessions.

Fixes #1951

* fix(ollama): add streaming config and fix OLLAMA_API_KEY env var support (#9870)

* fix(ollama): add streaming config and fix OLLAMA_API_KEY env var support

Adds configurable streaming parameter to model configuration and sets streaming
to false by default for Ollama models. This addresses the corrupted response
issue caused by upstream SDK bug badlogic/pi-mono#1205 where interleaved
content/reasoning deltas in streaming responses cause garbled output.

Changes:
- Add streaming param to AgentModelEntryConfig type
- Set streaming: false default for Ollama models
- Add OLLAMA_API_KEY to envMap (was missing, preventing env var auth)
- Document streaming configuration in Ollama provider docs
- Add tests for Ollama model configuration

Users can now configure streaming per-model and Ollama authentication
via OLLAMA_API_KEY environment variable works correctly.

Fixes #8839
Related: badlogic/pi-mono#1205

* docs(ollama): use gpt-oss:20b as primary example

Updates documentation to use gpt-oss:20b as the primary example model
since it supports tool calling. The model examples now show:

- gpt-oss:20b as the primary recommended model (tool-capable)
- llama3.3 and qwen2.5-coder:32b as additional options

This provides users with a clear, working example that supports
OpenClaw's tool calling features.

* chore: remove unused vi import from ollama test

* fix: untrack dist/control-ui build artifacts (#1856)

The dist/control-ui/ files were committed before the dist/ gitignore
rule was effective. These build artifacts get regenerated during
builds, causing dirty repo errors that block the auto-update mechanism.

Removes the files from git tracking while keeping them locally and
respecting the existing dist/ gitignore entry.

Fixes #1838

Co-authored-by: Claude <[email protected]>

* chore: add weekly upstream sync workflow

Merges openclaw/openclaw main on a weekly schedule using -X ours
so our fork patches always take priority. Opens a PR for visibility.

Co-Authored-By: Claude <[email protected]>

* feat(gateway): route OpenAI endpoint through dispatchInboundMessage for command support

* fix(gateway): remove unused import and add defensive stream close

* fix(gateway): add abort on disconnect, finish_reason stop, and changelog

* fix(gateway): simplify tautological ternary and split command SSE terminal chunk

---------

Co-authored-by: Yeom-JinHo <[email protected]>
Co-authored-by: Peter Steinberger <[email protected]>
Co-authored-by: Lucas Kim <[email protected]>
Co-authored-by: Val Alexander <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>
Co-authored-by: Christian Klotz <[email protected]>
Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Ayaan Zaidi <[email protected]>
Co-authored-by: Yudong Han <[email protected]>
Co-authored-by: Iranb <[email protected]>
Co-authored-by: Seb Slight <[email protected]>
Co-authored-by: Tak Hoffman <[email protected]>
Co-authored-by: Shakker <[email protected]>
Co-authored-by: Josh Palmer <[email protected]>
Co-authored-by: mudrii <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: lsh411 <[email protected]>
Co-authored-by: m1 16 512 <[email protected]>
Co-authored-by: Gustavo Madeira Santana <[email protected]>
Co-authored-by: Victor Mier <[email protected]>
Co-authored-by: f-trycua <[email protected]>
Co-authored-by: hyf0-agent <[email protected]>
Co-authored-by: hyf0-agent <[email protected]>
Co-authored-by: Kelvin Calcano <[email protected]>
Co-authored-by: M00N7682 <[email protected]>
Co-authored-by: Claude Opus 4.5 <[email protected]>
Co-authored-by: damaozi <[email protected]>
Co-authored-by: cpojer <[email protected]>
Co-authored-by: sebslight <[email protected]>
Co-authored-by: Soumyadeep Ghosh <[email protected]>
Co-authored-by: Clawdbot <[email protected]>
Co-authored-by: 大猫子 <[email protected]>
Co-authored-by: Rajat Joshi <[email protected]>
Co-authored-by: Michael Lee <[email protected]>
Co-authored-by: Slurpy <[email protected]>
Co-authored-by: 九灵云 <[email protected]>
Co-authored-by: Tyler Yust <[email protected]>
Co-authored-by: Caelum <[email protected]>
Co-authored-by: MattQ <[email protected]>
Co-authored-by: Omar Khaleel <[email protected]>
Co-authored-by: Vincent Koc <[email protected]>
Co-authored-by: vincentkoc <[email protected]>
Co-authored-by: Glucksberg <[email protected]>
Co-authored-by: Markus Glucksberg <[email protected]>
Co-authored-by: Shrinija Kummari <[email protected]>
Co-authored-by: George Pickett <[email protected]>
Co-authored-by: nicolasstanley <[email protected]>
Co-authored-by: Daijiro Miyazawa <[email protected]>
Co-authored-by: Igor Markelov <[email protected]>
Co-authored-by: Maksym Brashchenko <[email protected]>
Co-authored-by: fujiwara-tofu-shop <[email protected]>
Co-authored-by: Marcus Castro <[email protected]>
Co-authored-by: Abdel Sy Fane <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: Darshil <[email protected]>
Co-authored-by: slonce70 <[email protected]>
Co-authored-by: Aisling Cahill <[email protected]>
Co-authored-by: Robin <[email protected]>
Co-authored-by: wangai-studio <[email protected]>
Co-authored-by: ironbyte-rgb <[email protected]>
Co-authored-by: ironbyte-rgb <amontaboi…
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 19, 2026
Glucksberg added a commit to Glucksberg/OpenClaw that referenced this pull request Feb 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Return info when API doesnt have credits

2 participants

Comments