Compare commits

..

1169 Commits

Author SHA1 Message Date
Shadow
43b799391a fix: add Discord channel-edit thread params (#5542) (thanks @stumct) 2026-02-12 16:45:39 -06:00
Shadow
61d57be4c2 Discord: preserve media caption whitespace 2026-02-12 16:40:08 -06:00
Vignesh Natarajan
01e4e15364 fix: normalize Signal mentions (#2013) (thanks @alexgleason) 2026-02-12 14:37:55 -08:00
Vignesh Natarajan
d3e43de42b Signal: satisfy lint 2026-02-12 14:37:55 -08:00
Vignesh Natarajan
cfec19df53 Signal: normalize mention placeholders 2026-02-12 14:37:55 -08:00
Alex Gleason
051c574047 fix(signal): replace  with @uuid/@phone from mentions
Related #1926

Signal mentions were appearing as  (object replacement character)
instead of readable identifiers. This caused Clawdbot to misinterpret
messages and respond inappropriately.

Now parses dataMessage.mentions array and replaces the placeholder
character with @{uuid} or @{phone} from the mention metadata.
2026-02-12 14:37:55 -08:00
Web Vijayi
4d0443391c fix: use iterator.done check for LRU eviction
Fixes edge case where empty string key would stop eviction early
2026-02-12 16:31:36 -06:00
Web Vijayi
5882cf2f5d fix(discord): add TTL and LRU eviction to thread starter cache
Fixes #5260

The DISCORD_THREAD_STARTER_CACHE Map was growing unbounded during
long-running gateway sessions, causing memory exhaustion.

This fix adds:
- 5-minute TTL expiry (thread starters rarely change)
- Max 500 entries with LRU eviction
- Same caching pattern used by Slack's thread resolver

The implementation mirrors src/slack/monitor/thread-resolution.ts
which already handles this correctly.
2026-02-12 16:31:36 -06:00
Shadow
149db5b2c2 Discord: handle thread edit params 2026-02-12 16:31:06 -06:00
Kyle Tse
abdceedaf6 fix: respect session model override in agent runtime (#14783) (#14983)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: ec47d1a7bf
Co-authored-by: shtse8 <8020099+shtse8@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 17:12:15 -05:00
Gustavo Madeira Santana
c0c34c72bb chore: fix windows CI tests 2026-02-12 16:59:55 -05:00
Gustavo Madeira Santana
a158c46828 Tests: make download temp-path assertion cross-platform 2026-02-12 16:58:59 -05:00
Peter Steinberger
b50640c600 fix(irc): type socket error param 2026-02-12 22:58:42 +01:00
Peter Steinberger
722c010b95 chore(deps): update dependencies 2026-02-12 22:58:42 +01:00
Skyler Miao
cb0350230c feat(minimax): update models from M2.1 to M2.5 (#14865)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 1d58bc5760
Co-authored-by: adao-max <153898832+adao-max@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 16:48:46 -05:00
Gustavo Madeira Santana
b02c88d3e7 Browser/Logging: share default openclaw tmp dir resolver 2026-02-12 16:44:04 -05:00
Shadow
4aa035f38f CI: gate auto-response with trigger label 2026-02-12 15:41:16 -06:00
Shadow
978effcf26 CI: close PRs with excessive labels 2026-02-12 15:35:32 -06:00
Shadow
3b6bd202da Scripts: add issue labeler state + PR support 2026-02-12 15:28:12 -06:00
Gustavo Madeira Santana
afbce73570 fix: use os.tmpdir fallback paths for temp files (#14985)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 347c689407
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 16:08:41 -05:00
Shadow
282fb9ad52 CI: handle search 422 in labeler 2026-02-12 14:58:25 -06:00
Shadow
47cd7e29ef CI: add labeler backfill dispatch 2026-02-12 14:43:14 -06:00
Joseph Krug
5147656d65 fix: prevent heartbeat scheduler death when runOnce throws (#14901)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 022efbfef9
Co-authored-by: joeykrug <5925937+joeykrug@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 15:38:46 -05:00
Shadow
1f41f7b1e6 CI: add contributor tier labels 2026-02-12 14:33:30 -06:00
0xRain
d8d8109711 fix(agents): guard against undefined path in context file entries (#14903)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 25856b863d
Co-authored-by: 0xRaini <190923101+0xRaini@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 15:27:56 -05:00
Gustavo Madeira Santana
571a237d5a chore: move local imports to the top 2026-02-12 15:14:29 -05:00
Gustavo Madeira Santana
49188caf94 chore(pr-skills): suppress output for successful commands (pnpm install/build/test/etc) to lower context usage 2026-02-12 15:10:23 -05:00
Gustavo Madeira Santana
1123357c62 chore: refining review PR additional prompts 2026-02-12 14:55:07 -05:00
Gustavo Madeira Santana
a005881fc9 docs(changelog): add Control UI symlink install fix entry
Co-authored-by: aynorica <54416476+aynorica@users.noreply.github.com>
2026-02-12 14:48:25 -05:00
Gustavo Madeira Santana
8d5094e1f4 fix: resolve symlinked argv1 for Control UI asset detection (#14919)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 07b85041dc
Co-authored-by: gumadeiras <116837+gumadeiras@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 14:45:31 -05:00
fagemx
bdd0c12329 fix(providers): include provider name in billing error messages (#14697)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 774e0b6605
Co-authored-by: fagemx <117356295+fagemx@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
2026-02-12 18:23:27 +00:00
Peter Steinberger
5e7842a41d feat(zai): auto-detect endpoint + default glm-5 (#14786)
* feat(zai): auto-detect endpoint + default glm-5

* test: fix Z.AI default endpoint expectation (#14786)

* test: bump embedded runner beforeAll timeout

* chore: update changelog for Z.AI GLM-5 autodetect (#14786)

* chore: resolve changelog merge conflict with main (#14786)

* chore: append changelog note for #14786 without merge conflict

* chore: sync changelog with main to resolve merge conflict
2026-02-12 19:16:04 +01:00
Peter Steinberger
2b5df1dfea fix: local-time timestamps include offset (#14771) (thanks @0xRaini) 2026-02-12 19:09:20 +01:00
Elonito
468414cac4 fix: use local timezone in console log timestamps
formatConsoleTimestamp previously used Date.toISOString() which always
returns UTC time (suffixed with Z). This confused users whose local
timezone differs from UTC.

Now uses local time methods (getHours, getMinutes, etc.) and appends the
local UTC offset (e.g. +08:00) instead of Z. The pretty style returns
local HH:MM:SS. The hasTimestampPrefix regex is updated to accept both
Z and +/-HH:MM offset suffixes.

Closes #14699
2026-02-12 19:08:52 +01:00
0xRain
af172742a3 fix(feishu): use msg_type 'media' for video/audio messages (#14648)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: e8044cb208
Co-authored-by: 0xRaini <190923101+0xRaini@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-12 19:05:09 +01:00
Peter Steinberger
069670388e perf(test): speed up test runs and harden temp cleanup 2026-02-12 17:59:52 +00:00
Yi Liu
d3aee84499 fix(security): add --ignore-scripts to skills install commands (#14659)
Skills install runs package manager install commands (npm, pnpm, yarn,
bun) without --ignore-scripts, allowing malicious npm packages to
execute arbitrary code via postinstall/preinstall lifecycle scripts
during global installation.

This is inconsistent with the security fix in commit 92702af7a which
added --ignore-scripts to both plugin installs (src/plugins/install.ts)
and hook installs (src/hooks/install.ts). Skills install was overlooked
in that change.

Global install (-g) is particularly dangerous as scripts execute with
the user's full permissions and can modify globally-accessible binaries.
2026-02-13 02:56:35 +09:00
Tyler
4c86010b06 fix: remove bundled soul-evil hook (closes #8776) (#14757)
* fix: remove bundled soul-evil hook (closes #8776)

* fix: remove soul-evil docs (#14757) (thanks @Imccccc)

---------

Co-authored-by: OpenClaw Bot <bot@openclaw.ai>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-12 18:52:09 +01:00
0xRain
971ac0886b fix(cli): guard against read-only process.noDeprecation on Node.js v23+ (#14152)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 11bb9f141a
Co-authored-by: 0xRaini <190923101+0xRaini@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-12 18:30:14 +01:00
Peter Steinberger
7695b4842b chore: bump version to 2026.2.12 2026-02-12 18:20:46 +01:00
Peter Steinberger
d25e96637c test(agents): make grok api key test hermetic 2026-02-12 17:17:02 +00:00
Peter Steinberger
b8a5f94f25 refactor(test): consolidate infra unit tests 2026-02-12 17:16:42 +00:00
Peter Steinberger
8fce7dc9b6 perf(test): add vitest slowest report artifact 2026-02-12 17:16:42 +00:00
Peter Steinberger
9f507112b5 perf(test): speed up vitest by skipping plugins + LLM slug 2026-02-12 17:15:43 +00:00
0xRain
626a1d0699 fix(gateway): increase WebSocket max payload to 5 MB for image uploads (#14486)
* fix(gateway): increase WebSocket max payload to 5 MB for image uploads

The 512 KB limit was too small for base64-encoded images — a 400 KB
image becomes ~532 KB after encoding, exceeding the limit and closing
the connection with code 1006.

Bump MAX_PAYLOAD_BYTES to 5 MB and MAX_BUFFERED_BYTES to 8 MB to
support standard image uploads via webchat.

Closes #14400

* fix: align gateway WS limits with 5MB image uploads (#14486) (thanks @0xRaini)

* docs: fix changelog conflict for #14486

---------

Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-12 17:48:49 +01:00
Jake
a2ddcdadeb fix: fix: transcribe audio before mention check in groups with requireMention (openclaw#9973) thanks @mcinteerj
Verified:
- pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: mcinteerj <3613653+mcinteerj@users.noreply.github.com>
2026-02-12 09:58:01 -06:00
danielwanwx
a5ab9fac0c fix(tts): strip markdown before sending text to TTS engines (#13237)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 163c68539f
Co-authored-by: danielwanwx <144515713+danielwanwx@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
2026-02-12 10:46:57 -05:00
Jake
4736fe7fde fix: fix(boot): use ephemeral session per boot to prevent stale context (openclaw#11764) thanks @mcinteerj
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: mcinteerj <3613653+mcinteerj@users.noreply.github.com>
2026-02-12 09:41:43 -06:00
Jake
6b1f485ce8 fix(telegram): add retry logic to health probe (openclaw#7405) thanks @mcinteerj
Verified:
- CI=true pnpm install --frozen-lockfile
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: mcinteerj <3613653+mcinteerj@users.noreply.github.com>
2026-02-12 09:11:35 -06:00
Tak Hoffman
5554fd23cc AGENTS.md: make PR_WORKFLOW optional (don’t override maintainer workflows) 2026-02-12 08:43:06 -06:00
Sebastian
d31caa81ef fix(runtime): guard cleanup and preserve skipped cron jobs 2026-02-12 09:28:47 -05:00
0xRain
4f329f923c fix(agents): narrow billing error 402 regex to avoid false positives on issue IDs (#13827)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: b0501bbab7
Co-authored-by: 0xRaini <190923101+0xRaini@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
2026-02-12 09:18:06 -05:00
Tak Hoffman
6a12d83450 changelog: add missing fix entries 2026-02-12 08:10:23 -06:00
Akari
455bc1ebba fix: use last API call's cache tokens for context-size display (#13698) (#13805)
The UsageAccumulator sums cacheRead/cacheWrite across all API calls
within a single turn. With Anthropic prompt caching, each call reports
cacheRead ≈ current_context_size, so after N tool-call round-trips the
accumulated total becomes N × actual_context, which gets clamped to
contextWindow (200k) by deriveSessionTotalTokens().

Fix: track the most recent API call's cache fields separately and use
them in toNormalizedUsage() for context-size reporting. This makes
/status Context display accurate while preserving accumulated output
token counts.

Fixes #13698
Fixes #13782

Co-authored-by: akari-musubi <259925157+akari-musubi@users.noreply.github.com>
2026-02-12 08:01:36 -06:00
Kyle Chen
4c350bc4c8 Fix: Prevent file descriptor leaks in child process cleanup (#13565)
* fix: prevent FD leaks in child process cleanup

- Destroy stdio streams (stdin/stdout/stderr) after process exit
- Remove event listeners to prevent memory leaks
- Clean up child process reference in moveToFinished()
- Also fixes model override handling in agent.ts

Fixes EBADF errors caused by accumulating file descriptors
from sub-agent spawns.

* Fix: allow stdin destroy in process registry cleanup

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 08:01:33 -06:00
jg-noncelogic
6f74786384 fix(antigravity): opus 4.6 forward-compat model + thinking signature sanitization bypass (#14218)
Two fixes for Google Antigravity (Cloud Code Assist) reliability:

1. Forward-compat model fallback: pi-ai's model registry doesn't include
   claude-opus-4-6-thinking. Add resolveAntigravityOpus46ForwardCompatModel()
   that clones the opus-4-5 template so the correct api ("google-gemini-cli")
   and baseUrl are preserved. Fixes #13765.

2. Fix thinking.signature rejection: The API returns Claude thinking blocks
   without signatures, then rejects them on replay. The existing sanitizer
   strips unsigned blocks, but the orphaned-user-message path in attempt.ts
   bypassed it by reading directly from disk. Now applies
   sanitizeAntigravityThinkingBlocks at that code path.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 08:01:28 -06:00
Taras Lukavyi
d85150357f feat: support .agents/skills/ directory for cross-agent skill discovery (#9966)
Adds loading from two .agents/skills/ locations:
- ~/.agents/skills/ (personal/user-level, source "agents-skills-personal")
- {workspace}/.agents/skills/ (project-level, source "agents-skills-project")

Precedence: extra < bundled < managed < personal .agents/skills < project .agents/skills < workspace.

Closes #8822
2026-02-12 07:56:19 -06:00
taw0002
dcb921944a fix: prevent double compaction caused by cache-ttl entry bypassing guard (#13514)
Move appendCacheTtlTimestamp() to after prompt + compaction retry
completes instead of before. The previous placement inserted a custom
entry (openclaw.cache-ttl) between compaction and the next prompt,
which broke pi-coding-agent's prepareCompaction() guard — the guard
only checks if the last entry is type 'compaction', and the cache-ttl
custom entry made it type 'custom', allowing an immediate second
compaction at very low token counts (e.g. 5,545 tokens) that nuked
all preserved context.

Fixes #9282
Relates to #12170
2026-02-12 07:55:32 -06:00
0xRain
21d7203fa9 fix(daemon): suppress EPIPE error in restartLaunchAgent stdout write (#14343)
After a successful launchctl kickstart, the stdout.write() for the
status message may fail with EPIPE if the receiving end has already
closed. Catch and ignore EPIPE specifically; re-throw other errors.

Closes #14234

Co-authored-by: Echo Ito <echoito@MacBook-Air.local>
2026-02-12 07:55:29 -06:00
brandonwise
7f6f7f598c fix: ignore meta field changes in config file watcher (#13460)
Prevents infinite restart loop when gateway updates meta.lastTouchedAt
and meta.lastTouchedVersion on startup.

Fixes #13458
2026-02-12 07:55:26 -06:00
Coy Geek
647d929c9d fix: Unauthenticated Nostr profile API allows remote config tampering (#13719)
* fix(an-07): apply security fix

Generated by staged fix workflow.

* fix(an-07): apply security fix

Generated by staged fix workflow.

* fix(an-07): satisfy lint in plugin auth regression test

Replace unsafe unknown-to-string coercion in the gateway plugin auth test helper with explicit string/null/JSON handling so pnpm check passes.
2026-02-12 07:55:22 -06:00
0xRain
acb9cbb898 fix(gateway): drain active turns before restart to prevent message loss (#13931)
* fix(gateway): drain active turns before restart to prevent message loss

On SIGUSR1 restart, the gateway now waits up to 30s for in-flight agent
turns to complete before tearing down the server. This prevents buffered
messages from being dropped when config.patch or update triggers a restart
while agents are mid-turn.

Changes:
- command-queue.ts: add getActiveTaskCount() and waitForActiveTasks()
  helpers to track and wait on active lane tasks
- run-loop.ts: on restart signal, drain active tasks before server.close()
  with a 30s timeout; extend force-exit timer accordingly
- command-queue.test.ts: update imports for new exports

Fixes #13883

* fix(queue): snapshot active tasks for restart drain

---------

Co-authored-by: Elonito <0xRaini@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 07:55:19 -06:00
niceysam
f7e05d0136 fix: exclude maxTokens from config redaction + honor deleteAfterRun on skipped cron jobs (#13342)
* fix: exclude maxTokens and token-count fields from config redaction

The /token/i regex in SENSITIVE_KEY_PATTERNS falsely matched fields like
maxTokens, maxOutputTokens, maxCompletionTokens etc. These are numeric
config fields for token counts, not sensitive credentials.

Added a whitelist (SENSITIVE_KEY_WHITELIST) that explicitly excludes
known token-count field names from redaction. This prevents config
corruption when maxTokens gets replaced with __OPENCLAW_REDACTED__
during config round-trips.

Fixes #13236

* fix: honor deleteAfterRun for one-shot 'at' jobs with 'skipped' status

Previously, deleteAfterRun only triggered when result.status was 'ok'.
For one-shot 'at' jobs, a 'skipped' status (e.g. empty heartbeat file)
would leave the job in state but disabled, never getting cleaned up.

Now deleteAfterRun also triggers on 'skipped' status for 'at' jobs,
since a skipped one-shot job has no meaningful retry path.

Fixes #13249

* Cron: format timer.ts

---------

Co-authored-by: nice03 <niceyslee@gmail.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 07:55:05 -06:00
mcwigglesmcgee
f8cad44cd6 fix(voice-call): pass Twilio stream auth token via <Parameter> instead of query string (#14029)
Twilio strips query parameters from WebSocket URLs in <Stream> TwiML,
so the auth token set via ?token=xxx never arrives on the WebSocket
connection. This causes stream rejection when token validation is enabled.

Fix: pass the token as a <Parameter> element inside <Stream>, which
Twilio delivers in the start message's customParameters field. The
media stream handler now extracts the token from customParameters,
falling back to query string for backwards compatibility.

Co-authored-by: McWiggles <mcwigglesmcgee@users.noreply.github.com>
2026-02-12 07:55:00 -06:00
asklee-klawd
f8c91b3c5f fix: prevent undefined token in gateway auth config (#13809)
- Guard against undefined/empty token in buildGatewayAuthConfig
- Automatically generate random token when token param is undefined, empty, or whitespace
- Prevents JSON.stringify from writing literal string "undefined" to config
- Add tests for undefined, empty, and whitespace token cases

Fixes #13756

Co-authored-by: Klawd Asklee <klawdebot@gmail.com>
2026-02-12 07:45:38 -06:00
Keshav Rao
2ef4ac08cf fix(gateway): handle async EPIPE on stdout/stderr during shutdown (#13414)
* fix(gateway): handle async EPIPE on stdout/stderr during shutdown

The console capture forward() wrapper catches synchronous EPIPE errors,
but when the receiving pipe closes during shutdown Node emits the error
asynchronously on the stream. Without a listener this becomes an
uncaught exception that crashes the gateway, causing macOS launchd to
permanently unload the service.

Add error listeners on process.stdout and process.stderr inside
enableConsoleCapture() that silently swallow EPIPE/EIO (matching the
existing isEpipeError helper) and re-throw anything else.

Closes #13367

* guard stream error listeners against repeated enableConsoleCapture() calls

Use a separate streamErrorHandlersInstalled flag in loggingState so that
test resets of consolePatched don't cause listener accumulation on
process.stdout/stderr.
2026-02-12 07:45:36 -06:00
0xRain
94bc62ad46 fix(media): strip MEDIA: lines with local paths instead of leaking as text (#14399)
When internal tools (e.g. TTS) emit MEDIA:/tmp/... with absolute paths,
isValidMedia() correctly rejects them for security. However, the rejected
MEDIA: line was kept as visible text in the output, leaking the path to
the user.

Now strip MEDIA: lines that look like local paths even when the path
is invalid, so they never appear as user-visible text.

Closes #14365

Co-authored-by: Echo Ito <echoito@MacBook-Air.local>
2026-02-12 07:45:22 -06:00
Cathryn Lavery
94d6858160 fix(gateway): auto-generate token during gateway install to prevent launchd restart loop (#13813)
When the gateway is installed as a macOS launch agent and no token is
configured, the service enters an infinite restart loop because launchd
does not inherit shell environment variables. Auto-generate a token
during `gateway install` when auth mode is `token` and no token exists,
matching the existing pattern in doctor.ts and configure.gateway.ts.

The token is persisted to the config file and embedded in the plist
EnvironmentVariables for belt-and-suspenders reliability.

Relates-to: #5103, #2433, #1690, #7749
2026-02-12 07:45:09 -06:00
Coy Geek
f836c385ff fix: BlueBubbles webhook auth bypass via loopback proxy trust (#13787)
* fix(an-08): apply security fix

Generated by staged fix workflow.

* fix(an-08): apply security fix

Generated by staged fix workflow.

* fix(an-08): stabilize bluebubbles auth fixture for security patch

Restore the default test password in createMockAccount and add a
fallback password query in createMockRequest when auth is omitted.

This keeps the AN-08 loopback-auth regression tests strict while
preserving existing monitor behavior tests that assume authenticated
webhook fixtures.
2026-02-12 07:12:17 -06:00
大猫子
8dd60fc7d9 feat(telegram): render blockquotes as native <blockquote> tags (#14608) (#14626)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 4a967c51f5
Co-authored-by: lailoo <20536249+lailoo@users.noreply.github.com>
Co-authored-by: sebslight <19554889+sebslight@users.noreply.github.com>
Reviewed-by: @sebslight
2026-02-12 08:11:57 -05:00
Tomsun28
540996f10f feat(provider): Z.AI endpoints + model catalog (#13456) (thanks @tomsun28) (#13456)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 07:01:48 -06:00
vignesh07
b094491cf5 fix(config): restore schema.ts schema helper types after hints refactor 2026-02-12 01:19:46 -08:00
vignesh07
fa427f63b8 refactor(config): restore schema.ts to use schema.hints 2026-02-12 01:19:46 -08:00
0xRain
d8016c30cd fix: add types condition to plugin-sdk export for moduleResolution NodeNext (#14485)
Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
2026-02-12 17:31:48 +09:00
hyf0-agent
5c32989f53 perf: use JSON.parse instead of JSON5.parse for sessions.json (~35x faster) (#14530)
Co-authored-by: hyf0-agent <hyf0-agent@users.noreply.github.com>
2026-02-12 17:10:21 +09:00
Tak Hoffman
16f2492547 changelog: add telegram reaction warning fix entry 2026-02-12 00:30:39 -06:00
0xRain
4b86c9e555 fix(telegram): surface REACTION_INVALID as non-fatal warning (#14340)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 00:28:47 -06:00
Tak Hoffman
4094cef233 changelog: add telegram and slack fix entries 2026-02-12 00:14:31 -06:00
J. Brandon Johnson
472808a207 fix(tests): update thread ID handling in Slack message collection tests (#14108)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 00:13:15 -06:00
NM
3696b15abc fix(slack): change default replyToMode from "off" to "all" (#14364)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 00:13:07 -06:00
0xRain
bbfaac88ff fix(telegram): handle no-text message in model picker editMessageText (#14397)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 00:12:46 -06:00
Tak Hoffman
338bc90f82 changelog: add cron and whatsapp fix entries with contributor thanks 2026-02-11 23:44:56 -06:00
WalterSumbon
39e3d58fe1 fix: prevent cron jobs from skipping execution when nextRunAtMs advances (#14068)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 23:33:22 -06:00
大猫子
a88ea42ec7 fix(cron): prevent one-shot at jobs from re-firing on restart after skip/error (#13845) (#13878)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 23:33:15 -06:00
0xRain
b0dfb83952 fix(cron): use requested agentId for isolated job auth resolution (#13983)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 23:32:45 -06:00
Karim Naguib
7a0591ef87 fix(whatsapp): allow media-only sends and normalize leading blank payloads (#14408)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 23:21:21 -06:00
Marcus Castro
186dc0363f fix: default MIME type for WhatsApp voice messages when Baileys omits it (#14444) 2026-02-11 23:09:09 -06:00
Ajay Rajnikanth
e24d023080 fix(whatsapp): convert Markdown bold/strikethrough to WhatsApp formatting (#14285)
* fix(whatsapp): convert Markdown bold/strikethrough to WhatsApp formatting

* refactor: Move `escapeRegExp` utility function to `utils.js`.

---------

Co-authored-by: Luna AI <luna@coredirection.ai>
2026-02-11 23:09:02 -06:00
Tak Hoffman
2e03131712 chore: add feishu contributor thanks to changelog (openclaw#14448)
Co-authored-by: zhiyi <7426274+youngerstyle@users.noreply.github.com>
Co-authored-by: 王春跃 <80630709+openperf@users.noreply.github.com>
Co-authored-by: Wei Wang <1019875+onevcat@users.noreply.github.com>
Co-authored-by: Cynosure159 <29699738+Cynosure159@users.noreply.github.com>
Co-authored-by: jack-cooper <10961327+jackcooper2015@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
18610a587d chore: refresh lockfile after feishu dep removal (openclaw#14423) thanks @jackcooper2015
Co-authored-by: jack-cooper <10961327+jackcooper2015@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
7ca8d936d5 fix: remove workspace dev dependency in feishu plugin (openclaw#14423) thanks @jackcooper2015
Co-authored-by: jack-cooper <10961327+jackcooper2015@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
cf6e8e18d2 fix: preserve top-level feishu doc block order (openclaw#13994) thanks @Cynosure159
Co-authored-by: Cynosure159 <29699738+Cynosure159@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
a028c0512c fix: use resolved feishu account in status probe (openclaw#11233) thanks @onevcat
Co-authored-by: Wei Wang <1019875+onevcat@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
3d771afe79 fix: tighten feishu mention trigger matching (openclaw#11088) thanks @openperf
Co-authored-by: 王春跃 <80630709+openperf@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
Tak Hoffman
8fdb2e64a7 fix: buffer upload path for feishu SDK (openclaw#10345) thanks @youngerstyle
Co-authored-by: zhiyi <7426274+youngerstyle@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:53:40 -06:00
石川 諒
04e3a66f90 fix(cron): pass agentId to runHeartbeatOnce for main-session jobs (#14140)
* fix(cron): pass agentId to runHeartbeatOnce for main-session jobs

Main-session cron jobs with agentId always ran the heartbeat under
the default agent, ignoring the job's agent binding. enqueueSystemEvent
correctly routed the system event to the bound agent's session, but
runHeartbeatOnce was called without agentId, so the heartbeat ran under
the default agent and never picked up the event.

Thread agentId from job.agentId through the CronServiceDeps type,
timer execution, and the gateway wrapper so heartbeat-runner uses the
correct agent.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* cron: add heartbeat agentId propagation regression test (#14140) (thanks @ishikawa-pro)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-11 22:22:29 -06:00
MarvinDontPanic
04f695e562 fix(cron): isolate schedule errors to prevent one bad job from breaking all jobs (#14385)
Previously, if one cron job had a malformed schedule expression (e.g. invalid cron syntax),
the error would propagate up and break the entire scheduler loop. This meant one misconfigured
job could prevent ALL cron jobs from running.

Changes:
- Wrap per-job schedule computation in try/catch in recomputeNextRuns()
- Track consecutive schedule errors via new scheduleErrorCount field
- Log warnings for schedule errors with job ID and name
- Auto-disable jobs after 3 consecutive schedule errors (with error-level log)
- Clear error count when schedule computation succeeds
- Continue processing other jobs even when one fails

This ensures the scheduler is resilient to individual job misconfigurations while still
providing visibility into problems through logging.

Co-authored-by: Marvin <numegilagent@gmail.com>
2026-02-11 22:17:07 -06:00
Tom Ron
ace5e33cee fix(cron): re-arm timer when onTimer fires during active job execution (#14233)
* fix(cron): re-arm timer when onTimer fires during active job execution

When a cron job takes longer than MAX_TIMER_DELAY_MS (60s), the clamped
timer fires while state.running is still true.  The early return in
onTimer() previously exited without re-arming the timer, leaving no
setTimeout scheduled.  This silently kills the cron scheduler until the
next gateway restart.

The fix calls armTimer(state) before the early return so the scheduler
continues ticking even when a job is in progress.

This is the likely root cause of recurring cron jobs silently skipping,
as reported in #12025.  One-shot (kind: 'at') jobs were unaffected
because they typically complete within a single timer cycle.

Includes a regression test that simulates a slow job exceeding the
timer clamp period and verifies the next occurrence still fires.

* fix: update tests for timer re-arm behavior

- Update existing regression test to expect timer re-arm with non-zero
  delay instead of no timer at all
- Simplify new test to directly verify state.timer is set after onTimer
  returns early due to running guard

* fix: use fixed 60s delay for re-arm to prevent zero-delay hot-loop

When the running guard re-arms the timer, use MAX_TIMER_DELAY_MS
directly instead of calling armTimer() which can compute a zero delay
for past-due jobs.  This prevents a tight spin while still keeping the
scheduler alive.

* style: add curly braces to satisfy eslint(curly) rule
2026-02-11 22:13:27 -06:00
Xinhua Gu
dd6047d998 fix(cron): prevent duplicate fires when multiple jobs trigger simultaneously (#14256)
The `computeNextRunAtMs` function used `nowSecondMs - 1` as the
reference time for croner's `nextRun()`, which caused it to return the
current second as a valid next-run time. When a job fired at e.g.
11:00:00.500, computing the next run still yielded 11:00:00.000 (same
second, already elapsed), causing the scheduler to immediately re-fire
the job in a tight loop (15-21x observed in the wild).

Fix: use `nowSecondMs` directly (no `-1` lookback) and change the
return guard from `>=` to `>` so next-run is always strictly after
the current second.

Fixes #14164
2026-02-11 22:04:17 -06:00
Rodrigo Uroz
b912d3992d (fix): handle Cloudflare 521 and transient 5xx errors gracefully (#13500)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a8347e95c5
Co-authored-by: rodrigouroz <384037+rodrigouroz@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Reviewed-by: @Takhoffman
2026-02-11 21:42:33 -06:00
Tak Hoffman
c28cbac512 CI: add PR size autolabel workflow (#14410) 2026-02-11 21:12:27 -06:00
Jake
631102e714 fix(agents): scope process/exec tools to sessionKey for isolation (#4887)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 5d30672e75
Co-authored-by: mcinteerj <3613653+mcinteerj@users.noreply.github.com>
Co-authored-by: Takhoffman <781889+Takhoffman@users.noreply.github.com>
Reviewed-by: @Takhoffman
2026-02-11 19:55:12 -06:00
Vignesh Natarajan
36e27ad561 Memory: make qmd search-mode flags compatible 2026-02-11 17:51:08 -08:00
Vignesh Natarajan
6d9d4d04ed Memory/QMD: add configurable search mode 2026-02-11 17:51:08 -08:00
cpojer
c2f9f2e1cd chore: Remove accidentally committed .md file. 2026-02-12 09:45:58 +09:00
cpojer
018ebb35ba chore: Clean up pre-commit hook. 2026-02-12 09:44:30 +09:00
cpojer
c2178e2522 chore: Cleanup useless CI job. 2026-02-12 09:37:45 +09:00
cpojer
9df89ceda2 chore: Update deps. 2026-02-12 09:15:12 +09:00
0xRain
1d2c5783fd fix(agents): enable tool call ID sanitization for Anthropic provider (#13830)
Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
2026-02-12 08:42:24 +09:00
0xRain
43818e1583 fix(agents): re-run tool_use pairing repair after history truncation (#13926)
Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
2026-02-12 08:42:05 +09:00
0xRain
bebba124e8 fix(ui): escape raw HTML in chat messages instead of rendering it (#13952)
Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
2026-02-12 08:40:40 +09:00
0xRain
729181bd06 fix(agents): exclude rate limit errors from context overflow classification (#13747)
Co-authored-by: 0xRaini <rain@0xRaini.dev>
2026-02-12 08:40:09 +09:00
Vignesh Natarajan
2f1f82674a Memory/QMD: harden no-results parsing 2026-02-11 15:39:28 -08:00
Vignesh Natarajan
3d343932cf Memory/QMD: treat plain-text no-results as empty 2026-02-11 15:39:28 -08:00
buddyh
4baa43384a fix(media): guard local media reads + accept all path types in MEDIA directive 2026-02-11 15:01:18 -08:00
ENCHIGO
029b77c85b onboard: support custom provider in non-interactive flow (#14223)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 5b98d6514e
Co-authored-by: ENCHIGO <38551565+ENCHIGO@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-11 14:48:45 -05:00
Shadow
c8d9733e41 Changelog: add #13262 entry 2026-02-11 13:27:05 -06:00
Claude Code
a67752e6be fix(discord): use partial mock for @buape/carbon in slash test
Replace the full manual mock with importOriginal spread so new SDK
exports are available automatically. Only ChannelType, MessageType,
and Client are overridden — the rest come from the real module.

Prevents CI breakage when @buape/carbon adds exports (e.g. the
recent StringSelectMenu failure that blocked unrelated PRs).

Closes #13244
2026-02-11 13:10:37 -06:00
Gustavo Madeira Santana
940ce424c8 chore: make review mode switching idempotent 2026-02-11 14:09:16 -05:00
Kyle Tse
4200782a5d fix(heartbeat): honor heartbeat.model config for heartbeat turns (#14103)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: f46080b0ad
Co-authored-by: shtse8 <8020099+shtse8@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-11 14:00:40 -05:00
Gustavo Madeira Santana
72fbfaa755 chore: making PR review chores deterministic + less token hungry 2026-02-11 13:21:00 -05:00
0xRain
93411b74a0 fix(cli): exit with non-zero code when configure/agents-add wizards are cancelled (#14156)
* fix(cli): exit with non-zero code when configure/agents-add wizards are cancelled

Follow-up to the onboard cancel fix. The configure wizard and
agents add wizard also caught WizardCancelledError and exited with
code 0, which signals success to callers. Change to exit(1) for
consistency — user cancellation is not a successful completion.

This ensures scripts that chain these commands with set -e will
correctly stop when the user cancels.

* fix(cli): make wizard cancellations exit non-zero (#14156) (thanks @0xRaini)

---------

Co-authored-by: Rain <rain@Rains-MBA-M4.local>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-11 13:07:30 -05:00
Seb Slight
6758b6bfe4 docs(channels): modernize imessage docs page (#14213) 2026-02-11 12:58:02 -05:00
Dario Zhang
e85bbe01f2 fix: report subagent timeout as 'timed out' instead of 'completed successfully' (#13996)
* fix: report subagent timeout as 'timed out' instead of 'completed successfully'

* fix: propagate subagent timeout status across agent.wait (#13996) (thanks @dario-github)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-11 12:55:30 -05:00
Seb Slight
2c6569a488 docs(channels): modernize slack docs page (#14205) 2026-02-11 12:49:10 -05:00
0xRain
6d723c9f8a fix(agents): honor heartbeat.model override instead of session model (#14181)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: f19b789057
Co-authored-by: 0xRaini <190923101+0xRaini@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-11 12:46:51 -05:00
Seb Slight
8c963dc5a6 docs(channels): modernize whatsapp docs page (#14202) 2026-02-11 12:31:56 -05:00
Shadow
e95f41b5df Discord: honor explicit thread type 2026-02-11 11:23:19 -06:00
Rain
9e92fc8fa1 fix(discord): default standalone threads to public type (#14147)
When creating a Discord thread without a messageId (standalone thread),
the Discord API defaults to type 12 (private). Most users expect public.

- Default standalone non-forum threads to ChannelType.PublicThread (11)
- Add optional type field to DiscordThreadCreate for explicit control

Closes #14147
2026-02-11 11:23:19 -06:00
Rain
42f7538320 fix(discord): default standalone threads to public type (#14147)
When creating a Discord thread without a messageId, the API defaults
to type 12 (private thread) which is unexpected. This adds an explicit
type: PublicThread (11) for standalone thread creation in non-forum
channels, matching user expectations.

Closes #14147
2026-02-11 11:23:19 -06:00
Seb Slight
b90610c099 docs(nav): move grammy page to technical reference (#14198) 2026-02-11 12:19:44 -05:00
Seb Slight
6bee638648 docs(channels): modernize discord docs page (#14190) 2026-02-11 12:12:31 -05:00
Shadow
c4018a9c57 PI: assign landpr to self 2026-02-11 11:12:04 -06:00
Seb Slight
a98d7c26df docs(channels): fix telegram card icon (#14193) 2026-02-11 12:10:52 -05:00
Seb Slight
880f92c9e4 docs(channels): modernize telegram docs page (#14168) 2026-02-11 11:58:06 -05:00
J young Lee
2aa9570465 fix(slack): detect control commands when message starts with @mention (#14142)
Merged via /review-pr-v2 -> /prepare-pr-v2 -> /merge-pr-v2.

Prepared head SHA: cb0b4f6a3b
Co-authored-by: beefiker <55247450+beefiker@users.noreply.github.com>
Co-authored-by: gumadeiras <gumadeiras@gmail.com>
Reviewed-by: @gumadeiras
2026-02-11 11:41:48 -05:00
Kyle Tse
50a60b8be6 fix: use configured base URL for Ollama model discovery (#14131)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 2292d2de6d
Co-authored-by: shtse8 <8020099+shtse8@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-11 10:51:59 -05:00
Seb Slight
3ed06c6f36 docs: modernize gateway configuration page (Phase 1) (#14111)
* docs(configuration): split into overview + full reference with Mintlify components

* docs(configuration): use tooltip for JSON5 format note

* docs(configuration): fix Accordion closing tags inside list contexts

* docs(configuration): expand intro to reflect full config surface

* docs(configuration): trim intro to three concise bullets

* docs(configuration-examples): revert all branch changes

* docs(configuration): improve hot-reload section with tabs and accordion

* docs(configuration): uncramp hot-reload — subheadings, bullet list, warning

* docs(configuration): restore hot-apply vs restart table

* docs(configuration): fix hot-reload table against codebase

* docs: add configuration-reference.md — full field-by-field reference

* docs(gateway): refresh runbook and align config reference

* docs: include pending docs updates and install graphic
2026-02-11 10:44:34 -05:00
Seb Slight
4625da476a docs(skills): update mintlify skill to reference docs/ directory (#14125) 2026-02-11 09:46:52 -05:00
Seb Slight
f093ea1ed6 chore: update AGENTS.md and add mintlify skill (#14123) 2026-02-11 09:41:46 -05:00
Sebastian
a1a61f35df chore(irc): sync plugin version to 2026.2.10 2026-02-11 08:38:33 -05:00
Sebastian
f32214ea27 fix(cli): drop logs --localTime alias noise 2026-02-11 08:35:49 -05:00
constansino
66ca5746ce fix(config): avoid redacting maxTokens-like fields (#14006)
* fix(config): avoid redacting maxTokens-like fields

* fix(config): finalize redaction prep items (#14006) (thanks @constansino)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-11 08:32:00 -05:00
Peter Lee
851fcb2617 feat: Add --localTime option to logs command for local timezone display (#13818)
* feat: add --localTime options to make logs to show time with local time zone

fix #12447

* fix: prep logs local-time option and docs (#13818) (thanks @xialonglee)

---------

Co-authored-by: xialonglee <li.xialong@xydigit.com>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-11 08:24:08 -05:00
Sk Akram
620cf381ff fix: don't lowercase Slack channel IDs (#14055) 2026-02-11 20:53:58 +09:00
Peter Steinberger
92702af7a2 fix(plugins): ignore install scripts during plugin/hook install 2026-02-11 12:04:30 +01:00
Peter Steinberger
cfd112952e fix(gateway): default-deny missing connect scopes 2026-02-11 12:04:30 +01:00
Rain
27453f5a31 fix(web-search): handle xAI Responses API format in Grok provider
The xAI /v1/responses API returns content in a structured format with
typed output blocks (type: 'message') containing typed content blocks
(type: 'output_text') and url_citation annotations. The previous code
only checked output[0].content[0].text without filtering by type,
which could miss content in responses with multiple output entries.

Changes:
- Update GrokSearchResponse type to include annotations on content blocks
- Filter output blocks by type='message' and content by type='output_text'
- Extract url_citation annotations as fallback citations when top-level
  citations array is empty
- Deduplicate annotation-derived citation URLs
- Update tests for the new structured return type

Closes #13520
2026-02-11 11:59:07 +01:00
ryan-crabbe
a36b9be245 Feat/litellm provider (#12823)
* feat: add LiteLLM provider types, env var, credentials, and auth choice

Add litellm-api-key auth choice, LITELLM_API_KEY env var mapping,
setLitellmApiKey() credential storage, and LITELLM_DEFAULT_MODEL_REF.

* feat: add LiteLLM onboarding handler and provider config

Add applyLitellmProviderConfig which properly registers
models.providers.litellm with baseUrl, api type, and model definitions.
This fixes the critical bug from PR #6488 where the provider entry was
never created, causing model resolution to fail at runtime.

* docs: add LiteLLM provider documentation

Add setup guide covering onboarding, manual config, virtual keys,
model routing, and usage tracking. Link from provider index.

* docs: add LiteLLM to sidebar navigation in docs.json

Add providers/litellm to both English and Chinese provider page lists
so the docs page appears in the sidebar navigation.

* test: add LiteLLM non-interactive onboarding test

Wire up litellmApiKey flag inference and auth-choice handler for the
non-interactive onboarding path, and add an integration test covering
profile, model default, and credential storage.

* fix: register --litellm-api-key CLI flag and add preferred provider mapping

Wire up the missing Commander CLI option, action handler mapping, and
help text for --litellm-api-key. Add litellm-api-key to the preferred
provider map for consistency with other providers.

* fix: remove zh-CN sidebar entry for litellm (no localized page yet)

* style: format buildLitellmModelDefinition return type

* fix(onboarding): harden LiteLLM provider setup (#12823)

* refactor(onboarding): keep auth-choice provider dispatcher under size limit

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-11 11:46:56 +01:00
Peter Steinberger
5741b6cb3f docs: start 2026.2.10 changelog section 2026-02-11 11:27:58 +01:00
Peter Steinberger
1872d0c592 chore: bump version to 2026.2.10 2026-02-11 11:27:23 +01:00
andreesg
fb84e18bc3 docs: remove outdated pricing information
- Remove specific machine type (CX22) and pricing
- Hetzner pricing and server types change frequently
- Keep focus on technical approach rather than costs
2026-02-11 10:50:07 +01:00
andreesg
75f5da78f0 docs: add Terraform IaC approach to Hetzner guide
- Add Infrastructure as Code section to Hetzner installation docs
- Links to community-maintained Terraform repositories
- Provides alternative for users preferring IaC workflows
- Includes cost estimate and feature overview

Related: Discussion #12532
2026-02-11 10:50:07 +01:00
Marcus Castro
841dbeee0a fix(ui): coerce form values to schema types before config.set (#13468)
Co-authored-by: Gustavo Madeira Santana <gumadeiras@users.noreply.github.com>
2026-02-11 01:25:07 -05:00
Gustavo Madeira Santana
78eca155ac chore: make merge PR comment mandatory + skill name fix 2026-02-11 00:45:54 -05:00
the sun gif man
aade133978 🤖 memory-lancedb: avoid plugin-sdk enum helper in local TypeBox schema (#13897) 2026-02-10 21:28:32 -08:00
the sun gif man
80b56cabc2 🤖 macos: force session preview submenu repaint after async load (#13890) 2026-02-10 21:11:04 -08:00
Rodrigo Uroz
7f1712c1ba (fix): enforce embedding model token limit to prevent overflow (#13455)
* fix: enforce embedding model token limit to prevent 8192 overflow

- Replace EMBEDDING_APPROX_CHARS_PER_TOKEN=1 with UTF-8 byte length
  estimation (safe upper bound for tokenizer output)
- Add EMBEDDING_MODEL_MAX_TOKENS=8192 hard cap
- Add splitChunkToTokenLimit() that binary-searches for the largest
  safe split point, with surrogate pair handling
- Add enforceChunkTokenLimit() wrapper called in indexFile() after
  chunkMarkdown(), before any embedding API call
- Fixes: session files with large JSONL entries could produce chunks
  exceeding text-embedding-3-small's 8192 token limit

Tests: 2 new colocated tests in manager.embedding-token-limit.test.ts
- Verifies oversized ASCII chunks are split to <=8192 bytes each
- Verifies multibyte (emoji) content batching respects byte limits

* fix: make embedding token limit provider-aware

- Add optional maxInputTokens to EmbeddingProvider interface
- Each provider (openai, gemini, voyage) reports its own limit
- Known-limits map as fallback: openai 8192, gemini 2048, voyage 32K
- Resolution: provider field > known map > default 8192
- Backward compatible: local/llama uses fallback

* fix: enforce embedding input size limits (#13455) (thanks @rodrigouroz)

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-10 20:10:17 -06:00
Tak Hoffman
c95b3783ef Changelog: note gateway thinking/tool WS streaming (#10568) (thanks @nk1tz) 2026-02-10 19:30:20 -06:00
Nate
2b02e8a7a8 feat(gateway): stream thinking events and decouple tool events from verbose level (#10568) 2026-02-10 19:17:21 -06:00
Tak Hoffman
d2c2f4185b Heartbeat: inject cron-style current time into prompts (#13733)
* Heartbeat: inject cron-style current time into prompts

* Tests: fix type for web heartbeat timestamp test

* Infra: inline heartbeat current-time injection
2026-02-10 18:58:45 -06:00
Gustavo Madeira Santana
a853ded782 fix(pairing): use actual code in pairing approval text 2026-02-10 19:48:02 -05:00
0xRain
74273d62d0 fix(pairing): show actual code in approval command instead of placeholder (#13723)
* fix(pairing): show actual code in approval command instead of placeholder

The pairing reply shown to new users included the approval command with
a literal '<code>' placeholder. Users had to manually copy the code
from one line and substitute it into the command.

Now shows the ready-to-copy command with the real pairing code:

Before: openclaw pairing approve telegram <code>
After:  openclaw pairing approve telegram abc123

Fixed in both the shared pairing message builder and the Telegram
inline pairing reply.

* test(pairing): update test to expect actual code instead of placeholder

---------

Co-authored-by: Echo Ito <echoito@MacBook-Air.local>
2026-02-10 19:45:16 -05:00
Omair Afzal
88428260ce fix(web_search): remove unsupported include param from Grok API calls (#12910) (#12945)
xAI's /v1/responses endpoint does not support the 'include' parameter,
returning 400 'Argument not supported: include'. Inline citations are
returned automatically when available — no explicit request needed.

Closes #12910

Co-authored-by: Luna AI <luna@coredirection.ai>
2026-02-10 18:27:35 -06:00
Bill Chirico
ca629296c6 feat(hooks): add agentId support to webhook mappings (#13672)
* feat(hooks): add agentId support to webhook mappings

Allow webhook mappings to route hook runs to a specific agent via
the new `agentId` field. This enables lightweight agents with minimal
bootstrap files to handle webhooks, reducing token cost per hook run.

The agentId is threaded through:
- HookMappingConfig (config type + zod schema)
- HookMappingResolved + HookAction (mapping types)
- normalizeHookMapping + buildActionFromMapping (mapping logic)
- mergeAction (transform override support)
- HookAgentPayload + normalizeAgentPayload (direct /hooks/agent endpoint)
- dispatchAgentHook → CronJob.agentId (server dispatch)

The existing runCronIsolatedAgentTurn already supports agentId on
CronJob — this change simply wires it through from webhook mappings.

Usage in config:
  hooks.mappings[].agentId = "my-agent"

Usage via POST /hooks/agent:
  { "message": "...", "agentId": "my-agent" }

Includes tests for mapping passthrough and payload normalization.
Includes doc updates for webhook.md.

* fix(hooks): enforce webhook agent routing policy + docs/changelog updates (#13672) (thanks @BillChirico)

* fix(hooks): harden explicit agent allowlist semantics (#13672) (thanks @BillChirico)

---------

Co-authored-by: Pip <pip@openclaw.ai>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-10 19:23:58 -05:00
Marcus Castro
45488e4ec9 fix: remap session JSONL chunk line numbers to original source positions (#12102)
* fix: remap session JSONL chunk line numbers to original source positions

buildSessionEntry() flattens JSONL messages into plain text before
chunkMarkdown() assigns line numbers. The stored startLine/endLine
values therefore reference positions in the flattened text, not the
original JSONL file.

- Add lineMap to SessionFileEntry tracking which JSONL line each
  extracted message came from
- Add remapChunkLines() to translate chunk positions back to original
  JSONL lines after chunking
- Guard remap with source === "sessions" to prevent misapplication
- Include lineMap in content hash so existing sessions get re-indexed

Fixes #12044

* memory: dedupe session JSONL parsing

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-10 18:09:24 -06:00
Onur
424d2dddf5 fix: prevent act:evaluate hangs from getting browser tool stuck/killed (#13498)
* fix(browser): prevent permanent timeout after stuck evaluate

Thread AbortSignal from client-fetch through dispatcher to Playwright
operations. When a timeout fires, force-disconnect the Playwright CDP
connection to unblock the serialized command queue, allowing the next
call to reconnect transparently.

Key changes:
- client-fetch.ts: proper AbortController with signal propagation
- pw-session.ts: new forceDisconnectPlaywrightForTarget()
- pw-tools-core.interactions.ts: accept signal, align inner timeout
  to outer-500ms, inject in-browser Promise.race for async evaluates
- routes/dispatcher.ts + types.ts: propagate signal through dispatch
- server.ts + bridge-server.ts: Express middleware creates AbortSignal
  from request lifecycle
- client-actions-core.ts: add timeoutMs to evaluate type

Fixes #10994

* fix(browser): v2 - force-disconnect via Connection.close() instead of browser.close()

When page.evaluate() is stuck on a hung CDP transport, browser.close() also
hangs because it tries to send a close command through the same stuck pipe.

v2 fix: forceDisconnectPlaywrightForTarget now directly calls Playwright's
internal Connection.close() which locally rejects all pending callbacks and
emits 'disconnected' without touching the network. This instantly unblocks
all stuck Playwright operations.

closePlaywrightBrowserConnection (clean shutdown) now also has a 3s timeout
fallback that drops to forceDropConnection if browser.close() hangs.

Fixes permanent browser timeout after stuck evaluate.

* fix(browser): v3 - fire-and-forget browser.close() instead of Connection.close()

v2's forceDropConnection called browser._connection.close() which corrupts
the entire Playwright instance because Connection is shared across all
objects (BrowserType, Browser, Page, etc.). This prevented reconnection
with cascading 'connectOverCDP: Force-disconnected' errors.

v3 fix: forceDisconnectPlaywrightForTarget now:
1. Nulls cached connection immediately
2. Fire-and-forgets browser.close() (doesn't await — it may hang)
3. Next connectBrowser() creates a fresh connectOverCDP WebSocket

Each connectOverCDP creates an independent WebSocket to the CDP endpoint,
so the new connection is unaffected by the old one's pending close.
The old browser.close() eventually resolves when the in-browser evaluate
timeout fires, or the old connection gets GC'd.

* fix(browser): v4 - clear connecting state and remove stale disconnect listeners

The reconnect was failing because:
1. forceDisconnectPlaywrightForTarget nulled cached but not connecting,
   so subsequent calls could await a stale promise
2. The old browser's 'disconnected' event handler raced with new
   connections, nulling the fresh cached reference

Fix: null both cached and connecting, and removeAllListeners on the
old browser before fire-and-forget close.

* fix(browser): v5 - use raw CDP Runtime.terminateExecution to kill stuck evaluate

When forceDisconnectPlaywrightForTarget fires, open a raw WebSocket
to the stuck page's CDP endpoint and send Runtime.terminateExecution.
This kills running JS without navigating away or crashing the page.
Also clear connecting state and remove stale disconnect listeners.

* fix(browser): abort cancels stuck evaluate

* Browser: always cleanup evaluate abort listener

* Chore: remove Playwright debug scripts

* Docs: add CDP evaluate refactor plan

* Browser: refactor Playwright force-disconnect

* Browser: abort stops evaluate promptly

* Node host: extract withTimeout helper

* Browser: remove disconnected listener safely

* Changelog: note act:evaluate hang fix

---------

Co-authored-by: Bob <bob@dutifulbob.com>
2026-02-11 07:54:48 +08:00
Riccardo Giorato
be6de9bb75 Update Together default model to together/moonshotai/Kimi-K2.5 (#13324) 2026-02-11 08:39:15 +09:00
Vignesh
fa906b26ad feat: IRC — add first-class channel support
Adds IRC as a first-class channel with core config surfaces (schema/hints/dock), plugin auto-enable detection, routing/policy alignment, and docs/tests.

Co-authored-by: Vignesh <vigneshnatarajan92@gmail.com>
2026-02-10 17:33:57 -06:00
Sebastian
90f58333e9 test(docker): make bash 3.2 compatibility check portable 2026-02-10 18:04:48 -05:00
Daniel Olshansky
31f616d45b feat: ClawDock - shell docker helpers for OpenClaw development (#12817)
Discussion: https://github.com/openclaw/openclaw/discussions/13528

## Checklist

- [x] **Mark as AI-assisted in the PR title or description** - Implemented by 🤖, reviewed by 👨‍💻 
- [x] **Note the degree of testing** - fully tested and I use it myself
- [x] **Include prompts or session logs if possible (super helpful!)** - I can try doing a "resume" on a few sessions, but don't think it'll provide value. Lmk if this is a blocker.
- [x] **Confirm you understand what the code does** - It's simple :)

## Summary of changes

- **ClawDock** - Shell helpers replace verbose `docker-compose` commands with simple `clawdock-*` shortcuts
- **Zero-config setup** - First run auto-detects the OpenClaw project directory from common paths and saves the config for future use
- **No extra dependencies** - Just bash
- **Built-in auth & device pairing helpers** - `clawdock-fix-token`, `clawdock-dashboard`, etc to handle gateay setup, streamline web UI, etc...
- **Updated Docker docs** - Installation docs now include the optional ClawDock helper setup for users who want simplified container management

## Example Usage

```bash
$ clawdock-help

🦞 ClawDock - Docker Helpers for OpenClaw

 Basic Operations
  clawdock-start       Start the gateway
  clawdock-stop        Stop the gateway
  clawdock-restart     Restart the gateway
  clawdock-status      Check container status
  clawdock-logs        View live logs (follows)

🐚 Container Access
  clawdock-shell       Shell into container (openclaw alias ready)
  clawdock-cli         Run CLI commands (e.g., clawdock-cli status)
  clawdock-exec <cmd>  Execute command in gateway container

🌐 Web UI & Devices
  clawdock-dashboard   Open web UI in browser (auto-guides you)
  clawdock-devices     List device pairings (auto-guides you)
  clawdock-approve <id> Approve device pairing (with examples)

⚙️  Setup & Configuration
  clawdock-fix-token   Configure gateway token (run once)

🔧 Maintenance
  clawdock-rebuild     Rebuild Docker image
  clawdock-clean       ⚠️  Remove containers & volumes (nuclear)

🛠️  Utilities
  clawdock-health      Run health check
  clawdock-token       Show gateway auth token
  clawdock-cd          Jump to openclaw project directory
  clawdock-config      Open config directory (~/.openclaw)
  clawdock-workspace   Open workspace directory

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 First Time Setup
  1. clawdock-start          # Start the gateway
  2. clawdock-fix-token      # Configure token
  3. clawdock-dashboard      # Open web UI
  4. clawdock-devices        # If pairing needed
  5. clawdock-approve <id>   # Approve pairing

💬 WhatsApp Setup
  clawdock-shell
    > openclaw channels login --channel whatsapp
    > openclaw status

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

💡 All commands guide you through next steps!
📚 Docs: https://docs.openclaw.ai
```\n\nCo-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-10 16:04:41 -05:00
Gustavo Madeira Santana
2dcaaa4b61 chore: add format:fix to AGENTS 2026-02-10 15:46:03 -05:00
Gustavo Madeira Santana
a6187b568c chore: add missing CHANGELOG entry 2026-02-10 15:12:37 -05:00
Gustavo Madeira Santana
c4d3800c29 fix: resolve message tool lint error (#13453) (thanks @liebertar) 2026-02-10 15:09:58 -05:00
Cklee
22458f57f2 fix(agents): strip [Historical context: ...] and tool call text from streaming path (#13453)
- Add [Historical context: ...] marker pattern to stripDowngradedToolCallText
- Apply stripDowngradedToolCallText in emitBlockChunk streaming path
- Previously only stripBlockTags ran during streaming, leaking [Tool Call: ...] markers to users
- Add 7 test cases for the new pattern stripping
2026-02-10 15:04:52 -05:00
meaadore1221-afk
67d25c6533 fix: strip reasoning tags from messaging tool text to prevent <think> leakage (#11053)
Co-authored-by: MEA <mea@MEAdeMac-mini.local>
2026-02-10 14:48:17 -05:00
williamtwomey
5fab11198d Fix matrix media attachments (#12967) thanks @williamtwomey
Co-authored-by: Gustavo Madeira Santana @gumadeiras
2026-02-10 13:18:47 -05:00
Shadow
96c46ed612 Docs: restore maintainers in contributing 2026-02-10 10:33:32 -06:00
Shadow
71fd054711 Revert "fix(credits): deduplicate contributors by GitHub username and display name"
This reverts commit d2f5d45f08.
2026-02-10 10:25:51 -06:00
Shadow
614befd15d Revert "credits: categorize direct changes, exclude bots, fix MDX (#13322)"
This reverts commit 8666d9f837.
2026-02-10 10:25:48 -06:00
Shadow
cfd1fa4bd2 Revert "CI: extend stale timelines to be contributor-friendly (#13209)"
This reverts commit 656a467518.
2026-02-10 10:24:28 -06:00
Sebastian
8933010e84 docs(env): clarify .env precedence and config token note 2026-02-10 10:18:36 -05:00
Omair Afzal
6ac56baf8e docs: clarify which workspace files are injected into context window (#12937)
* docs: clarify which workspace files are injected into context window (#12909)

The system prompt docs listed bootstrap files but omitted MEMORY.md,
which IS injected when present. This led users to assume memory files
are on-demand only and not consuming context tokens.

Changes:
- Add MEMORY.md to the bootstrap file list
- Note that all listed files consume tokens on every turn
- Clarify that memory/*.md daily files are NOT injected (on-demand only)
- Document sub-agent bootstrap filtering (AGENTS.md + TOOLS.md only)

Closes #12909

* docs: mention memory.md alternate filename in bootstrap list

Address review feedback: the runtime also injects lowercase memory.md
(DEFAULT_MEMORY_ALT_FILENAME) when present.

* docs: align memory bootstrap docs (#12937) (thanks @omair445)

---------

Co-authored-by: Luna AI <luna@coredirection.ai>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-10 10:06:23 -05:00
Mateusz Michalik
6731c6a1cd fix(docker): support Bash 3.2 in docker-setup.sh (#9441)
* fix(docker): use Bash 3.2-compatible upsert_env in docker-setup.sh

* refactor(docker): simplify argument handling in write_extra_compose function

* fix(docker): add bash 3.2 regression coverage (#9441) (thanks @mateusz-michalik)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-10 09:55:43 -05:00
Gustavo Madeira Santana
2914cb1d48 Onboard: rename Custom API Endpoint to Custom Provider 2026-02-10 07:36:04 -05:00
Blossom
c0befdee0b feat(onboard): add custom/local API configuration flow (#11106)
* feat(onboard): add custom/local API configuration flow

* ci: retry macos check

* fix: expand custom API onboarding (#11106) (thanks @MackDing)

* fix: refine custom endpoint detection (#11106) (thanks @MackDing)

* fix: streamline custom endpoint onboarding (#11106) (thanks @MackDing)

* fix: skip model picker for custom endpoint (#11106) (thanks @MackDing)

* fix: avoid allowlist picker for custom endpoint (#11106) (thanks @MackDing)

* Onboard: reuse shared fetch timeout helper (#11106) (thanks @MackDing)

* Onboard: clarify default base URL name (#11106) (thanks @MackDing)

---------

Co-authored-by: OpenClaw Contributor <contributor@openclaw.ai>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-10 07:31:02 -05:00
max
8666d9f837 credits: categorize direct changes, exclude bots, fix MDX (#13322) 2026-02-10 02:27:48 -08:00
max
d2f5d45f08 fix(credits): deduplicate contributors by GitHub username and display name
* Scripts: add sync-credits.py to populate maintainers/contributors from git/GitHub

* fix(credits): deduplicate contributors by GitHub username and display name
2026-02-10 02:04:29 -08:00
vignesh07
53fd26a960 maintainers: mention QMD 2026-02-10 01:14:00 -08:00
vignesh07
d8b9aff2f5 update maintainers 2026-02-10 01:05:20 -08:00
quotentiroler
bf308cf6a8 CI: expand Docker Release paths-ignore to skip on any markdown 2026-02-10 00:39:26 -08:00
quotentiroler
57e60f5c05 Docs: consolidate maintainer PR workflow into PR_WORKFLOW.md 2026-02-10 00:33:24 -08:00
Vignesh Natarajan
8d80212f99 docs: credit memorySearch changelog 2026-02-10 00:21:27 -08:00
Vignesh Natarajan
a76dea0d23 Config: clarify memorySearch migration precedence 2026-02-10 00:21:27 -08:00
Vignesh Natarajan
8688730161 Config: migrate legacy top-level memorySearch 2026-02-10 00:21:27 -08:00
Vignesh Natarajan
efc79f69a2 Gateway: eager-init QMD backend on startup 2026-02-09 23:58:34 -08:00
Vignesh
ef4a0e92b7 fix(memory/qmd): scope query to managed collections (#11645) 2026-02-09 23:35:27 -08:00
quotentiroler
40919b1fc8 fix(test): add StringSelectMenu to @buape/carbon mock 2026-02-09 23:11:53 -08:00
Peter Steinberger
53273b490b fix(auto-reply): prevent sender spoofing in group prompts 2026-02-10 00:44:38 -06:00
Shadow
8ff1618bfc Discord: add exec approval cleanup option (#13205) 2026-02-10 00:39:42 -06:00
max
656a467518 CI: extend stale timelines to be contributor-friendly (#13209)
Extends stale automation timelines:

- Issues: 30 days stale → 14 days close (44 total, was 12)
- PRs: 14 days stale → 7 days close (21 total, was 8)

PR #13209
2026-02-09 22:34:36 -08:00
Shadow
4537ebc43a fix: enforce Discord agent component DM auth (#11254) (thanks @thedudeabidesai) 2026-02-10 00:26:59 -06:00
max
f17c978f5c refactor(security,config): split oversized files (#13182)
refactor(security,config): split oversized files using dot-naming convention

- audit-extra.ts (1,199 LOC) -> barrel (31) + sync (559) + async (668)
- schema.ts (1,114 LOC) -> schema (353) + field-metadata (729)
- Add tmp-refactoring-strategy.md documenting Wave 1-4 plan

PR #13182
2026-02-09 22:22:29 -08:00
Shadow
47f6bb4146 Commands: add commands.allowFrom config 2026-02-09 23:58:52 -06:00
Shadow
e7f0769c82 CI: configure stale automation 2026-02-09 23:37:12 -06:00
zerone0x
1d46ca3a95 fix(signal): enforce mention gating for group messages (#13124)
* fix(signal): enforce mention gating for group messages

Signal group messages bypassed mention gating, causing the bot to reply
even when requireMention was enabled and the message did not mention
the bot. This aligns Signal with Slack, Discord, Telegram, and iMessage
which all enforce mention gating correctly.

Fixes #13106

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(signal): keep pending history context for mention-gated skips (#13124) (thanks @zerone0x)

---------

Co-authored-by: Yansu <no-reply@yansu.ai>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-09 23:19:07 -06:00
Marcus Castro
137b7d9aab fix(ui): prioritize displayName over label in webchat session picker (#13108)
* fix(ui): prioritize displayName over label in webchat session picker

The session picker dropdown in the webchat UI was showing raw session
keys instead of human-readable display names. resolveSessionDisplayName()
checked label before displayName and formatted displayName-based entries
as key (displayName) instead of displayName (key).

Swap the priority so displayName is checked first, and use a consistent
humanName (key) format for both displayName and label fallbacks.

Fixes #6645

* test: use deterministic updatedAt in session display name tests
2026-02-10 00:02:54 -05:00
Shadow
f38dfe4544 Chore: add testflight auto-response 2026-02-09 22:52:46 -06:00
Tak Hoffman
72f89b1f53 Docker: include A2UI sources for bundle (#13114)
* Docker: include A2UI sources for bundle

* Build: fail bundling when sources missing and no prebuilt A2UI bundle
2026-02-09 22:44:59 -06:00
Gustavo Madeira Santana
e19a23520c fix: unify session maintenance and cron run pruning (#13083)
* fix: prune stale session entries, cap entry count, and rotate sessions.json

The sessions.json file grows unbounded over time. Every heartbeat tick (default: 30m)
triggers multiple full rewrites, and session keys from groups, threads, and DMs
accumulate indefinitely with large embedded objects (skillsSnapshot,
systemPromptReport). At >50MB the synchronous JSON parse blocks the event loop,
causing Telegram webhook timeouts and effectively taking the bot down.

Three mitigations, all running inside saveSessionStoreUnlocked() on every write:

1. Prune stale entries: remove entries with updatedAt older than 30 days
   (configurable via session.maintenance.pruneDays in openclaw.json)

2. Cap entry count: keep only the 500 most recently updated entries
   (configurable via session.maintenance.maxEntries). Entries without updatedAt
   are evicted first.

3. File rotation: if the existing sessions.json exceeds 10MB before a write,
   rename it to sessions.json.bak.{timestamp} and keep only the 3 most recent
   backups (configurable via session.maintenance.rotateBytes).

All three thresholds are configurable under session.maintenance in openclaw.json
with Zod validation. No env vars.

Existing tests updated to use Date.now() instead of epoch-relative timestamps
(1, 2, 3) that would be incorrectly pruned as stale.

27 new tests covering pruning, capping, rotation, and integration scenarios.

* feat: auto-prune expired cron run sessions (#12289)

Add TTL-based reaper for isolated cron run sessions that accumulate
indefinitely in sessions.json.

New config option:
  cron.sessionRetention: string | false  (default: '24h')

The reaper runs piggy-backed on the cron timer tick, self-throttled
to sweep at most every 5 minutes. It removes session entries matching
the pattern cron:<jobId>:run:<uuid> whose updatedAt + retention < now.

Design follows the Kubernetes ttlSecondsAfterFinished pattern:
- Sessions are persisted normally (observability/debugging)
- A periodic reaper prunes expired entries
- Configurable retention with sensible default
- Set to false to disable pruning entirely

Files changed:
- src/config/types.cron.ts: Add sessionRetention to CronConfig
- src/config/zod-schema.ts: Add Zod validation for sessionRetention
- src/cron/session-reaper.ts: New reaper module (sweepCronRunSessions)
- src/cron/session-reaper.test.ts: 12 tests covering all paths
- src/cron/service/state.ts: Add cronConfig/sessionStorePath to deps
- src/cron/service/timer.ts: Wire reaper into onTimer tick
- src/gateway/server-cron.ts: Pass config and session store path to deps

Closes #12289

* fix: sweep cron session stores per agent

* docs: add changelog for session maintenance (#13083) (thanks @skyfallsin, @Glucksberg)

* fix: add warn-only session maintenance mode

* fix: warn-only maintenance defaults to active session

* fix: deliver maintenance warnings to active session

* docs: add session maintenance examples

* fix: accept duration and size maintenance thresholds

* refactor: share cron run session key check

* fix: format issues and replace defaultRuntime.warn with console.warn

---------

Co-authored-by: Pradeep Elankumaran <pradeepe@gmail.com>
Co-authored-by: Glucksberg <markuscontasul@gmail.com>
Co-authored-by: max <40643627+quotentiroler@users.noreply.github.com>
Co-authored-by: quotentiroler <max.nussbaumer@maxhealth.tech>
2026-02-09 20:42:35 -08:00
Jamieson O'Reilly
0657d7c772 docs: expand vulnerability reporting guidelines in SECURITY.md 2026-02-10 15:39:04 +11:00
Jamieson O'Reilly
b39669d1b4 docs: add vulnerability reporting guidelines to CONTRIBUTING.md 2026-02-10 15:39:04 +11:00
quotentiroler
a26670a2fb refactor: consolidate fetchWithTimeout into shared utility 2026-02-09 20:34:56 -08:00
Jake
757522fb48 fix(memory): default batch embeddings to off
Disables async batch embeddings by default for memory indexing; batch remains opt-in via agents.defaults.memorySearch.remote.batch.enabled.

(#13069) Thanks @mcinteerj.

Co-authored-by: Jake McInteer <mcinteerj@gmail.com>
2026-02-09 22:31:58 -06:00
quotentiroler
5c62e4d51b Improve code analyzer for independent packages, CI: only run release-check on push to main 2026-02-09 19:57:13 -08:00
Evan Reid
0c7bc303c9 fix(tools): correct Grok response parsing for xAI Responses API (#13049)
* fix(tools): correct Grok response parsing for xAI Responses API

The xAI Responses API returns content in output[0].content[0].text,
not in output_text field. Updated GrokSearchResponse type and
runGrokSearch to extract content from the correct path.

Fixes the 'No response' issue when using Grok web search.

* fix(tools): harden Grok web_search parsing (#13049) (thanks @ereid7)

---------

Co-authored-by: erai <erai@erais-Mac-mini.local>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-09 21:51:24 -06:00
quotentiroler
8fad4c2844 chore(deps): update dependencies, remove hono pinning 2026-02-09 19:35:37 -08:00
quotentiroler
cc87c0ed7c Update contributing, deduplicate more functions 2026-02-09 19:21:33 -08:00
quotentiroler
453eaed4dc improve pre-commit hook 2026-02-09 18:59:42 -08:00
quotentiroler
53910f3643 Deduplicate more 2026-02-09 18:56:58 -08:00
max
c4d9b6eadb fix: docs broken links and improve link checker (#13056)
* docs: fix broken links checker and add CI docs

- Replace buggy mint broken-links with existing docs:check-links script
- Fix zh-CN/vps.md broken links (/railway  /install/railway)
- Add docs/ci.md explaining CI pipeline
- Add Experiments group to docs.json navigation

* improve docs checker
2026-02-09 18:45:06 -08:00
Rami Abdelrazzaq
c2b2d535fb fix: suggest /clear in context overflow error message (#12973)
* fix: suggest /reset in context overflow error message

When the context window overflows, the error message now suggests
using /reset to clear session history, giving users an actionable
recovery path instead of a dead-end error.

Closes #12940

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: suggest /reset in context overflow error message (#12973) (thanks @RamiNoodle733)

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Rami Abdelrazzaq <RamiNoodle733@users.noreply.github.com>
2026-02-09 20:44:37 -06:00
Liu Yuan
33ee8bbf1d feat: add zai/glm-4.6v image understanding support (#10267)
Fixes #10265. Thanks @liuy.
2026-02-09 18:38:09 -08:00
Yida-Dev
d3c71875e4 fix: cap Discord gateway reconnect at 50 attempts to prevent infinite loop (#12230)
* fix: cap Discord gateway reconnect attempts to prevent infinite loop

The Discord GatewayPlugin was configured with maxAttempts: Infinity,
which causes an unbounded reconnection loop when the Discord gateway
enters a persistent failure state (e.g. code 1005 with stalled HELLO).

In production, this manifested as 2,483+ reconnection attempts in a
single log file, starving the Node.js event loop and preventing cron,
heartbeat, and other subsystems from functioning.

Cap maxAttempts at 50, which provides ~25 minutes of retry time
(with 30s HELLO timeout between attempts) before cleanly exiting
via the existing "Max reconnect attempts" error handler.

Closes #11836

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Changelog: note Discord gateway reconnect cap (#12230) (thanks @Yida-Dev)

---------

Co-authored-by: Yida-Dev <reyifeijun@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Shadow <shadow@clawd.bot>
2026-02-09 20:36:43 -06:00
max
67d3bab890 docs: fix broken links checker and add CI docs (#13041)
- Fix zh-CN/vps.md broken links (/railway  /install/railway)
- Add docs/ci.md explaining CI pipeline
- Add Experiments group to docs.json navigation
2026-02-09 18:30:05 -08:00
magendary
ead3bb645f discord: auto-create thread when sending to Forum/Media channels (#12380)
* discord: auto-create thread when sending to Forum/Media channels

* Discord: harden forum thread sends (#12380) (thanks @magendary)

* fix: clean up discord send exports (#12380) (thanks @magendary)

---------

Co-authored-by: Shadow <shadow@clawd.bot>
2026-02-09 20:26:42 -06:00
quotentiroler
6d26ba3bb6 only check is check-docs when only docs changed 2026-02-09 18:05:13 -08:00
quotentiroler
59a4aaf376 Merge branch 'main' of https://github.com/openclaw/openclaw 2026-02-09 17:57:28 -08:00
quotentiroler
039aaf176e CI: cleanup and fix broken job references
- Fix code-size -> code-analysis job name (5 jobs had wrong dependency)
- Remove useless install-check job (was no-op)
- Add explicit docs_only guard to release-check
- Remove dead submodule checkout steps (no submodules in repo)
- Rename detect-docs-only -> detect-docs-changes, add docs_changed output
- Reorder check script: format first for faster fail
- Fix billing error test (PR #12946 removed fallback detection but not test)
2026-02-09 17:52:51 -08:00
Tak Hoffman
54315aeacf Agents: scope sanitizeUserFacingText rewrites to errorContext
Squash-merge #12988.

Refs: #12889 #12309 #3594 #7483 #10094 #10368 #11317 #11359 #11649 #12022 #12432 #12676 #12711
2026-02-09 19:52:24 -06:00
quotentiroler
64cf50dfc3 chore: rename format scripts for conventional naming
- format = fix (write)

- format:check = check only

- Update CI to use format:check
2026-02-09 17:11:16 -08:00
Shadow
8e607d927c Docs: require labeler + label updates for channels/extensions 2026-02-09 17:08:18 -08:00
max
8d75a496bf refactor: centralize isPlainObject, isRecord, isErrno, isLoopbackHost utilities (#12926) 2026-02-09 17:02:55 -08:00
Shadow
70f9edeec7 CI: check maintainer team membership for labels 2026-02-09 18:59:41 -06:00
Jabez Borja
8c73dbe705 fix(telegram): prevent false-positive billing error detection in conversation text (#12946) thanks @jabezborja 2026-02-09 19:49:31 -05:00
cpojer
49fb8f74e4 chore: Fix types after ChatType changes. 2026-02-10 09:20:39 +09:00
Yifeng Wang
5c2cb6c591 feat(feishu): sync community contributions from clawdbot-feishu (#12662)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-10 09:19:44 +09:00
peetzweg/
49c60e9065 feat(matrix): add thread session isolation (#8241)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-10 09:16:40 +09:00
George Pickett
a97db0c372 docs: add changelog entry for #9564 (#12963) 2026-02-09 16:11:52 -08:00
George Pickett
afec0f11f8 test: lock /think off persistence (#9564) 2026-02-09 16:08:15 -08:00
Liu Yuan
97b3ee7ec0 Fix: Honor /think off for reasoning-capable models
Problem:
When users execute `/think off`, they still receive `reasoning_content`
from models configured with `reasoning: true` (e.g., GLM-4.7, GLM-4.6,
Kimi K2.5, MiniMax-M2.1).

Expected: `/think off` should completely disable reasoning content.
Actual: Reasoning content is still returned.

Root Cause:
The directive handlers delete `sessionEntry.thinkingLevel` when user
executes `/think off`. This causes the thinking level to become undefined,
and the system falls back to `resolveThinkingDefault()`, which checks the
model catalog and returns "low" for reasoning-capable models, ignoring the
user's explicit intent.

Why We Must Persist "off" (Design Rationale):

1. **Model-dependent defaults**: Unlike other directives where "off" means
   use a global default, `thinkingLevel` has model-dependent defaults:
   - Reasoning-capable models (GLM-4.7, etc.) → default "low"
   - Other models → default "off"

2. **Existing pattern**: The codebase already follows this pattern for
   `elevatedLevel`, which persists "off" explicitly to override defaults
   that may be "on". The comment explains:
   "Persist 'off' explicitly so `/elevated off` actually overrides defaults."

3. **User intent**: When a user explicitly executes `/think off`, they want
   to disable thinking regardless of the model's capabilities. Deleting the
   field breaks this intent by falling back to the model's default.

Solution:
Persist "off" value instead of deleting the field in all internal directive handlers:
- `src/auto-reply/reply/directive-handling.impl.ts`: Directive-only messages
- `src/auto-reply/reply/directive-handling.persist.ts`: Inline directives
- `src/commands/agent.ts`: CLI command-line flags

Gateway API Backward Compatibility:
The original implementation incorrectly mapped `null` to "off" in
`sessions-patch.ts` for consistency with internal handlers. This was a
breaking change because:
- Previously, `null` cleared the override (deleted the field)
- API clients lost the ability to "clear to default" via `null`
- This contradicts standard JSON semantics where `null` means "no value"

Restored original null semantics in `src/gateway/sessions-patch.ts`:
- `null` → delete field, fall back to model default (clear override)
- `"off"` → persist explicit override
- Other values → normalize and persist

This ensures backward compatibility for API clients while fixing the `/think off`
issue in internal handlers.

Signed-off-by: Liu Yuan <namei.unix@gmail.com>
2026-02-09 16:08:15 -08:00
cpojer
fa21050af0 chore: Update deps. 2026-02-10 08:52:07 +09:00
Riccardo Giorato
661279cbfa feat: adding support for Together ai provider (#10304) 2026-02-10 08:49:34 +09:00
quotentiroler
ffeed212dc ci(docker): use registry cache for persistent layer storage 2026-02-09 15:05:37 -08:00
Tak Hoffman
4df252d895 Gateway: add CLAUDE.md symlink for AGENTS.md 2026-02-09 17:02:55 -06:00
Tak Hoffman
2f9014c6ff AGENTS: require CLAUDE.md symlink alongside new AGENTS.md 2026-02-09 17:02:55 -06:00
Rodrigo Uroz
ae99e656af (fix): .env vars not available during runtime config reloads (healthchecks fail with MissingEnvVarError) (#12748)
* Config: reload dotenv before env substitution on runtime loads

* Test: isolate config env var regression from host state env

* fix: keep dotenv vars resolvable on runtime config reloads (#12748) (thanks @rodrigouroz)

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-09 16:31:41 -06:00
quotentiroler
b40a7771e5 ci: imprpove warning for size check 2026-02-09 14:30:36 -08:00
quotentiroler
a172ff9ed2 docs: SEO and AI discoverability improvements
- Add description to docs.json for llms.txt blockquote summary
- Add title frontmatter to 10 docs files for llms.txt link text
- ci(docker): skip builds for docs-only changes
2026-02-09 14:20:56 -08:00
quotentiroler
e4a04f32e3 docs: add ci.md to Contributing navigation 2026-02-09 14:01:28 -08:00
Sk Akram
1cee5135e4 fix: preserve original filename for WhatsApp inbound documents (#12691)
* fix: preserve original filename for WhatsApp inbound documents

* fix: cover WhatsApp document filenames (#12691) (thanks @akramcodez)

* test: streamline inbound media waits (#12691) (thanks @akramcodez)

---------

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-09 16:56:19 -05:00
quotentiroler
1074d13e4e Improve flagging in code analyzer 2026-02-09 13:41:36 -08:00
quotentiroler
1fad19008e fix: improve code-size gate output and duplicate detection, fix Windows path in source-display 2026-02-09 13:18:51 -08:00
max
65dae9a088 ci: add SwiftPM cache, fix Mintlify frontmatter (#12863)
* ci: add SwiftPM cache to macOS job, fix action description

* ci: fix frontmatter, remove DerivedData cache
2026-02-09 12:40:58 -08:00
quotentiroler
0b7e561434 ci: split format/lint into tiered gates with shared setup action 2026-02-09 12:24:11 -08:00
quotentiroler
dd25b96d0b ci: make code-size depend on checks-lint 2026-02-09 12:14:57 -08:00
quotentiroler
715e8b5440 ci: lint/format failures also block heavy jobs 2026-02-09 11:54:37 -08:00
quotentiroler
57a598c013 feat(ci): code-size gates heavy jobs, re-enable --strict 2026-02-09 11:53:29 -08:00
quotentiroler
de8eb2b29c feat(ci): also flag already-large files that grew larger 2026-02-09 11:51:51 -08:00
max
50b3d32d3c CI: add code-size check for files crossing LOC threshold (#12810)
* CI: add code-size check for files crossing LOC threshold

* feat(ci): add duplicate function detection to CI code-size check

The --compare-to mode now also detects new duplicate function names
introduced by a PR. Uses git diff to scope checks to changed files
only, keeping CI fast.

* fix(ci): address review feedback for code-size check

- Validate git ref upfront; exit 2 if ref doesn't exist
- Distinguish 'file missing at ref' from genuine git errors
- Explicitly fetch base branch ref in CI workflow
- Raise threshold from 700 to 1000 lines

* fix(ci): exclude Swabble, skills, .pi from code analysis

* update gitignore for pycache

* ci: make code-size check informational (no failure on violations)
2026-02-09 11:34:18 -08:00
Peter Steinberger
268094938b fix: reduce brew noise in onboarding 2026-02-09 13:27:21 -06:00
Peter Steinberger
9a765c9fb4 chore(mac): update appcast for 2026.2.9 2026-02-09 13:23:44 -06:00
max
ce71c7326c chore: add tsconfig.test.json for type-checking test files (#12828)
Adds a separate tsconfig that includes only *.test.ts files (which the main
tsconfig excludes). Available via 'pnpm tsgo:test' for incremental cleanup.

Not yet wired into 'pnpm check'  there are ~2.8k pre-existing errors in
test files that need to be fixed incrementally first.
2026-02-09 11:16:19 -08:00
Seb Slight
ec55583bb7 fix: align extension tests and fetch typing for gate stability (#12816) 2026-02-09 11:12:07 -08:00
Peter Steinberger
3e63b2a4fa fix(cli): improve plugins list source display 2026-02-09 13:05:48 -06:00
Peter Steinberger
33c75cb6bf chore(extensions): mark bundled packages private 2026-02-09 12:59:06 -06:00
Peter Steinberger
394d60c1fb fix(onboarding): auto-install shell completion in QuickStart 2026-02-09 12:56:12 -06:00
Chase Dorsey
512b2053c5 fix(web_search): Fix invalid model name sent to Perplexity (#12795)
* fix(web_search): Fix invalid model name sent to Perplexity

* chore: Only apply fix to direct Perplexity calls

* fix(web_search): normalize direct Perplexity model IDs

* fix: add changelog note for perplexity model normalization (#12795) (thanks @cdorsey)

* fix: align tests and fetch type for gate stability (#12795) (thanks @cdorsey)

* chore: keep #12795 scoped to web_search changes

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-09 13:43:57 -05:00
max
40b11db80e TypeScript: add extensions to tsconfig and fix type errors (#12781)
* TypeScript: add extensions to tsconfig and fix type errors

- Add extensions/**/* to tsconfig.json includes
- Export ProviderAuthResult, AnyAgentTool from plugin-sdk
- Fix optional chaining for messageActions across channels
- Add missing type imports (MSTeamsConfig, GroupPolicy, etc.)
- Add type annotations for provider auth handlers
- Fix undici/fetch type compatibility in zalo proxy
- Correct ChannelAccountSnapshot property usage
- Add type casts for tool registrations
- Extract usage view styles and types to separate files

* TypeScript: fix optional debug calls and handleAction guards
2026-02-09 10:05:38 -08:00
Peter Steinberger
2e4334c32c test(auth): cover key normalization 2026-02-09 11:58:18 -06:00
Peter Steinberger
e3ff844bdc docs(changelog): credit human for #11646 2026-02-09 11:51:39 -06:00
Peter Steinberger
d311152a7d docs(changelog): reorder 2026.2.9 for end users 2026-02-09 11:42:24 -06:00
Ayaan Zaidi
5d4f42016f chore(changelog): note Telegram DM allowFrom sender-id fix (#12779) (thanks @liuxiaopai-ai) 2026-02-09 22:59:47 +05:30
Ayaan Zaidi
a77afe618d fix(telegram): add DM allowFrom regression tests 2026-02-09 22:59:47 +05:30
Mark Liu
29425e27e5 fix(telegram): match DM allowFrom against sender user id
Telegram DM access-control incorrectly used chatId as the allowFrom match key.

For DMs, allowFrom entries are typically Telegram user ids (msg.from.id) and/or @usernames. Using chatId causes legitimate DMs to be rejected or silently dropped even when dmPolicy is open/allowlist.

This change matches allowFrom against the sender's user id when available, falling back to chatId only if msg.from.id is missing.

Tests: existing telegram DM/thread routing tests pass.

Closes #4515
2026-02-09 22:59:47 +05:30
Peter Steinberger
c6e142f22e docs(changelog): add 2026.2.9 auth fix 2026-02-09 11:27:10 -06:00
Peter Steinberger
3626b07bea docs: fix ja-JP dashboard URL link 2026-02-09 11:26:27 -06:00
Peter Steinberger
42a07791c4 fix(auth): strip line breaks from pasted keys 2026-02-09 11:26:27 -06:00
Peter Steinberger
fb8c653f53 chore(release): 2026.2.9 2026-02-09 11:19:07 -06:00
Hudson Rivera
588d7133f5 fix(docs): correct wake command in coding-agent skill (#10516)
The skill documented `openclaw gateway wake --text ... --mode now` which
is not a valid subcommand. The correct command is
`openclaw system event --text ... --mode now`.

Fixes #10515.
2026-02-09 12:18:20 -05:00
Denis Rybnikov
582732391a fix(telegram): avoid nested reply quote misclassification 2026-02-09 22:43:29 +05:30
Denis Rybnikov
b430998c2f fix(telegram): clean tsgo/format regressions 2026-02-09 22:43:29 +05:30
Denis Rybnikov
1c1d7fa0e5 fix(telegram): make quote parsing/types CI-safe 2026-02-09 22:43:29 +05:30
Denis Rybnikov
a4b38ce886 fix(telegram): preserve inbound quote context and avoid QUOTE_TEXT_INVALID 2026-02-09 22:43:29 +05:30
Ayaan Zaidi
727a390d13 fix: add telegram command-cap regression test (#12356) (thanks @arosstale) 2026-02-09 22:27:03 +05:30
Claude Code
a656dcc199 fix(telegram): truncate commands to 100 to avoid BOT_COMMANDS_TOO_MUCH
Telegram Bot API limits setMyCommands to 100 commands per scope. When
users have many skills installed (~15+), the combined native + plugin +
custom commands can exceed this limit, causing a 400 error on every
gateway restart.

Truncate the command list to 100 (native commands first, then plugins,
then custom) and log a warning instead of failing the registration.

Fixes #11567
2026-02-09 22:27:03 +05:30
Seb Slight
0768fc65d2 docs(subagents): simplify page and verify behavior/examples (#12761)
* docs(subagents): rewrite page for clarity with examples and Mintlify components

- Add Quick Start section with natural language usage examples
- Add step-by-step How It Works using <Steps> component
- Break configuration into focused subsections with code examples
- Add proper parameters table for sessions_spawn tool
- Document model resolution order (verified against codebase)
- Add interactive /subagents command examples in <AccordionGroup>
- Fix inaccurate tool deny list: document all 11 denied tools (was 4)
- Use <Tip>, <Note>, <Warning>, <Accordion> components throughout
- Add cross-agent spawning config example
- Add full configuration example in collapsible accordion
- Add See Also links to related pages
- All information preserved or verified against codebase

* docs(subagents): correct behavior and config defaults

- Fix model/thinking defaults to match runtime behavior
- Clarify model and thinking resolution order for sessions_spawn
- Remove incorrect claim that announce runs in child session
- Replace ANNOUNCE_SKIP note with NO_REPLY behavior
- Align announce status wording with runtime outcomes

* docs(subagents): clarify NO_REPLY vs ANNOUNCE_SKIP (#12761) (thanks @sebslight)
2026-02-09 11:50:53 -05:00
Xu Haoran
0efaf5aa82 chore: Update .gitignore (#12427)
Added:
.tsbuildinfo - TypeScript build info cache
Android build artifacts:
apps/android/.gradle/
apps/android/app/build/
apps/android/.cxx/

Removed (duplicates):
Duplicate .env entry
Duplicate apps/ios/fastlane/report.xml
2026-02-09 10:25:58 -06:00
Seb Slight
6397e53f3a Delete README-header.png (warelay) 2026-02-09 10:32:35 -05:00
Suvin Nimnaka
24e9b23c4a Replace text diagrams with mermaid (#7165)
* Replace text diagrams with mermaid

* Fix review comments

* Remove newlines

* docs: fix mermaid prep blockers (#7165)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-09 10:27:27 -05:00
Victor Castell
9f4466c116 Simplify ownership commands in hetzner.md (#12703)
* Simplify ownership commands in hetzner.md

Removed redundant chown command for workspace directory.

* Add --allow-unconfigured option to Hetzner config

Container won't start unless allow-unconfigured is set

* docs: clarify hetzner bootstrap caveat (#12703) (thanks @vcastellm)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-09 10:02:54 -05:00
Seb Slight
9050a94a0f docs(skills): allow docs-only prep to skip pnpm test (#12718) 2026-02-09 09:55:46 -05:00
max
79c2466662 refactor: consolidate throwIfAborted + fix isCompactionFailureError (#12463)
* refactor: consolidate throwIfAborted in outbound module

- Create abort.ts with shared throwIfAborted helper

- Update deliver.ts, message-action-runner.ts, outbound-send-service.ts

* fix: handle context overflow in isCompactionFailureError without requiring colon
2026-02-09 00:32:57 -08:00
max
f0924d3c4e refactor: consolidate PNG encoder and safeParseJson utilities (#12457)
- Create shared PNG encoder module (src/media/png-encode.ts)

- Refactor qr-image.ts and live-image-probe.ts to use shared encoder

- Add safeParseJson to utils.ts and plugin-sdk exports

- Update msteams and pairing-store to use centralized safeParseJson
2026-02-09 00:21:54 -08:00
Mariano
5acb1e3c52 Tests: trim timezone in envelope timestamp helper (#12446) 2026-02-09 09:04:54 +01:00
max
ec910a235e refactor: consolidate duplicate utility functions (#12439)
* refactor: consolidate duplicate utility functions

- Add escapeRegExp to src/utils.ts and remove 10 local duplicates
- Rename bash-tools clampNumber to clampWithDefault (different signature)
- Centralize formatError calls to use formatErrorMessage from infra/errors.ts
- Re-export formatErrorMessage from cli/cli-utils.ts to preserve API

* refactor: consolidate remaining escapeRegExp duplicates

* refactor: consolidate sleep, stripAnsi, and clamp duplicates
2026-02-08 23:59:43 -08:00
Mariano
8968d9a339 Auto-reply: include weekday in envelope timestamps (#12438) 2026-02-09 08:55:50 +01:00
Tyler Yust
e4651d6afa Memory/QMD: reuse default model cache and skip ENOENT warnings (#12114)
* Memory/QMD: symlink default model cache into custom XDG_CACHE_HOME

QmdMemoryManager overrides XDG_CACHE_HOME to isolate the qmd index
per-agent, but this also moves where qmd looks for its ML models
(~2.1GB). Since models are installed at the default location
(~/.cache/qmd/models/), every qmd invocation would attempt to
re-download them from HuggingFace and time out.

Fix: on initialization, symlink ~/.cache/qmd/models/ into the custom
XDG_CACHE_HOME path so the index stays isolated per-agent while the
shared models are reused. The symlink is only created when the default
models directory exists and the target path does not already exist.

Includes tests for the three key scenarios: symlink creation, existing
directory preservation, and graceful skip when no default models exist.

* Memory/QMD: skip model symlink warning on ENOENT

* test: stabilize warning-filter visibility assertion (#12114) (thanks @tyler6204)

* fix: add changelog entry for QMD cache reuse (#12114) (thanks @tyler6204)

* fix: handle plain context-overflow strings in compaction detection (#12114) (thanks @tyler6204)
2026-02-08 23:43:08 -08:00
Stephen Brian King
c984e6d8df fix: prevent false positive context overflow detection in conversation text (#2078) 2026-02-08 23:22:57 -08:00
Oren
71b4be8799 fix: handle 400 status in failover to enable model fallback (#1879) 2026-02-08 23:12:06 -08:00
CLAWDINATOR Bot
5e55a181b7 docs: add changelog entry for Grok web_search 2026-02-09 07:11:33 +00:00
Trevin Chow
5f2ad938aa fix(tools): include provider-specific settings in web search cache key 2026-02-09 07:11:33 +00:00
Trevin Chow
a6cab10976 fix(tools): correct docs URL and pass inlineCitations for Grok 2026-02-09 07:11:33 +00:00
Trevin Chow
139d70e2a9 feat(tools): add Grok (xAI) as web_search provider
Add xAI's Grok as a new web_search provider alongside Brave and Perplexity.
Uses the xAI /v1/responses API with tools: [{type: "web_search"}].

Configuration:
- tools.web.search.provider: "grok"
- tools.web.search.grok.apiKey or XAI_API_KEY env var
- tools.web.search.grok.model (default: grok-4-1-fast)
- tools.web.search.grok.inlineCitations (optional, embeds markdown links)

Returns AI-synthesized answers with citations similar to Perplexity.
2026-02-09 07:11:33 +00:00
Tyler Yust
07375a65d8 fix(cron): recover flat params when LLM omits job wrapper (#12124)
* fix(cron): recover flat params when LLM omits job wrapper (#11310)

Non-frontier models (e.g. Grok) flatten job properties to the top level
alongside `action` instead of nesting them inside the `job` parameter.
The opaque schema (`Type.Object({}, { additionalProperties: true })`)
gives these models no structural hint, so they put name, schedule,
payload, etc. as siblings of action.

Add a flat-params recovery step in the cron add handler: when
`params.job` is missing or an empty object, scan for recognised job
property names on params and construct a synthetic job object before
passing to `normalizeCronJobCreate`. Recovery requires at least one
meaningful signal field (schedule, payload, message, or text) to avoid
false positives.

Added tests:
- Flat params with no job wrapper → recovered
- Empty job object + flat params → recovered
- Message shorthand at top level → inferred as agentTurn
- No meaningful fields → still throws 'job required'
- Non-empty job takes precedence over flat params

* fix(cron): floor nowMs to second boundary before croner lookback

Cron expressions operate at second granularity. When nowMs falls
mid-second (e.g. 12:00:00.500) and the pattern targets that exact
second (like '0 0 12 * * *'), a 1ms lookback still lands inside the
matching second.  Croner interprets this as 'already past' and skips
to the next occurrence (e.g. the following day).

Fix: floor nowMs to the start of the current second before applying
the 1ms lookback.  This ensures the reference always falls in the
*previous* second, so croner correctly identifies the current match.

Also compare the result against the floored nowSecondMs (not raw nowMs)
so that a match at the start of the current second is not rejected by
the >= guard when nowMs has sub-second offset.

Adds regression tests for 6-field cron patterns with specific seconds.

* fix: add changelog entries for cron fixes (#12124) (thanks @tyler6204)

* test: stabilize warning filter emit assertion (#12124) (thanks @tyler6204)
2026-02-08 23:10:09 -08:00
clawdinator[bot]
fb8e4489a3 feat: Implement Telegram video note support with tests and docs (#12408)
* feat: Implement Telegram video note support with tests and docs

* fixing lint

* feat: add doctor-state-integrity command, Telegram messaging, and PowerShell Docker setup scripts.

* Update src/telegram/send.video-note.test.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix: Set video note follow-up text to undefined for empty input and adjust caption test expectation.

* test: add assertion for `sendMessage` with reply markup and HTML parse mode in `send.video-note` test.

* docs: add changelog entry for Telegram video notes

---------

Co-authored-by: Evgenii Utkin <thewulf7@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: CLAWDINATOR Bot <clawdinator[bot]@users.noreply.github.com>
2026-02-09 07:00:57 +00:00
clawdinator[bot]
6ed255319f fix(skills): ignore Python venvs and caches in skills watcher (#12399)
* fix(skills): ignore Python venvs and caches in skills watcher

Add .venv, venv, __pycache__, .mypy_cache, .pytest_cache, build, and
.cache to the default ignored patterns for the skills watcher.

This prevents file descriptor exhaustion when a skill contains a Python
virtual environment with tens of thousands of files, which was causing
EBADF spawn errors on macOS.

Fixes #1056

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: add changelog entry for skills watcher ignores

* docs: fill changelog PR number

---------

Co-authored-by: Kyle Howells <freerunnering@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: CLAWDINATOR Bot <clawdinator[bot]@users.noreply.github.com>
2026-02-09 06:41:53 +00:00
juanpablodlc
8d96955e19 fix(routing): make bindings dynamic by calling loadConfig() per-message (#11372) 2026-02-09 00:34:55 -06:00
Tak Hoffman
0cf93b8fa7 Gateway: fix post-compaction amnesia for injected messages (#12283)
* Gateway: preserve Pi transcript parentId for injected messages

Thread: unknown
When: 2026-02-08 20:08 CST
Repo: https://github.com/openclaw/openclaw.git
Branch: codex/wip/2026-02-09/compact-post-compaction-parentid-fix

Problem
- Post-compaction turns sometimes lost the compaction summary + kept suffix in the *next* provider request.
- Root cause was session graph corruption: gateway appended "stopReason: injected" transcript lines via raw JSONL writes without `parentId`.
- Pi's `SessionManager.buildSessionContext()` walks the `parentId` chain from the current leaf; missing `parentId` can sever the active branch and hide the compaction entry.

Fix
- Use `SessionManager.appendMessage(...)` for injected assistant transcript writes so `parentId` is set to the current leaf.
- Route `chat.inject` through the same helper to avoid duplicating the broken raw JSONL append logic.

Why This Matters
- The compaction algorithm may be correct, but if the leaf chain is broken right after compaction, the provider payload cannot include the summary/suffix "shape" Pi expects.

Testing
- pnpm test src/agents/pi-embedded-helpers.post-compaction-shape.test.ts src/agents/pi-embedded-runner/run.overflow-compaction.post-context.test.ts
- pnpm build

Notes
- This is provider-shape agnostic: it fixes transcript structure so Anthropic/Gemini/etc all see the same post-compaction context.

Resume
- If post-compaction looks wrong again, inspect the session transcript for entries missing `parentId` immediately after `type: compaction`.

* Gateway: guardrail test for transcript parentId (chat.inject)

* Gateway: guardrail against raw transcript appends (chat.ts)

* Gateway: add local AGENTS.md note to preserve Pi transcript parentId chain

* Changelog: note gateway post-compaction amnesia fix

* Gateway: store injected transcript messages with valid stopReason

* Gateway: use valid stopReason in injected fallback
2026-02-08 23:07:31 -06:00
Ayaan Zaidi
d85f0566a9 fix: tighten thread-clear and telegram retry guards 2026-02-09 08:59:21 +05:30
Ayaan Zaidi
d7bd68ff24 fix: recover telegram sends from stale thread ids 2026-02-09 08:59:21 +05:30
Patrick Shao
5ac1be9cb6 fix: all bundled hooks broken since 2026.2.2 (tsdown migration) (#9295)
* fix: compile bundled hook handlers in tsdown build

The migration from tsc to tsdown in 2026.2.2 dropped bundled hook handlers
from the build output. The copy-hook-metadata.ts script only copies HOOK.md
metadata files, not the handler.ts source files. Without corresponding tsdown
entry points, the handlers were never compiled to JS, causing
`openclaw hooks list` to show 0 hooks on npm installs.

This adds each bundled hook handler and the llm-slug-generator (dynamically
imported by session-memory) as tsdown entry points:

  - src/hooks/bundled/session-memory/handler.ts
  - src/hooks/bundled/command-logger/handler.ts
  - src/hooks/bundled/boot-md/handler.ts
  - src/hooks/bundled/soul-evil/handler.ts
  - src/hooks/llm-slug-generator.ts

Regression introduced in 2026.2.2; versions 2026.1.29–2026.2.1 worked
correctly under the previous tsc build.

* refactor: use glob for bundled hook entries, fix dist output paths

- Replace hardcoded entry list with glob pattern in tsdown.config.ts
  so new hooks are auto-discovered (matching scripts/copy-hook-metadata.ts)
- Remove inconsistent comment block from tsdown.config.ts
- Fix copy-hook-metadata.ts to copy HOOK.md to dist/bundled/ (matching
  the runtime resolution in bundled-dir.ts which resolves path.join(moduleDir, 'bundled')
  relative to the chunk in dist/)
- Update stale path comment in session-memory handler
2026-02-09 11:35:47 +09:00
Josh Palmer
69aa3df116 macOS: honor stable Nix defaults suite (#12205)
* macOS: honor Nix defaults suite; auto launch in Nix mode

Fixes repeated onboarding in Nix deployments by detecting nixMode from the stable defaults suite (ai.openclaw.mac) and bridging key settings into the current defaults domain.

Also enables LaunchAgent autostart by default in Nix mode (escape hatch: openclaw.nixAutoLaunchAtLogin=false).

* macOS: keep Nix mode fix focused

Drop the automatic launch-at-login behavior from the Nix defaults patch; keep this PR scoped to reliable nixMode detection + defaults bridging.

* macOS: simplify nixMode fix

Remove the defaults-bridging helper and rely on a single, stable defaults suite (ai.openclaw.mac) for nixMode detection when running as an app bundle. This keeps the fix focused on onboarding suppression and rename churn resilience.

* macOS: fix nixMode defaults suite churn (#12205)
2026-02-08 17:28:22 -08:00
max
53a1ac36f5 test: normalize paths in OPENCLAW_HOME tests for cross-platform support (#12212)
* test: normalize paths in OPENCLAW_HOME tests for cross-platform support

* test: normalize paths in Nix integration tests for cross-platform support

* test: remove unnecessary Windows skip from pi-embedded-runner test

* test: fix nix integration tests for path.resolve behavior
2026-02-08 17:21:31 -08:00
Marcus Castro
456bd58740 fix(paths): structurally resolve home dir to prevent Windows path bugs (#12125)
* fix(paths): structurally resolve home dir to prevent Windows path bugs

Extract resolveRawHomeDir as a private function and gate the public
resolveEffectiveHomeDir through a single path.resolve() exit point.
This makes it structurally impossible for unresolved paths (missing
drive letter on Windows) to escape the function, regardless of how
many return paths exist in the raw lookup logic.

Simplify resolveRequiredHomeDir to only resolve the process.cwd()
fallback, since resolveEffectiveHomeDir now returns resolved values.

Fix shortenMeta in tool-meta.ts: the colon-based split for file:line
patterns (e.g. file.txt:12) conflicts with Windows drive letters
(C:\...) because indexOf(":") matches the drive colon first.
shortenHomeInString already handles file:line patterns correctly via
split/join, so the colon split was both unnecessary and harmful.

Update test assertions across all affected files to use path.resolve()
in expected values and input strings so they match the now-correct
resolved output on both Unix and Windows.

Fixes #12119

* fix(changelog): add paths Windows fix entry (#12125)

---------

Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-08 20:06:29 -05:00
cpojer
0244d521a1 chore: Fix lockfile + pnpm dedupe. 2026-02-09 10:00:53 +09:00
cpojer
6614c3f932 chore: Fix lint. 2026-02-09 09:58:58 +09:00
cpojer
0497bb0544 chore: Fix failing test. 2026-02-09 09:58:58 +09:00
cpojer
3573f26d40 chore: Update deps. 2026-02-09 09:58:58 +09:00
max
f4fc65d234 test: skip unix-path OPENCLAW_HOME tests on Windows (#12206) 2026-02-09 09:56:21 +09:00
max
223eee0a20 refactor: unify peer kind to ChatType, rename dm to direct (#11881)
* fix: use .js extension for ESM imports of RoutePeerKind

The imports incorrectly used .ts extension which doesn't resolve
with moduleResolution: NodeNext. Changed to .js and added 'type'
import modifier.

* fix tsconfig

* refactor: unify peer kind to ChatType, rename dm to direct

- Replace RoutePeerKind with ChatType throughout codebase
- Change 'dm' literal values to 'direct' in routing/session keys
- Keep backward compat: normalizeChatType accepts 'dm' -> 'direct'
- Add ChatType export to plugin-sdk, deprecate RoutePeerKind
- Update session key parsing to accept both 'dm' and 'direct' markers
- Update all channel monitors and extensions to use ChatType

BREAKING CHANGE: Session keys now use 'direct' instead of 'dm'.
Existing 'dm' keys still work via backward compat layer.

* fix tests

* test: update session key expectations for dmdirect migration

- Fix test expectations to expect :direct: in generated output
- Add explicit backward compat test for normalizeChatType('dm')
- Keep input test data with :dm: keys to verify backward compat

* fix: accept legacy 'dm' in session key parsing for backward compat

getDmHistoryLimitFromSessionKey now accepts both :dm: and :direct:
to ensure old session keys continue to work correctly.

* test: add explicit backward compat tests for dmdirect migration

- session-key.test.ts: verify both :dm: and :direct: keys are valid
- getDmHistoryLimitFromSessionKey: verify both formats work

* feat: backward compat for resetByType.dm config key

* test: skip unix-path Nix tests on Windows
2026-02-09 09:20:52 +09:00
George Pickett
0b07e15b63 chore(changelog): note maxTokens clamp (#5516) (thanks @lailoo) (#12139) 2026-02-08 14:27:11 -08:00
George Pickett
92764a60d6 test(config): cover maxTokens clamping 2026-02-08 14:24:57 -08:00
damaozi
eed580d310 fix(config): clamp maxTokens to contextWindow to prevent invalid configurations
Closes #5308

When users configure maxTokens larger than contextWindow (e.g., maxTokens: 40960
with contextWindow: 32768), the model may fail silently. This fix clamps
maxTokens to be at most contextWindow, preventing such invalid configurations.
2026-02-08 14:24:57 -08:00
Sebastian
41f3e90ea8 changelog: split #12091 entry into Added + Fixes 2026-02-08 16:21:18 -05:00
Seb Slight
db137dd65d fix(paths): respect OPENCLAW_HOME for all internal path resolution (#12091)
* fix(paths): respect OPENCLAW_HOME for all internal path resolution (#11995)

Add home-dir module (src/infra/home-dir.ts) that centralizes home
directory resolution with precedence: OPENCLAW_HOME > HOME > USERPROFILE > os.homedir().

Migrate all path-sensitive callsites: config IO, agent dirs, session
transcripts, pairing store, cron store, doctor, CLI profiles.

Add envHomedir() helper in config/paths.ts to reduce lambda noise.
Document OPENCLAW_HOME in docs/help/environment.md.

* fix(paths): handle OPENCLAW_HOME '~' fallback (#12091) (thanks @sebslight)

* docs: mention OPENCLAW_HOME in install and getting started (#12091) (thanks @sebslight)

* fix(status): show OPENCLAW_HOME in shortened paths (#12091) (thanks @sebslight)

* docs(changelog): clarify OPENCLAW_HOME and HOME precedence (#12091) (thanks @sebslight)
2026-02-08 16:20:13 -05:00
Josh Palmer
c95e6fe6dc Docs: note language switcher ordering + JP flag fix (#12023) (thanks @joshp123) 2026-02-08 10:45:44 -08:00
Josh Palmer
2b4135debc Docs: fix language switcher order + Japanese locale 2026-02-08 10:45:44 -08:00
Josh Palmer
6e3271ebb6 Docs: note ja-JP docs POC in changelog (#11988) (thanks @joshp123) 2026-02-08 10:18:04 -08:00
Josh Palmer
d8dbfc701c Docs: use ja-jp Mintlify language code 2026-02-08 10:18:04 -08:00
Josh Palmer
c4213b89eb Docs: seed ja-JP translations 2026-02-08 10:18:04 -08:00
Josh Palmer
d2ec78607d Docs i18n: make translation prompt language-pluggable 2026-02-08 10:18:04 -08:00
Vignesh Natarajan
7f7d49aef0 Memory/QMD: warn when scope denies search 2026-02-08 09:21:17 -08:00
Mariano Belinky
6aedc54bd7 iOS: alpha node app + setup-code onboarding (#11756) 2026-02-08 18:08:13 +01:00
Mariano Belinky
730f86dd5c Gateway/Plugins: device pairing + phone control plugins (#11755) 2026-02-08 18:07:13 +01:00
Seb Slight
2f91bf550f docs: fix changelog PR reference
Fix Exec approvals command text formatting issue for safer approval scanning.
2026-02-08 10:50:10 -05:00
Seb Slight
ad8b839aa7 Exec approvals: render forwarded commands in monospace (#11937)
* fix(exec-approvals): format forwarded commands as code

* fix(exec-approvals): place fenced command blocks on new line (#11937) (thanks @sebslight)
2026-02-08 10:48:52 -05:00
seans-openclawbot
744892de72 Add GitHub Copilot models to xhigh list (#11646)
* Add GitHub Copilot models to xhigh list

* fix(thinking): add xhigh copilot tests and changelog (#11646) (thanks @seans-openclawbot)

---------

Co-authored-by: Sean Dai <sdai@gatech.edu>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-08 08:45:59 -05:00
max
eb3e9c649b chore: fix vitest standalone configs and update package description (#11865)
* chore: fix vitest standalone configs and update package description

- vitest.live.config.ts and vitest.e2e.config.ts now extend root config
- Inherits testTimeout (120s), resolve.alias, pool, setupFiles, excludes
- ui/vitest.node.config.ts gets explicit 120s timeout
- package.json description updated for multi-channel AI gateway
- Removed unused src/utils/time-format.ts

* chore: filter inherited excludes in live/e2e vitest configs

* refactor: dedupe GroupPolicy/DmPolicy in extensions

Import from openclaw/plugin-sdk instead of re-declaring identical types.
2026-02-08 05:24:50 -08:00
max
a1123dd9be Centralize date/time formatting utilities (#11831) 2026-02-08 04:53:31 -08:00
theonejvo
74fbbda283 docs: add security & trust documentation
Add threat model (MITRE ATLAS), contribution guide, and security
directory README. Update SECURITY.md with trust page reporting
instructions and Jamieson O'Reilly as Security & Trust.

Co-Authored-By: theonejvo <theonejvo@users.noreply.github.com>
2026-02-08 21:53:05 +11:00
max
28e1a65ebc chore: project hygiene — fix workspace:*, sandbox USER, dead config (#11289)
* chore: project hygiene fixes (workspace:*, sandbox USER, dead config)

* chore: also fix workspace:* in zalouser dependencies
2026-02-08 02:36:42 -08:00
Gustavo Madeira Santana
c56fb7f353 chore: suppress warnings for node default output path 2026-02-08 05:32:58 -05:00
Gustavo Madeira Santana
3119057161 chore: centralizing warning filters 2026-02-08 05:18:08 -05:00
Gustavo Madeira Santana
cef9bfce22 CI: scope heavy jobs, build once, and remove duplicate validation work (#11570)
* CI: scope jobs and reuse build artifacts

* CI: fix scope fallback and remove unused artifact job

* CI: remove setup-node pnpm cache inputs

* CI: add pnpm store cache and dist artifact smoke

* CI: extract pnpm cache action and consume dist artifact
2026-02-08 02:08:56 -08:00
Gustavo Madeira Santana
b75d618080 fix(doctor): suppress repeated legacy state migration warnings (#11709)
* fix(doctor): suppress repeated state migration warning

* fix: harden state-dir mirror detection + warnings (#11709) (thanks @gumadeiras)

* test: cover mirror hardening edge cases (#11709) (thanks @gumadeiras)
2026-02-08 02:27:49 -05:00
ezhikkk
e02d144af9 feat(telegram): add spoiler tag support (#11543)
* feat(telegram): add spoiler tag support

Render markdown ||spoiler|| syntax as <tg-spoiler> tags in Telegram HTML output.

The markdown IR already parses spoiler syntax, but the Telegram renderer was
missing the style marker. This adds the spoiler marker to renderTelegramHtml().

Fixes spoiler text appearing as raw ||text|| instead of hidden text.

* fix: enable Telegram spoiler rendering (#11543) (thanks @ezhikkk)

---------

Co-authored-by: Параша <parasha@openclaw.local>
Co-authored-by: Muhammed Mukhthar CM <mukhtharcm@gmail.com>
2026-02-08 11:25:56 +05:30
jarvis89757
9949f82590 fix(discord): support forum channel thread-create (#10062)
* fix(discord): support forum channel thread-create

* fix: harden discord forum thread-create (#10062) (thanks @jarvis89757)

---------

Co-authored-by: Shakker <shakkerdroid@gmail.com>
2026-02-08 05:51:10 +00:00
Tyler Yust
bc475f0172 fix(ui): smooth chat refresh scroll and suppress new-messages badge flash 2026-02-07 20:21:27 -08:00
Tyler Yust
191da1feb5 fix: context overflow compaction and subagent announce improvements (#11664) (thanks @tyler6204)
* initial commit

* feat: implement deriveSessionTotalTokens function and update usage tests

* Added deriveSessionTotalTokens function to calculate total tokens based on usage and context tokens.
* Updated usage tests to include cases for derived session total tokens.
* Refactored session usage calculations in multiple files to utilize the new function for improved accuracy.

* fix: restore overflow truncation fallback + changelog/test hardening (#11551) (thanks @tyler6204)
2026-02-07 20:02:32 -08:00
Tyler Yust
8fae55e8e0 fix(cron): share isolated announce flow + harden cron scheduling/delivery (#11641)
* fix(cron): comprehensive cron scheduling and delivery fixes

- Fix delivery target resolution for isolated agent cron jobs
- Improve schedule parsing and validation
- Add job retry logic and error handling
- Enhance cron ops with better state management
- Add timer improvements for more reliable cron execution
- Add cron event type to protocol schema
- Support cron events in heartbeat runner (skip empty-heartbeat check,
  use dedicated CRON_EVENT_PROMPT for relay)

* fix: remove cron debug test and add changelog/docs notes (#11641) (thanks @tyler6204)
2026-02-07 19:46:01 -08:00
Oleg Kossoy
ebe5730401 fix: use STATE_DIR instead of hardcoded ~/.openclaw for identity and canvas (#4824)
* fix: use STATE_DIR instead of hardcoded ~/.openclaw for identity and canvas

device-identity.ts and canvas-host/server.ts used hardcoded
path.join(os.homedir(), '.openclaw', ...) ignoring OPENCLAW_STATE_DIR
env var and the resolveStateDir() logic from config/paths.ts.

This caused ~/.openclaw/identity and ~/.openclaw/canvas directories
to be created even when state dir was overridden or resided elsewhere.

* fix: format and remove duplicate imports

* fix: scope state-dir patch + add regression tests (#4824) (thanks @kossoy)

* fix: align state-dir fallbacks in hooks and agent paths (#4824) (thanks @kossoy)

---------

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-07 22:16:59 -05:00
大猫子
0499656c59 Docs: fix cron.update param name id → jobId (#11365) (#11467)
* Docs: fix cron.update param name id → jobId (#11365)

* Docs: sync zh-CN cron.update param name id → jobId

* docs: revert manual zh-CN generated docs edit (#11467) (thanks @lailoo)

---------

Co-authored-by: damaozi <1811866786@qq.com>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-07 22:08:41 -05:00
danielcadenhead
05a57e94a4 Fix Nix repository link in README (#7910)
Updated Nix repository link in README.

Co-authored-by: Josh <141778+bolapara@users.noreply.github.com>
Co-authored-by: Seb Slight <19554889+sebslight@users.noreply.github.com>
2026-02-07 21:53:32 -05:00
Gustavo Madeira Santana
c27b03794a chore: updated PR review skills and workflow info on tests + fake timers 2026-02-07 21:47:25 -05:00
Rohan Patil
9866a857a7 docs: clarify onboarding instructions for beginners (#10956) 2026-02-07 21:43:59 -05:00
Gustavo Madeira Santana
e2dea2684f Tests: harden flake hotspots and consolidate provider-auth suites (#11598)
* Tests: harden flake hotspots and consolidate provider-auth suites

* Tests: restore env vars by deleting missing snapshot values

* Tests: use real newline in memory summary filter case

* Tests(memory): use fake timers for qmd timeout coverage

* Changelog: add tests hardening entry for #11598
2026-02-07 21:32:23 -05:00
Tyler Yust
a30c4f45c3 Update CHANGELOG.md for version 2026.2.6-4: Added RPC methods for agent management, fixed context overflow recovery, improved LAN IP handling, enhanced memory retrieval, and updated media understanding for audio transcription. 2026-02-07 18:15:25 -08:00
Vignesh Natarajan
95263f4e60 Memory: add SQLITE_BUSY fallback regression test 2026-02-07 17:55:34 -08:00
Vignesh Natarajan
6f1ba986b3 Memory: make QMD cache eviction callback idempotent 2026-02-07 17:55:34 -08:00
Vignesh Natarajan
c741d008dd Memory: chain forced QMD queue and fail over on busy index 2026-02-07 17:55:34 -08:00
Vignesh Natarajan
0d60ef6fef Memory: queue forced QMD sync and handle sqlite busy reads 2026-02-07 17:55:34 -08:00
Vignesh Natarajan
ce715c4c56 Memory: harden QMD startup, timeouts, and fallback recovery 2026-02-07 17:55:34 -08:00
Tyler Yust
0deb8b0da1 fix: recover from context overflow caused by oversized tool results (#11579)
* fix: gracefully handle oversized tool results causing context overflow

When a subagent reads a very large file or gets a huge tool result (e.g.,
gh pr diff on a massive PR), it can exceed the model's context window in
a single prompt. Auto-compaction can't help because there's no older
history to compact — just one giant tool result.

This adds two layers of defense:

1. Pre-emptive: Hard cap on tool result size (400K chars ≈ 100K tokens)
   applied in the session tool result guard before persistence. This
   prevents extremely large tool results from being stored in full,
   regardless of model context window size.

2. Recovery: When context overflow is detected and compaction fails,
   scan session messages for oversized tool results relative to the
   model's actual context window (30% max share). If found, truncate
   them in the session via branching (creating a new branch with
   truncated content) and retry the prompt.

The truncation preserves the beginning of the content (most useful for
understanding what was read) and appends a notice explaining the
truncation and suggesting offset/limit parameters for targeted reads.

Includes comprehensive tests for:
- Text truncation with newline-boundary awareness
- Context-window-proportional size calculation
- In-memory message truncation
- Oversized detection heuristics
- Guard-level size capping during persistence

* fix: prep fixes for tool result truncation PR (#11579) (thanks @tyler6204)
2026-02-07 17:40:51 -08:00
Aviral
b8c8130efe fix(gateway): use LAN IP for WebSocket/probe URLs when bind=lan (#11448)
* fix(gateway): use LAN IP for WebSocket/probe URLs when bind=lan (#11329)

When gateway.bind=lan, the HTTP server correctly binds to 0.0.0.0
(all interfaces), but WebSocket connection URLs, probe targets, and
Control UI links were hardcoded to 127.0.0.1. This caused CLI commands
and status probes to show localhost-only URLs even in LAN mode, and
made onboarding display misleading connection info.

- Add pickPrimaryLanIPv4() to gateway/net.ts to detect the machine's
  primary LAN IPv4 address (prefers en0/eth0, falls back to any
  external interface)
- Update pickProbeHostForBind() to use LAN IP when bind=lan
- Update buildGatewayConnectionDetails() to use LAN IP and report
  "local lan <ip>" as the URL source
- Update resolveControlUiLinks() to return LAN-accessible URLs
- Update probe note in status.gather.ts to reflect new behavior
- Add tests for pickPrimaryLanIPv4 and bind=lan URL resolution

Closes #11329

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: move vi.restoreAllMocks to afterEach in pickPrimaryLanIPv4

Per review feedback: avoid calling vi.restoreAllMocks() inside
individual tests as it restores all spies globally and can cause
ordering issues. Use afterEach in the describe block instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Changelog: note LAN bind URLs fix (#11448) (thanks @AnonO6)

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-07 19:16:51 -06:00
Tyler Yust
ea423bbbfd feat(sanitize): enhance context overflow error handling in user-facing text
- Added tests to ensure proper sanitization of context overflow errors.
- Introduced a new function to determine when to rewrite context overflow messages.
- Updated the sanitization logic to improve user experience by providing clearer error messages while preserving conversational context.
2026-02-07 17:07:12 -08:00
Advait Paliwal
980f788731 feat(gateway): add agents.create/update/delete methods (#11045)
* feat(gateway): add agents.create/update/delete methods

* fix(lint): preserve memory-lancedb load error cause

* feat(gateway): trash agent files on agents.delete

* chore(protocol): regenerate Swift gateway models

* fix(gateway): stabilize agents.create dirs and agentDir

* feat(gateway): support avatar in agents.create

* fix: prep agents.create/update/delete handlers (#11045) (thanks @advaitpaliwal)

- Reuse movePathToTrash from browser/trash.ts (has ~/.Trash fallback on non-macOS)
- Fix partial-failure: workspace setup now runs before config write
- Always write Name to IDENTITY.md regardless of emoji/avatar
- Add unit tests for agents.create, agents.update, agents.delete
- Add CHANGELOG entry

---------

Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-02-07 16:47:58 -08:00
Tak Hoffman
9271fcb3d4 Gateway: fix multi-agent sessions.usage discovery (#11523)
* Gateway: fix multi-agent sessions.usage discovery

* Gateway: resolve sessions.usage keys via sessionId
2026-02-07 17:40:56 -06:00
succ985
b8f740fb14 fix: add .caf to AUDIO_FILE_EXTENSIONS (#10982)
* fix: add .caf to AUDIO_FILE_EXTENSIONS for iMessage voice messages

* fix: add caf audio extension regression coverage (#10982) (thanks @succ985)

---------

Co-authored-by: succ985 <succ985@users.noreply.github.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-07 18:04:22 -05:00
max
8da20027c4 CI: skip heavy jobs on docs-only changes (#11328) 2026-02-08 07:43:47 +09:00
Abdullah
9201e140cb Fix typo in FAQ regarding model configuration command (#6048) 2026-02-07 15:48:54 -05:00
Gustavo Madeira Santana
ff80646085 chore: bump pi to 0.52.8 2026-02-07 15:41:27 -05:00
Seb Slight
929a3725d3 docs: canonicalize docs paths and align zh navigation (#11428)
* docs(navigation): canonicalize paths and align zh nav

* chore(docs): remove stray .DS_Store

* docs(scripts): add non-mint docs link audit

* docs(nav): fix zh source paths and preserve legacy redirects (#11428) (thanks @sebslight)

* chore(docs): satisfy lint for docs link audit script (#11428) (thanks @sebslight)
2026-02-07 15:40:35 -05:00
Gustavo Madeira Santana
cde29fef71 added more explicit instructions 2026-02-07 15:27:24 -05:00
Gustavo Madeira Santana
6d1daf2ba5 adding PR review workflow 2026-02-07 15:22:08 -05:00
Tak Hoffman
82419eaad6 Web UI: show Compaction divider in chat history (#11341) 2026-02-07 12:37:22 -06:00
Tak Hoffman
f0722498a4 Agents: include runtime shell (#1835)
* Agents: include runtime shell

* Agents: fix compact runtime build

* chore: fix CLAUDE.md formatting, security regex for secret

---------

Co-authored-by: Tak hoffman <takayukihoffman@gmail.com>
Co-authored-by: quotentiroler <max.nussbaumer@maxhealth.tech>
2026-02-07 09:32:31 -08:00
大猫子
a4d5c7f673 docs: add missing HEARTBEAT.md and MEMORY.md to bootstrap files list (#8105)
* docs: add missing HEARTBEAT.md and MEMORY.md to bootstrap files list

Fixes #7928

The documentation for skipBootstrap and workspace setup was missing
HEARTBEAT.md and MEMORY.md from the bootstrap files list.

Changes:
- docs/gateway/configuration.md: Add HEARTBEAT.md and MEMORY.md
- docs/zh-CN/gateway/configuration.md: Same for Chinese version
- docs/start/openclaw.md: Add HEARTBEAT.md, clarify MEMORY.md is optional
- docs/zh-CN/start/openclaw.md: Same for Chinese version

* fix: reference PR number instead of issue in CHANGELOG

* docs(workspace): align bootstrap file docs with runtime (#8105)

---------

Co-authored-by: damaozi <1811866786@qq.com>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-07 10:51:44 -05:00
Seb Slight
9a3f62cb86 docs: add symptom-first troubleshooting hub and deep runbooks (#11196)
* docs(troubleshooting): add symptom-first troubleshooting runbooks

* docs(troubleshooting): fix approvals command examples

* docs(troubleshooting): wrap symptom cases in accordions

* docs(automation): clarify userTimezone missing-key behavior

* docs(troubleshooting): fix first-60-seconds ladder order
2026-02-07 10:28:19 -05:00
Tyler Yust
1007d71f0c fix: comprehensive BlueBubbles and channel cleanup (#11093)
* feat(bluebubbles): auto-strip markdown from outbound messages (#7402)

* fix(security): add timeout to webhook body reading (#6762)

Adds 30-second timeout to readBody() in voice-call, bluebubbles, and nostr
webhook handlers. Prevents Slow-Loris DoS (CWE-400, CVSS 7.5).
Merged with existing maxBytes protection in voice-call.

* fix(security): unify Error objects and lint fixes in webhook timeouts (#6762)

* fix: prevent plugins from auto-enabling without user consent (#3961)

Changes default plugin enabled state from true to false in enablePluginEntry().
Preserves existing enabled:true values. Fixes #3932.

* fix: apply hierarchical mediaMaxMb config to all channels (#8749)

Generalizes resolveAttachmentMaxBytes() to use account → channel → global
config resolution for all channels, not just BlueBubbles. Fixes #7847.

* fix(bluebubbles): sanitize attachment filenames against header injection (#10333)

Strip ", \r, \n, and \\ from filenames after path.basename() to prevent
multipart Content-Disposition header injection (CWE-93, CVSS 5.4).
Also adds sanitization to setGroupIconBlueBubbles which had zero filename
sanitization.

* fix(lint): exclude extensions/ from Oxlint preflight check (#9313)

Extensions use PluginRuntime|null patterns that trigger
no-redundant-type-constituents because PluginRuntime resolves to any.
Excluding extensions/ from Oxlint unblocks user upgrades.
Re-applies the approach from closed PR #10087.

* fix(bluebubbles): add tempGuid to createNewChatWithMessage payload (#7745)

Non-Private-API mode (AppleScript) requires tempGuid in send payloads.
The main sendMessageBlueBubbles already had it, but createNewChatWithMessage
was missing it, causing 400 errors for new chat creation without Private API.

* fix: send stop-typing signal when run ends with NO_REPLY (#8785)

Adds onCleanup callback to the typing controller that fires when the
controller is cleaned up while typing was active (e.g., after NO_REPLY).
Channels using createTypingCallbacks automatically get stop-typing on
cleanup. This prevents the typing indicator from lingering in group chats
when the agent decides not to reply.

* fix(telegram): deduplicate skill commands in multi-agent setup (#5717)

Two fixes:
1. Skip duplicate workspace dirs when listing skill commands across agents.
   Multiple agents sharing the same workspace would produce duplicate commands
   with _2, _3 suffixes.
2. Clear stale commands via deleteMyCommands before registering new ones.
   Commands from deleted skills now get cleaned up on restart.

* fix: add size limits to unbounded in-memory caches (#4948)

Adds max-size caps with oldest-entry eviction to prevent OOM in
long-running deployments:
- BlueBubbles serverInfoCache: 64 entries (already has TTL)
- Google Chat authCache: 32 entries
- Matrix directRoomCache: 1024 entries
- Discord presenceCache: 5000 entries per account

* fix: address review concerns (#11093)

- Chain deleteMyCommands → setMyCommands to prevent race condition (#5717)
- Rename enablePluginEntry to registerPluginEntry (now sets enabled: false)
- Add Slow-Loris timeout test for readJsonBody (#6023)
2026-02-07 05:00:55 -08:00
Peter Steinberger
9f703a44dc chore(release): 2026.2.6-3 2026-02-07 00:44:32 -08:00
Peter Steinberger
85ed6c7fa4 chore(onboard): reorder xAI + Qianfan providers 2026-02-07 00:43:13 -08:00
Peter Steinberger
ad4dd0422e chore(release): 2026.2.6-2 2026-02-07 00:30:43 -08:00
Peter Steinberger
4ba9809f18 test(hooks): stabilize session-memory hook tests 2026-02-07 00:22:34 -08:00
Peter Steinberger
80d42eb0ba fix(docker): support .mjs entrypoints in images and e2e 2026-02-07 00:22:34 -08:00
Peter Steinberger
2b6cf03b47 fix(build): support daemon-cli .mjs bundles in compat shim 2026-02-07 00:22:34 -08:00
Peter Steinberger
88ffad1c4f Merge PR #8868: add Baidu Qianfan support (thanks @ide-rea) 2026-02-07 00:19:04 -08:00
Peter Steinberger
875324e7c7 docs(changelog): note CI pipeline optimization (#10784) (thanks @mcaxtr) 2026-02-06 23:31:48 -08:00
Marcus Castro
2d7428a7f2 ci: re-enable parallel vitest on Windows CI 2026-02-06 23:31:48 -08:00
Marcus Castro
47596257ea ci: add concurrency controls, consolidate macOS jobs, optimize Windows CI 2026-02-06 23:31:48 -08:00
Peter Steinberger
ab3045cb48 chore(onboard): move xAI below Google 2026-02-06 23:06:55 -08:00
Peter Steinberger
aaddbdae52 chore(release): 2026.2.6-1 2026-02-06 22:48:19 -08:00
Peter Steinberger
8d0e7997c8 chore(onboard): move xAI up in auth list 2026-02-06 22:41:19 -08:00
Peter Steinberger
31a7e4f937 chore(skills): remove bird skill 2026-02-06 22:28:44 -08:00
Peter Steinberger
c5194d8148 fix(dashboard): restore tokenized control ui links 2026-02-06 22:17:09 -08:00
ide-rea
43c0a7fe1c Merge branch 'openclaw:main' into qianfan 2026-02-07 14:07:52 +08:00
Jake
e78ae48e69 fix(memory): add input_type to Voyage AI embeddings for improved retrieval (#10818)
* fix(memory): add input_type to Voyage AI embeddings for improved retrieval

Voyage AI recommends passing input_type='document' when indexing and
input_type='query' when searching. This improves retrieval quality by
optimising the embedding space for each direction.

Changes:
- embedQuery now passes input_type: 'query'
- embedBatch now passes input_type: 'document'
- Batch API request_params includes input_type: 'document'
- Tests updated to verify input_type is passed correctly

* Changelog: note Voyage embeddings input_type fix (#10818) (thanks @mcinteerj)

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-06 21:55:09 -06:00
Markus Buhatem Koch
4c1da23a71 Revert previous change from 'Clawdbot' to 'OpenClaw' in lore (#9119) 2026-02-06 21:53:02 -05:00
Val Alexander
3d2fe9284e Fix repository links in formal-verification.md (#10200)
Updated repository links for formal verification models.
2026-02-06 21:47:55 -05:00
Peter Steinberger
a3b5f1b15c fix(build): unblock pnpm build dts 2026-02-06 18:43:11 -08:00
DEOKLYONG MOON
d1dc60774b Docs: fix broken /plugins links (#9308)
* Docs: fix broken /plugins links to /plugin

The documentation linked to /plugins which doesn't exist.
The correct path is /plugin (singular) which contains the
plugins overview documentation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs: drop manual zh-CN doc edits from plugins link fix

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Sebastian <19554889+sebslight@users.noreply.github.com>
2026-02-06 21:08:26 -05:00
Tyler Yust
d90cac990c fix: cron scheduler reliability, store hardening, and UX improvements (#10776)
* refactor: update cron job wake mode and run mode handling

- Changed default wake mode from 'next-heartbeat' to 'now' in CronJobEditor and related CLI commands.
- Updated cron-tool tests to reflect changes in run mode, introducing 'due' and 'force' options.
- Enhanced cron-tool logic to handle new run modes and ensure compatibility with existing job structures.
- Added new tests for delivery plan consistency and job execution behavior under various conditions.
- Improved normalization functions to handle wake mode and session target casing.

This refactor aims to streamline cron job configurations and enhance the overall user experience with clearer defaults and improved functionality.

* test: enhance cron job functionality and UI

- Added tests to ensure the isolated agent correctly announces the final payload text when delivering messages via Telegram.
- Implemented a new function to pick the last deliverable payload from a list of delivery payloads.
- Enhanced the cron service to maintain legacy "every" jobs while minute cron jobs recompute schedules.
- Updated the cron store migration tests to verify the addition of anchorMs to legacy every schedules.
- Improved the UI for displaying cron job details, including job state and delivery information, with new styles and layout adjustments.

These changes aim to improve the reliability and user experience of the cron job system.

* test: enhance sessions thinking level handling

- Added tests to verify that the correct thinking levels are applied during session spawning.
- Updated the sessions-spawn-tool to include a new parameter for overriding thinking levels.
- Enhanced the UI to support additional thinking levels, including "xhigh" and "full", and improved the handling of current options in dropdowns.

These changes aim to improve the flexibility and accuracy of thinking level configurations in session management.

* feat: enhance session management and cron job functionality

- Introduced passthrough arguments in the test-parallel script to allow for flexible command-line options.
- Updated session handling to hide cron run alias session keys from the sessions list, improving clarity.
- Enhanced the cron service to accurately record job start times and durations, ensuring better tracking of job execution.
- Added tests to verify the correct behavior of the cron service under various conditions, including zero-delay timers.

These changes aim to improve the usability and reliability of session and cron job management.

* feat: implement job running state checks in cron service

- Added functionality to prevent manual job runs if a job is already in progress, enhancing job management.
- Updated the `isJobDue` function to include checks for running jobs, ensuring accurate scheduling.
- Enhanced the `run` function to return a specific reason when a job is already running.
- Introduced a new test case to verify the behavior of forced manual runs during active job execution.

These changes aim to improve the reliability and clarity of cron job execution and management.

* feat: add session ID and key to CronRunLogEntry model

- Introduced `sessionid` and `sessionkey` properties to the `CronRunLogEntry` struct for enhanced tracking of session-related information.
- Updated the initializer and Codable conformance to accommodate the new properties, ensuring proper serialization and deserialization.

These changes aim to improve the granularity of logging and session management within the cron job system.

* fix: improve session display name resolution

- Updated the `resolveSessionDisplayName` function to ensure that both label and displayName are trimmed and default to an empty string if not present.
- Enhanced the logic to prevent returning the key if it matches the label or displayName, improving clarity in session naming.

These changes aim to enhance the accuracy and usability of session display names in the UI.

* perf: skip cron store persist when idle timer tick produces no changes

recomputeNextRuns now returns a boolean indicating whether any job
state was mutated. The idle path in onTimer only persists when the
return value is true, eliminating unnecessary file writes every 60s
for far-future or idle schedules.

* fix: prep for merge - explicit delivery mode migration, docs + changelog (#10776) (thanks @tyler6204)
2026-02-06 18:03:03 -08:00
Peter Steinberger
0dd7033521 chore(lockfile): fix pnpm-lock 2026-02-06 17:56:22 -08:00
Raymond Berger
c80a09fc2f Fix QMD CLI installation link in memory.md (#8647)
Correct the installation link for the QMD CLI in the documentation.
2026-02-06 20:53:47 -05:00
Peter Steinberger
f831c48e56 docs(changelog): highlight Opus 4.6 + Codex 5.3 first 2026-02-06 17:28:46 -08:00
Peter Steinberger
c237a82b42 docs(changelog): curate 2026.2.6 2026-02-06 16:56:29 -08:00
Peter Steinberger
94b2fc14f2 chore(deps): bump carbon beta 2026-02-06 16:56:25 -08:00
Peter Steinberger
0daf416908 fix(agents): add Opus 4.6 forward-compat fallback 2026-02-06 16:56:21 -08:00
Peter Steinberger
dca8cf958b chore(deps): update deps 2026-02-06 16:37:56 -08:00
Seb Slight
93bf75279f docs(imessage): improve macOS TCC troubleshooting guidance (#10781) 2026-02-06 19:21:52 -05:00
gitpds
fe308a3aa1 docs(imessage): add macOS TCC troubleshooting 2026-02-06 19:10:01 -05:00
Peter Steinberger
7be921c434 docs(changelog): refresh 2026.2.6 since v2026.2.3 2026-02-06 15:47:10 -08:00
Peter Steinberger
5163833be5 docs: fix markdownlint fragments + headings 2026-02-06 15:45:39 -08:00
Peter Steinberger
d898ad6807 fix(telegram): cast fetch for grammY ApiClientOptions 2026-02-06 15:45:34 -08:00
Peter Steinberger
677450cd9b chore(release): bump version to 2026.2.6 2026-02-06 15:37:31 -08:00
Peter Steinberger
ac5944cde7 docs(changelog): include merged PRs since v2026.2.3 2026-02-06 15:36:13 -08:00
Peter Steinberger
3b768a2851 docs(changelog): prepare 2026.2.6 2026-02-06 15:30:17 -08:00
Shadril Hassan Shifat
2c8af78d20 fix(hooks): replace debug console.log with proper subsystem logging in session-memory (#10730)
* fix: replace debug console.log with proper subsystem logging in session-memory

* fix(hooks): normalize session-memory subsystem logging

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-06 17:22:38 -06:00
calvin-hpnet
48b0fd8d88 feat(antigravity): update default model to Claude Opus 4.6 (#10720)
* feat(antigravity): update default model to Claude Opus 4.6

Claude Opus 4.5 has been replaced by Claude Opus 4.6 on the
Antigravity (Google Cloud Code Assist) platform.

- Update DEFAULT_MODEL in google-antigravity-auth extension
- Update testing docs to reference the new model

* fix: update remaining antigravity opus 4.5 refs in zh-CN docs and tests

Address review comments from Greptile:
- Update zh-CN/testing.md antigravity model references
- Update pi-tools-agent-config.test.ts model IDs

* Antigravity: default OAuth model to Opus 4.6 (#10720) (thanks @calvin-hpnet)

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-06 16:42:57 -06:00
Tak Hoffman
40425db0f1 feat(memory): document Voyage embeddings + VOYAGE_API_KEY (#7078) (thanks @mcinteerj) (#10699) 2026-02-06 15:51:47 -06:00
Jake
6965a2cc9d feat(memory): native Voyage AI support (#7078)
* feat(memory): add native Voyage AI embedding support with batching

Cherry-picked from PR #2519, resolved conflict in memory-search.ts
(hasRemote -> hasRemoteConfig rename + added voyage provider)

* fix(memory): optimize voyage batch memory usage with streaming and deduplicate code

Cherry-picked from PR #2519. Fixed lint error: changed this.runWithConcurrency
to use imported runWithConcurrency function after extraction to internal.ts
2026-02-06 15:09:32 -06:00
Tak Hoffman
e3d3893d5d Docs: revise PR and issue submission guides (#10617)
* Docs: revise PR submission guide

* Docs: revise issue submission guide
2026-02-06 13:29:11 -06:00
Yida-Dev
4216449405 fix: guard resolveUserPath against undefined input (#10176)
* fix: guard resolveUserPath against undefined input

When subagent spawner omits workspaceDir, resolveUserPath receives
undefined and crashes on .trim().  Add a falsy guard that falls back
to process.cwd(), matching the behavior callers already expect.

Closes #10089

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: harden runner workspace fallback (#10176) (thanks @Yida-Dev)

* fix: harden workspace fallback scoping (#10176) (thanks @Yida-Dev)

* refactor: centralize workspace fallback classification and redaction (#10176) (thanks @Yida-Dev)

* test: remove explicit any from utils mock (#10176) (thanks @Yida-Dev)

* security: reject malformed agent session keys for workspace resolution (#10176) (thanks @Yida-Dev)

---------

Co-authored-by: Yida-Dev <reyifeijun@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-06 13:16:58 -05:00
Tak Hoffman
5842bcaaf7 Docs: add PR sign-off template (#10561) 2026-02-06 11:58:39 -06:00
Seb Slight
991cf4d7fe Docs: revamp installer internals for readability and accuracy (#10499)
* docs(install): revamp installer internals for readability and accuracy

Restructure the installer internals page with better flow and Mintlify
components (CardGroup, Steps, Tabs, AccordionGroup). All flags, env vars,
and behavioral descriptions cross-checked against install.sh,
install-cli.sh, and install.ps1 source code.

- Add CardGroup chooser and Quick Commands section at top
- Organize each script into consistent Flow → Examples → Reference pattern
- Move flags/env var tables into collapsible Accordions
- Consolidate troubleshooting into AccordionGroup at bottom
- Add missing flags (--version, --beta, --verbose, --help, etc.)
- Add missing env vars (OPENCLAW_VERSION, OPENCLAW_BETA, etc.)
- Document install-cli.sh fully (was one paragraph)
- Fix non-interactive checkout detection behavior (defaults to npm)
- Use --proto/--tlsv1.2 in curl examples to match script usage
- No content deleted; all original info preserved or relocated

* fix(docs): correct in-page anchor hrefs for installer cards

* docs(install): replace CardGroup with table for installer overview
2026-02-06 10:49:38 -05:00
Seb Slight
578a6e27aa Docs: enable markdownlint autofixables except list numbering (#10476)
* docs(markdownlint): enable autofixable rules except list numbering

* docs(zalo): fix malformed bot platform link
2026-02-06 10:08:59 -05:00
Sebastian
0a1f4f666a revert(docs): undo markdownlint autofix churn 2026-02-06 10:00:08 -05:00
Sebastian
c7aec0660e docs(markdownlint): enable autofixable rules and normalize links 2026-02-06 09:55:12 -05:00
Sebastian
1bf9f237f7 docs: linting 2026-02-06 09:35:57 -05:00
Sebastian
134c03a903 feat: add markdownlint configuration for documentation formatting and linting 2026-02-06 09:14:36 -05:00
Seb Slight
18b480dd3e Docs: sharpen Install tab to stop duplicating Getting Started (#10416)
* docs(install): reframe install overview to stop duplicating getting started

* docs(install): link default installer row to getting started, not internals

* docs(install): use Mintlify components for install overview

* docs(install): fix card grid layout with CardGroup

* docs(install): platform tabs for global install, npm/pnpm as accordion

* docs(install): add PowerShell no-onboard alternative

* docs(install): add repo link to from-source clone step

* docs(install): capitalize OpenClaw in repo link

* docs(install): add pnpm link --global to from-source steps

* docs(install): rewrite install overview for clarity and flow

* docs(install): use tooltip for Windows WSL2 recommendation

* docs(install): use Note box for Windows WSL2 recommendation

* docs(install): group install methods under single heading

* docs(install): standardize tab labels across installer sections

* docs(install): rewrite Node.js page with install instructions and better structure

* docs(install): clarify Node.js page intro

* docs(install): scope auto-install note to installer script, link Node page

* docs(install): fix installer script link to internals page

* docs: rename Install methods nav group to Other install methods

* docs(install): link to on-page anchor, use Tip box for recommended

* docs(install): wrap install methods in AccordionGroup with Tip box

* docs: move Node.js page from Install to Help > Environment and debugging

* docs(install): add complete flags and env vars reference to installer internals

* docs(install): use stable troubleshooting anchor for Node.js link

* docs(install): fix Node page installer anchor

* docs(install): fix broken installer script anchor in requirements note
2026-02-06 08:55:05 -05:00
ideoutrea
0b51f0d762 Fix conflicts 2026-02-06 18:16:20 +08:00
ideoutrea
7a9deb2400 Resolve conflicts 2026-02-06 18:13:42 +08:00
ide-rea
3997316fb0 Merge branch 'main' into qianfan 2026-02-06 17:58:28 +08:00
ideoutrea
360851366f Support ERNIE-5.0-Thinking-Preview 2026-02-06 17:49:54 +08:00
Gustavo Madeira Santana
c75275f109 Update: harden control UI asset handling in update flow (#10146)
* Update: harden control UI asset handling in update flow

* fix: harden update doctor entrypoint guard (#10146) (thanks @gumadeiras)
2026-02-06 01:14:00 -05:00
Tak Hoffman
50e687d17d Docs: add PR and issue submission guides (#10150)
* Docs: add PR and issue submission guides

* Docs: fix LLM-assisted wording
2026-02-05 23:59:47 -06:00
Gustavo Madeira Santana
4a59b7786b fix: CLI harden update restart imports and fix nested bundle version resolution 2026-02-06 00:09:48 -05:00
Tak Hoffman
8a352c8f9d Web UI: add token usage dashboard (#10072)
* feat(ui): Token Usage dashboard with session analytics

Adds a comprehensive Token Usage view to the dashboard:

Backend:
- Extended session-cost-usage.ts with per-session daily breakdown
- Added date range filtering (startMs/endMs) to API endpoints
- New sessions.usage, sessions.usage.timeseries, sessions.usage.logs endpoints
- Cost breakdown by token type (input/output/cache read/write)

Frontend:
- Two-column layout: Daily chart + breakdown | Sessions list
- Interactive daily bar chart with click-to-filter and shift-click range select
- Session detail panel with usage timeline, conversation logs, context weight
- Filter chips for active day/session selections
- Toggle between tokens/cost view modes (default: cost)
- Responsive design for smaller screens

UX improvements:
- 21-day default date range
- Debounced date input (400ms)
- Session list shows filtered totals when days selected
- Context weight breakdown shows skills, tools, files contribution

* fix(ui): restore gatewayUrl validation and syncUrlWithSessionKey signature

- Restore normalizeGatewayUrl() to validate ws:/wss: protocol
- Restore isTopLevelWindow() guard for iframe security
- Revert syncUrlWithSessionKey signature (host param was unused)

* feat(ui): Token Usage dashboard with session analytics

Adds a comprehensive Token Usage view to the dashboard:

Backend:
- Extended session-cost-usage.ts with per-session daily breakdown
- Added date range filtering (startMs/endMs) to API endpoints
- New sessions.usage, sessions.usage.timeseries, sessions.usage.logs endpoints
- Cost breakdown by token type (input/output/cache read/write)

Frontend:
- Two-column layout: Daily chart + breakdown | Sessions list
- Interactive daily bar chart with click-to-filter and shift-click range select
- Session detail panel with usage timeline, conversation logs, context weight
- Filter chips for active day/session selections
- Toggle between tokens/cost view modes (default: cost)
- Responsive design for smaller screens

UX improvements:
- 21-day default date range
- Debounced date input (400ms)
- Session list shows filtered totals when days selected
- Context weight breakdown shows skills, tools, files contribution

* fix: usage dashboard data + cost handling (#8462) (thanks @mcinteerj)

* Usage: enrich metrics dashboard

* Usage: add latency + model trends

* Gateway: improve usage log parsing

* UI: add usage query helpers

* UI: client-side usage filter + debounce

* Build: harden write-cli-compat timing

* UI: add conversation log filters

* UI: fix usage dashboard lint + state

* Web UI: default usage dates to local day

* Protocol: sync session usage params (#8462) (thanks @mcinteerj, @TakHoffman)

---------

Co-authored-by: Jake McInteer <mcinteerj@gmail.com>
2026-02-05 22:35:46 -06:00
Gustavo Madeira Santana
b40da2cb7a fix: remove dead restore control-ui step from update runner 2026-02-05 22:10:55 -05:00
Gustavo Madeira Santana
72245855e5 fix: add fallback for Control UI asset resolution in global installs 2026-02-05 22:03:43 -05:00
Gustavo Madeira Santana
7b2a221212 chore: run lint step after build during preflight check 2026-02-05 21:22:27 -05:00
Sebastian
ac0c2f260f docs: update clawtributors (add @unisone) 2026-02-05 21:19:42 -05:00
Alex Zaytsev
d2aee7da68 docs: add activeHours to heartbeat field notes and examples (#9366)
Co-authored-by: unisone <unisone@users.noreply.github.com>
2026-02-05 21:18:57 -05:00
Coy Geek
717129f7f9 fix: silence unused hook token url param (#9436)
* fix: Gateway authentication token exposed in URL query parameters

* fix: silence unused hook token url param

* fix: remove gateway auth tokens from URLs (#9436) (thanks @coygeek)

* test: fix Windows path separators in audit test (#9436)

---------

Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 18:08:29 -08:00
Matt Ezell
b1430aaaca Chore: Update memory.md with current default workspace path (#9559)
Removed 'clawd' workspace reference - updated with current default workspace path of '~/.openclaw/workspace'
2026-02-05 21:06:14 -05:00
Shailesh
bccdc95a9b Cap sessions_history payloads to prevent context overflow (#10000)
* Cap sessions_history payloads to prevent context overflow

* fix: harden sessions_history payload caps

* fix: cap sessions_history payloads to prevent context overflow (#10000) (thanks @gut-puncture)

---------

Co-authored-by: Shailesh Rana <shaileshrana@ShaileshMM.local>
Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 17:50:57 -08:00
cpojer
328b69be17 chore: Fix audit test on Windows. 2026-02-06 10:22:48 +09:00
cpojer
f16e32b73d fix: Do not process.exit(0) in the middle of a test. 2026-02-06 09:57:51 +09:00
cpojer
8abce8a84d fix: onToolResult fallback is not expected. 2026-02-06 09:55:56 +09:00
therealZpoint-bot
c448e5da6f fix(docs): correct OpenCode Zen description in code comment (#9998)
* fix(docs): correct OpenCode Zen description in code comment

OpenCode Zen is a pay-as-you-go token-based API, not a $200/month
subscription. The subscription tiers ($20/$100/$200) are OpenCode Black,
a separate product.

This fixes the misleading comment that conflated Zen with Black.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: align OpenCode Zen billing copy (#9998) (thanks @therealZpoint-bot)

---------

Co-authored-by: Claude <claude@archibald.local>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-05 19:55:02 -05:00
cpojer
6c42d34610 chore: Add VS Code defaults and extensions so that Oxlint/Oxfmt work automatically. 2026-02-06 09:49:47 +09:00
cpojer
ee1ec3faba Add proper onToolResult fallback. 2026-02-06 09:42:10 +09:00
George Pickett
a459e237e8 fix(gateway): require auth for canvas host and a2ui assets (#9518) (thanks @coygeek) 2026-02-05 16:37:58 -08:00
Coy Geek
47538bca4d fix: Gateway canvas host bypasses auth and serves files unauthenticated 2026-02-05 16:37:58 -08:00
adam91holt
05b28c147d fix: wire onToolResult callback for verbose tool summaries (#2022)
HOTFIX: Tool summaries were not being sent to chat channels when verbose mode
was enabled. The onToolResult callback was defined in the types but never
wired up in dispatch-from-config.ts.

This adds the missing callback alongside onBlockReply, using the same
dispatcher.sendBlockReply() path to deliver tool summaries to WhatsApp,
Telegram, and other chat channels.

Fixes verbose tool summaries not appearing in WhatsApp despite /verbose on.
2026-02-05 16:37:30 -08:00
Gustavo Madeira Santana
0a48592475 add PR review workflow templates 2026-02-05 19:36:34 -05:00
zerone0x
3ad7958365 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 <noreply@anthropic.com>
2026-02-05 16:35:56 -08:00
Raphael Borg Ellul Vincenti
34a58b839c 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
2026-02-05 16:35:38 -08:00
Sash Zats
ec0728b357 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
2026-02-05 16:35:34 -08:00
Abdel Sy Fane
0c7fa2b0d5 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 <gpickett00@gmail.com>
2026-02-05 16:34:48 -08:00
Yifeng Wang
5f6e1c19bd 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 <noreply@anthropic.com>
2026-02-06 09:32:10 +09:00
Yifeng Wang
7e005acd3c chore: update pnpm-lock.yaml for feishu extension deps
Add lockfile entries for:
- @larksuiteoapi/node-sdk@^1.56.1
- @sinclair/typebox@0.34.47
- zod@^4.3.6

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-06 09:32:10 +09:00
Yifeng Wang
8ba1387ba2 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 <noreply@anthropic.com>
2026-02-06 09:32:10 +09:00
Yifeng Wang
7e32f1ce20 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 <noreply@anthropic.com>
2026-02-06 09:32:10 +09:00
Yifeng Wang
2267d58afc 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 <noreply@anthropic.com>
2026-02-06 09:32:10 +09:00
ironbyte-rgb
02842bef91 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 <amontaboi76@gmail.com>
Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 16:29:07 -08:00
wangai-studio
57326f72e6 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
2026-02-05 16:25:21 -08:00
Tyler Yust
370bbcd89b 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 <4robinlehmann@gmail.com>
2026-02-05 16:23:18 -08:00
cpojer
6f4665dda3 chore: Update deps. 2026-02-06 09:11:46 +09:00
大猫子
2d15dd757d 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 <1811866786@qq.com>
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-02-05 16:11:19 -08:00
Aisling Cahill
861725fba1 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.
2026-02-05 16:08:46 -08:00
Darshil
de7b2ba7d5 fix: normalize xhigh aliases and docs sync (#9976) 2026-02-05 16:07:51 -08:00
slonce70
7db839544d Changelog: note #9976 thinking alias + Codex 5.3 docs sync 2026-02-05 16:07:51 -08:00
slonce70
5958e5693c Thinking: accept extra-high alias and sync Codex FAQ wording 2026-02-05 16:07:51 -08:00
Abdel Sy Fane
bc88e58fcf 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 <ddhameliya@mail.sfsu.edu>
Co-authored-by: Darshil <81693876+dvrshil@users.noreply.github.com>
Co-authored-by: George Pickett <gpickett00@gmail.com>
2026-02-05 16:06:11 -08:00
George Pickett
141f551a4c fix(exec-approvals): coerce bare string allowlist entries (#9903) (thanks @mcaxtr) 2026-02-05 15:52:51 -08:00
Marcus Castro
6ff209e932 fix(exec-approvals): coerce bare string allowlist entries to objects (#9790) 2026-02-05 15:52:51 -08:00
fujiwara-tofu-shop
b0befb5f5d 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).
2026-02-05 15:49:03 -08:00
Maksym Brashchenko
40e23b05f7 fix(cron): re-arm timer in finally to survive transient errors (#9948) 2026-02-05 15:46:59 -08:00
Igor Markelov
313e2f2e85 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 <TYTYYUST@YAHOO.COM>
2026-02-05 15:43:37 -08:00
George Pickett
68393bfa36 chore: changelog for xAI onboarding (#9885) (thanks @grp06) 2026-02-05 15:14:50 -08:00
George Pickett
155dfa93e5 fix(onboard): align xAI default model to grok-4 2026-02-05 15:14:50 -08:00
George Pickett
db31c0ccca feat: add xAI Grok provider support 2026-02-05 15:14:50 -08:00
Daijiro Miyazawa
cefd87f355 Fix: Enable scrolling on the dashboard config page (#1822)
* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard

* Fix: Enable scrolling in dashboard
2026-02-05 15:10:11 -08:00
Gustavo Madeira Santana
8577d015b2 chore: remove tracked .DS_Store files 2026-02-05 18:01:29 -05:00
nicolasstanley
4a5e9f0a4f 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 <gpickett00@gmail.com>
2026-02-05 14:45:45 -08:00
Seb Slight
c18452598a 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
2026-02-05 17:45:01 -05:00
Gustavo Madeira Santana
3299aeb904 Agents: bump pi-mono to 0.52.5 (#9949)
* Agents: bump pi-mono to 0.52.5

* Changelog: add PR reference for pi bump
2026-02-05 17:36:25 -05:00
George Pickett
8fdc0a2841 docs: note secure DM guidance update (#9377) (thanks @Shrinija17) 2026-02-05 14:27:56 -08:00
George Pickett
873182ec2d docs: tighten secure DM example 2026-02-05 14:27:56 -08:00
Shrinija Kummari
b8004a28cc 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 <noreply@anthropic.com>
2026-02-05 14:27:56 -08:00
Gustavo Madeira Santana
6b7d3c3062 Revert "feat(skills): add QR code skill (#8817)"
This reverts commit ad13c265ba.
2026-02-05 17:20:27 -05:00
Glucksberg
d4c560853c 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
2026-02-05 13:58:43 -08:00
Glucksberg
4e1a7cd60c 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.
2026-02-05 13:58:37 -08:00
Gustavo Madeira Santana
4629054403 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)
2026-02-05 16:54:44 -05:00
Glucksberg
93b450349f 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.
2026-02-05 13:42:59 -08:00
Glucksberg
2ca78a8aed 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 <markus@glucksberg.com>
2026-02-05 13:42:52 -08:00
Vincent Koc
db8e9b37c6 chore(agentsmd): add tsgo command to AGENTS.md (#9894)
Add `pnpm tsgo` command to AGENTS.md development reference

Co-authored-by: vincentkoc <vincentkoc@users.noreply.github.com>
2026-02-05 13:37:14 -08:00
Omar Khaleel
ad13c265ba 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
2026-02-05 13:34:43 -08:00
MattQ
7159d3b254 Docs: escape hash symbol in help channel names in issue template (#9695) 2026-02-05 13:27:50 -08:00
Caelum
d6c088910b 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 <noreply@anthropic.com>
2026-02-05 13:27:45 -08:00
Tyler Yust
821520a057 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)
2026-02-05 13:08:41 -08:00
Christian Klotz
f32eeae3bc 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
2026-02-05 20:37:53 +00:00
Josh Palmer
7c951b01ab 🤖 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
2026-02-05 12:33:59 -08:00
Josh Palmer
4fc4c5256a 🤖 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: 九灵云 <server@jiulingyun.cn>
2026-02-05 12:29:04 -08:00
Michael Lee
eb80b9acb3 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 <slurpy@openclaw.ai>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-05 12:09:23 -08:00
Rajat Joshi
ea237115a9 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.
2026-02-05 12:05:14 -08:00
大猫子
679bb087db 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 <1811866786@qq.com>
2026-02-05 13:56:58 -05:00
Gustavo Madeira Santana
1473fb19a5 update handle 2026-02-05 13:56:10 -05:00
Ayaan Zaidi
01db1dde1a fix: telegram topic auto-threading — use parseTelegramTarget, add tests (#7235) (thanks @Lukavyi) 2026-02-06 00:23:04 +05:30
Clawdbot
a13efbe2b5 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.
2026-02-06 00:23:04 +05:30
Clawdbot
6ac5dd2c0e test: cover telegram topic threadId auto-injection and subagent origin threading 2026-02-06 00:23:04 +05:30
Clawdbot
eef247b7a4 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.
2026-02-06 00:23:04 +05:30
Seb Slight
9e0030b75f docs(onboarding): streamline CLI onboarding docs (#9830) 2026-02-05 13:46:11 -05:00
Christian Klotz
ddedb56c01 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
2026-02-05 18:25:03 +00:00
Peter Steinberger
547374220c chore: reset appcast to 2026.2.3 2026-02-05 09:48:14 -08:00
Sebastian
c8f4bca0c4 docs: fix onboarding rendering issues 2026-02-05 12:14:45 -05:00
Seb Slight
3011b00d39 docs(onboarding): add bootstrapping page (#9767) 2026-02-05 12:08:35 -05:00
Ayaan Zaidi
cf95b2f3f4 fix: update changelog for help sorting (#8068) (thanks @deepsoumya617) 2026-02-05 21:23:41 +05:30
Soumyadeep Ghosh
203e3804b3 CLI: sort commands alphabetically in help output
Fixes #7964

Added sortSubcommands: true to configureHelp() to display
commands in alphabetical order when running 'openclaw --help'.
2026-02-05 21:23:41 +05:30
sebslight
34424ce536 docs(install): rename install overview page 2026-02-05 10:29:35 -05:00
Seb Slight
675c26b2b0 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
2026-02-05 10:09:45 -05:00
cpojer
8b8451231c chore: Typecheck test helper files. 2026-02-05 19:51:00 +09:00
cpojer
460808e0c8 Update deps. 2026-02-05 19:44:08 +09:00
Ayaan Zaidi
f2c5c847bd fix: preserve telegram DM topic threadId (#9039) (thanks @lailoo) 2026-02-05 15:33:30 +05:30
damaozi
c0b267a03a 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.
2026-02-05 15:33:30 +05:30
damaozi
8860d2ed7f 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
2026-02-05 15:33:30 +05:30
Peter Steinberger
a4d1af1b11 fix: resolve discord owner allowFrom matches 2026-02-05 00:51:39 -08:00
Peter Steinberger
5031b283a5 chore: bump version to 2026.2.4 2026-02-05 00:38:50 -08:00
Peter Steinberger
bdb90ea4ee test: register discord plugin in allowlist test 2026-02-05 00:38:50 -08:00
Peter Steinberger
d6cde28c8e fix: stabilize windows acl tests and command auth registry (#9335) (thanks @M00N7682) 2026-02-05 00:38:35 -08:00
M00N7682
f26cc60872 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 <noreply@anthropic.com>
2026-02-05 00:35:29 -08:00
Peter Steinberger
1ee1522daa fix: resolve bundled chrome extension assets (#8914) (thanks @kelvinCB) 2026-02-05 00:17:09 -08:00
Kelvin Calcano
34e78a7054 style(cli): satisfy lint rules in extension path resolver 2026-02-05 00:17:09 -08:00
Kelvin Calcano
44bbe09bee fix(cli): support bundled extension path in dist root 2026-02-05 00:17:09 -08:00
Kelvin Calcano
1008c28f5a test(cli): use unique temp dir for extension install 2026-02-05 00:17:09 -08:00
Kelvin Calcano
0621d0e9e8 fix(cli): resolve bundled chrome extension path 2026-02-05 00:17:09 -08:00
Peter Steinberger
3b40227bc6 fix: remove unused cron import 2026-02-05 07:56:16 +00:00
Peter Steinberger
d84eb46467 fix: restore discord owner hint from allowlists 2026-02-04 23:34:22 -08:00
ideoutrea
7af00f040a Optimize import 2026-02-05 14:53:38 +08:00
ideoutrea
4d30f97407 Fix key resolve 2026-02-05 14:40:56 +08:00
ideoutrea
ff948a6dd7 Optimize doc 2026-02-05 14:04:23 +08:00
ideoutrea
ad759c9446 Optimize format 2026-02-05 13:50:09 +08:00
ide-rea
9ccbd57016 Merge branch 'openclaw:main' into qianfan 2026-02-05 13:36:44 +08:00
ideoutrea
52c9d3480f Add auth choice 2026-02-05 13:35:35 +08:00
hyf0-agent
8524666454 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 <hyf0-agent@users.noreply.github.com>
2026-02-05 13:52:55 +09:00
ide-rea
517a8eafe5 Merge branch 'openclaw:main' into qianfan 2026-02-05 12:43:21 +08:00
ideoutrea
c8e67ad5d5 Fix import error 2026-02-05 12:39:38 +08:00
Peter Steinberger
54ddbc4660 chore: update 2026.2.3 notes 2026-02-04 17:55:13 -08:00
Peter Steinberger
7f95cdac78 chore(mac): update appcast for 2026.2.3 2026-02-04 17:55:13 -08:00
Peter Steinberger
cfdc551346 fix(mac): resolve cron schedule formatters 2026-02-04 17:55:13 -08:00
Peter Steinberger
f895c9fba1 chore: sync plugin versions to 2026.2.3 2026-02-04 17:55:13 -08:00
Gustavo Madeira Santana
22927b0834 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 <f@trycua.com>
2026-02-04 20:38:46 -05:00
Gustavo Madeira Santana
385a7eba33 fix: enforce owner allowlist for commands 2026-02-04 20:05:08 -05:00
Gustavo Madeira Santana
a6fd76efeb Message: clarify media schema + fix MEDIA newline 2026-02-04 19:59:15 -05:00
Christian Klotz
21f8c3db18 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)
2026-02-05 00:58:49 +00:00
Gustavo Madeira Santana
392bbddf29 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 <victormier@gmail.com>
2026-02-04 19:49:36 -05:00
Tak Hoffman
0cd47d830f fix: cover anonymous voice allowlist callers (#8104) (thanks @victormier) (#9188) 2026-02-04 18:23:19 -06:00
Christian Klotz
90b4e54354 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)
2026-02-05 00:20:44 +00:00
Gustavo Madeira Santana
4434cae565 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 <victormier@gmail.com>
2026-02-04 19:11:23 -05:00
Gustavo Madeira Santana
5e025c4ba3 Tests: restore TUI gateway env 2026-02-04 19:09:52 -05:00
Gustavo Madeira Santana
a13ff55bd9 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 <victormier@gmail.com>
2026-02-04 18:59:44 -05:00
Christian Klotz
96abc1c864 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)
2026-02-04 22:35:51 +00:00
Gustavo Madeira Santana
38e6da1fe0 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)
2026-02-04 17:12:16 -05:00
lsh411
a42e3cb78a 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 <m116512@m1ui-MacBookAir-2.local>
Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-04 16:49:12 -05:00
Josh Palmer
bebf323775 Discord: allow disabling thread starter context 2026-02-04 13:25:56 -08:00
mudrii
5d82c82313 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 <gumadeiras@gmail.com>
2026-02-04 16:16:34 -05:00
Shakker
43590d8287 changelog: add shell completion auto-fix entry 2026-02-04 19:51:06 +00:00
Shakker
dae0e2b325 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
2026-02-04 19:51:06 +00:00
Shakker
3e14192730 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
2026-02-04 19:51:06 +00:00
Shakker
dbaf0a8ae2 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
2026-02-04 19:51:06 +00:00
Shakker
5bd63b012c doctor: integrate shell completion check into doctor command
- Import and call `doctorShellCompletion` during doctor run
- Checks/fixes completion issues before gateway health check
2026-02-04 19:51:06 +00:00
Shakker
3fae903863 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
2026-02-04 19:51:06 +00:00
Shakker
d5f8208c38 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
2026-02-04 19:51:06 +00:00
Shakker
42c690632d feat: add shell completion test script for installation verification 2026-02-04 19:51:06 +00:00
Shakker
1d17630dc6 feat: add shell completion installation prompt to CLI update command 2026-02-04 19:51:06 +00:00
Josh Palmer
2b1da4f5d8 🤖 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
2026-02-04 10:42:12 -08:00
Josh Palmer
aaeecc8c8d 🤖 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
2026-02-04 10:42:12 -08:00
Christian Klotz
eab0a07f71 chore: replace landpr prompt with end-to-end landing workflow (#8916) 2026-02-04 16:01:46 +00:00
Shakker
01ce144fa9 fix(app-render): handle optional model in renderApp function 2026-02-04 15:42:58 +00:00
Seb Slight
718dba8cb6 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
2026-02-04 10:37:14 -05:00
Tak Hoffman
9822985ea6 fix(web ui): agent model selection 2026-02-04 09:25:38 -06:00
ideoutrea
fb5280e1b5 optimize doc 2026-02-04 22:57:34 +08:00
ide-rea
009abd306a Merge branch 'main' into qianfan 2026-02-04 22:39:13 +08:00
ideoutrea
8c53dfb74f Optimize doc 2026-02-04 22:30:42 +08:00
ideoutrea
7bf4080608 Fix format 2026-02-04 22:27:51 +08:00
ideoutrea
1de05ad068 Add baidu qianfan model provider 2026-02-04 22:27:49 +08:00
Seb Slight
2196456d4a Revert "feat: Add Docs Chat Widget with RAG-powered Q&A (#7908)" (#8834)
This reverts commit fa4b28d7af.
2026-02-04 08:35:46 -05:00
Peter Steinberger
6f200ea77f fix: force reload cron store 2026-02-04 04:24:04 -08:00
Peter Steinberger
5b0851ebd8 feat: add cloudflare ai gateway provider 2026-02-04 04:10:13 -08:00
Peter Steinberger
19ecdce275 fix: align proxy fetch typing 2026-02-04 04:09:53 -08:00
Iranb
18652d181b fix(imessage): detect self-chat echoes to prevent infinite loops (#8680) 2026-02-04 03:31:35 -08:00
Yudong Han
f633a8cb22 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
2026-02-04 03:26:13 -08:00
Yudong Han
78f8a29071 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)
2026-02-04 03:26:13 -08:00
Ayaan Zaidi
78fd194722 fix: telegram forward metadata + cron delivery guard (#8392) (thanks @Glucksberg) 2026-02-04 16:43:20 +05:30
Glucksberg
b2361292e7 fix: trim legacy signature fallback, type fromChatType as union 2026-02-04 16:43:20 +05:30
Glucksberg
57566c5e4d 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
2026-02-04 16:43:20 +05:30
Christian Klotz
da6de49815 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
2026-02-04 10:09:28 +00:00
Peter Steinberger
6341819d74 fix: cron announce delivery path (#8540) (thanks @tyler6204) 2026-02-04 01:03:59 -08:00
Tyler Yust
c396877dd9 Changelog: move cron entries to 2026.2.3 2026-02-04 01:03:59 -08:00
Tyler Yust
79d00e20db UI: handle future timestamps in formatAgo 2026-02-04 01:03:59 -08:00
Tyler Yust
f8d2534062 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)
2026-02-04 01:03:59 -08:00
Tyler Yust
6fb8d8850e 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
246896d64b 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
64df61f697 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
ef4949b936 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
1409943863 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
3f82daefd8 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
ab9f06f4ff 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
0bb0dfc9bc 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
511c656cbc 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.
2026-02-04 01:03:59 -08:00
Tyler Yust
3a03e38378 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
2026-02-04 01:03:59 -08:00
ideoutrea
30ac80b96b Add baidu qianfan model provider 2026-02-04 16:36:37 +08:00
Peter Steinberger
a749db9820 fix: harden voice-call webhook verification 2026-02-03 23:47:27 -08:00
Val Alexander
fa4b28d7af 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 6721f5b0b7.

* 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
2026-02-04 07:42:20 +00:00
Peter Steinberger
5292367324 docs: update Feishu plugin docs 2026-02-03 23:24:41 -08:00
Peter Steinberger
35eb40a700 fix(security): separate untrusted channel metadata from system prompt (thanks @KonstantinMirin) 2026-02-03 23:02:45 -08:00
Lucas Kim
6fdb136688 docs: document secure DM mode preset (#7872)
* docs: document secure DM mode preset

* fix: resolve merge conflict in resizable-divider
2026-02-04 06:55:13 +00:00
Peter Steinberger
44d1aa31f3 docs: add changelog for #7178 (thanks @Yeom-JinHo) 2026-02-03 22:47:21 -08:00
Yeom-JinHo
7b3d23b703 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
2026-02-03 22:46:14 -08:00
Peter Steinberger
efc9d0a498 docs: note tmux send-keys TUI guidance (#7737) (thanks @Wangnov) 2026-02-03 22:04:27 -08:00
Wangnov
089d03453d docs(skills): split tmux send-keys for TUI (#7737)
* docs(skills): split tmux send-keys for TUI

* docs(skills): soften TUI send-keys wording

---------

Co-authored-by: wangnov <1694546283@qq.com>
2026-02-03 22:03:47 -08:00
Peter Steinberger
4a5d368926 fix: keep Moonshot CN base URL in onboarding (#7180) (thanks @waynelwz) 2026-02-03 21:58:51 -08:00
Liu Weizhan
1c6b25ddbb feat: add support for Moonshot API key for China endpoint 2026-02-03 21:58:51 -08:00
Peter Steinberger
9f16de2533 style: update chat new-messages button 2026-02-03 21:53:50 -08:00
Stephen Chen
d2ff28dda7 Make openclaw consistent in this file (#8533)
Co-authored-by: stephenchen2025 <schenjobs@gmail.com>
2026-02-04 00:02:25 -05:00
Michelle Tilley
f04e84f194 Address PR feedback 2026-02-04 04:02:38 +00:00
Michelle Tilley
5af322f710 feat(discord): add set-presence action for bot activity and status
Bridge the agent tools layer to the Discord gateway WebSocket via a new
gateway registry, allowing agents to set the bot's activity and online
status. Supports playing, streaming, listening, watching, custom, and
competing activity types. Custom type uses activityState as the sidebar
text; other types show activityName in the sidebar and activityState in
the flyout. Opt-in via channels.discord.actions.presence (default false).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 04:02:38 +00:00
Ayaan Zaidi
b64c1a56a1 chore: update changelog for #8193 (thanks @gildo) 2026-02-04 09:23:17 +05:30
Ayaan Zaidi
41a4f1200b fix: honor telegram model overrides in buttons (#8193) (thanks @gildo) 2026-02-04 09:23:17 +05:30
Ermenegildo Fiorito
202c554d09 Telegram: fix model button review issues
- Add currentModel to callback handler for checkmark display
- Add 64-byte callback_data limit protection (skip long model IDs)
- Add tests for large model lists and callback_data limits
2026-02-04 09:23:17 +05:30
Ermenegildo Fiorito
16349b6e93 Telegram: add inline button model selection for /models and /model commands 2026-02-04 09:23:17 +05:30
Tyler Yust
efb4a34be4 feat: add new messages indicator style for chat interface
- Introduced a floating pill element above the compose area to indicate new messages.
- Styled the indicator with hover effects and responsive design for better user interaction.
2026-02-03 19:32:50 -08:00
Peter Steinberger
e4b084c762 chore: bump version to 2026.2.3 2026-02-03 18:33:27 -08:00
Tyler Yust
3e6c623cfe refactor: remove unnecessary blank line in policy test file 2026-02-03 18:10:26 -08:00
Tyler Yust
9c4eab69cc iMessage: promote BlueBubbles and refresh docs/skills (#8415)
* feat: Make BlueBubbles the primary iMessage integration

- Remove old imsg skill (skills/imsg/SKILL.md)
- Create new BlueBubbles skill (skills/bluebubbles/SKILL.md) with message tool examples
- Add keep-alive script documentation for VM/headless setups to docs/channels/bluebubbles.md
  - AppleScript that pokes Messages.app every 5 minutes
  - LaunchAgent configuration for automatic execution
  - Prevents Messages.app from going idle in VM environments
- Update all documentation to prioritize BlueBubbles over legacy imsg:
  - Mark imsg channel as legacy throughout docs
  - Update README.md channel lists
  - Update wizard, hubs, pairing, and index docs
  - Update FAQ to recommend BlueBubbles for iMessage
  - Update RPC docs to note imsg as legacy pattern
  - Update Chinese documentation (zh-CN)
- Replace imsg examples with generic macOS skill examples where appropriate

BlueBubbles is now the recommended first-class iMessage integration,
with the legacy imsg integration marked for potential future removal.

* refactor: Update import paths and improve code formatting

- Adjusted import paths in session-status-tool.ts, whatsapp-heartbeat.ts, and heartbeat-runner.ts for consistency.
- Reformatted code for better readability by aligning and grouping related imports and function parameters.
- Enhanced error messages and conditional checks for clarity in heartbeat-runner.ts.

* skills: restore imsg skill and align bluebubbles skill

* docs: update FAQ for clarity and formatting

- Adjusted the formatting of the FAQ section to ensure consistent bullet point alignment.
- No content changes were made, only formatting improvements for better readability.

* style: oxfmt touched files

* fix: preserve BlueBubbles developer reference (#8415) (thanks @tyler6204)
2026-02-03 18:06:54 -08:00
Peter Steinberger
9c5941ba46 fix: add legacy daemon-cli shim for updates 2026-02-03 18:04:48 -08:00
Peter Steinberger
41d2993f7b fix(matrix): require unique allowlist matches in wizard 2026-02-03 18:04:02 -08:00
Peter Steinberger
d3ba57b7d7 feat: add configurable web_fetch maxChars cap 2026-02-03 18:03:53 -08:00
Peter Steinberger
6b4b6049b4 fix: enforce Nextcloud Talk allowlist by user id 2026-02-03 18:03:53 -08:00
Peter Steinberger
bbe9cb3022 fix(update): honor update.channel for update.run 2026-02-03 17:57:55 -08:00
Tak Hoffman
61a7fc5e0e Docs: drop healthcheck from bootstrap 2026-02-03 19:50:40 -06:00
Peter Steinberger
e895e85f54 fix: improve build-info resolution for commit/version 2026-02-03 17:31:51 -08:00
Peter Steinberger
e59eb814bd chore: bump version to 2026.2.2-1 2026-02-03 17:26:10 -08:00
Gustavo Madeira Santana
df55eeacdb Merge branch 'main' of https://github.com/openclaw/openclaw 2026-02-03 20:20:16 -05:00
Josh Palmer
fd8f8843bd Docs: guard zh-CN i18n workflow
What:
- document zh-CN docs pipeline and generated-doc guardrails
- note Discord escalation when the pipeline drags

Why:
- prevent accidental edits to generated translations

Tests:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: Josh Palmer <joshpalmer123@gmail.com>
2026-02-03 17:19:58 -08:00
Gustavo Madeira Santana
a9bb96ade3 fix: use build-info for version fallback 2026-02-03 20:19:32 -05:00
Peter Steinberger
f1cbe7db1d chore: add mac dSYM zip to release artifacts 2026-02-03 17:15:02 -08:00
Peter Steinberger
95cd2210f9 chore: update appcast for 2026.2.2 2026-02-03 17:04:27 -08:00
Peter Steinberger
539a15e63f chore: prep 2026.2.2 docs/release checks 2026-02-03 16:38:42 -08:00
Peter Steinberger
4df4435c45 test: reset /approve mock per test (#1) (thanks @mitsuhiko) 2026-02-03 16:19:41 -08:00
Peter Steinberger
d41acf99a6 test: add /approve gateway scope coverage (#1) (thanks @mitsuhiko) 2026-02-03 16:19:20 -08:00
Armin Ronacher
efe2a464af fix(approvals): gate /approve by gateway scopes 2026-02-03 16:18:49 -08:00
Peter Steinberger
66d8117d44 fix: harden control ui framing + ws origin 2026-02-03 16:00:57 -08:00
Josh Palmer
0223416c61 Channels: finish Feishu/Lark integration 2026-02-03 14:27:39 -08:00
Josh Palmer
2483f26c23 Channels: add Feishu/Lark support 2026-02-03 14:27:13 -08:00
Josh Palmer
4027b3583e Docs(zh-CN): add AGENTS translation workflow 2026-02-03 13:23:15 -08:00
Josh Palmer
a3ec2d0734 Docs: update zh-CN translations and pipeline
What:
- update zh-CN glossary, TM, and translator prompt
- regenerate zh-CN docs and apply targeted fixes
- add zh-CN AGENTS pipeline guidance

Why:
- address terminology/spacing feedback from #6995

Tests:
- pnpm build && pnpm check && pnpm test
2026-02-03 13:23:00 -08:00
Josh Palmer
9f03791aa9 Docs: refresh zh-CN translations + i18n guidance
What:
- update zh-CN glossary, translation prompt, and TM
- regenerate zh-CN docs and apply targeted fixes
- add zh-CN AGENTS guidance for translation pipeline

Why:
- address zh-CN terminology and spacing feedback from #6995

Tests:
- pnpm build && pnpm check && pnpm test
2026-02-03 13:22:28 -08:00
Gustavo Madeira Santana
f52ca0a712 fix(ui): note agent file refresh in changelog 2026-02-03 14:15:47 -05:00
Gustavo Madeira Santana
ddccfd3ec1 fix(ui): refresh agent files after external edits 2026-02-03 14:14:16 -05:00
Gustavo Madeira Santana
f60eae83fa fix(skills): warn when bundled dir missing 2026-02-03 14:01:40 -05:00
Gustavo Madeira Santana
5935c4d23d fix(ui): fix web UI after tsdown migration and typing changes 2026-02-03 13:56:20 -05:00
Peter Steinberger
1c4db91593 chore: prepare 2026.2.2 release 2026-02-03 10:02:01 -08:00
Peter Steinberger
9d2066bd53 fix: restore OpenClaw docs/source links in system prompt 2026-02-03 10:01:04 -08:00
Ethan Palm
f57e70912c docs: Update information architecture for OpenClaw docs (#7622)
* docs: restructure navigation into 5 tabs for better IA

* dedupe redirects

* use 8 tabs

* add missing /index extensions

* update zh navigation

* remove `default: true` and rearrange languages

* add missing redirects

* format:fix

* docs: update IA tabs + restore /images redirect (#7622) (thanks @ethanpalm)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-03 09:57:43 -08:00
Peter Steinberger
a7f4a53ce8 fix: harden Windows exec allowlist 2026-02-03 09:34:25 -08:00
Peter Steinberger
8f3bfbd1c4 fix(matrix): harden allowlists 2026-02-03 09:34:02 -08:00
Peter Steinberger
f8dfd034f5 fix(voice-call): harden inbound policy 2026-02-03 09:33:25 -08:00
Tak Hoffman
fc40ba8e7e Skills: refine healthcheck guidance 2026-02-03 09:21:34 -06:00
cpojer
1f2f79a7a7 chore: Merge tsconfigs, typecheck ui as part of pnpm tsgo locally and on CI. 2026-02-03 22:50:00 +09:00
cpojer
6e09c1142e chore: Switch to NodeNext for module/moduleResolution in ui. 2026-02-03 22:48:28 +09:00
cpojer
27677dd8bd chore: Fix all TypeScript errors in ui. 2026-02-03 22:45:29 +09:00
cpojer
be4f7ef361 fix: Fix Mac app build step. 2026-02-03 22:14:11 +09:00
cpojer
6b83d82e82 chore: clean up git hooks and actually install them again. 2026-02-03 22:08:24 +09:00
cpojer
6fb2d3d7d7 feat: remove slop. 2026-02-03 22:04:17 +09:00
cpojer
425003417d fix: Remove tsconfig.oxlint.json AGAIN. 2026-02-03 21:53:48 +09:00
cpojer
a8893094ea fix: CI: We no longer need to test the tsc build with Bun, we are always using tsdown to build now. 2026-02-03 21:34:49 +09:00
cpojer
a03d852d65 chore: Migrate to tsdown, speed up JS bundling by ~10x (thanks @hyf0).
The previous migration to tsdown was reverted because it caused a ~20x slowdown when running OpenClaw from the repo. @hyf0 investigated and found that simply renaming the `dist` folder also caused the same slowdown. It turns out the Plugin script loader has a bunch of voodoo vibe logic to determine if it should load files from source and compile them, or if it should load them from dist. When building with tsdown, the filesystem layout is different (bundled), and so some files weren't in the right location, and the Plugin script loader decided to compile source files from scratch using Jiti.

The new implementation uses tsdown to embed `NODE_ENV: 'production'`, which we now use to determine if we are running OpenClaw from a "production environmen" (ie. from dist). This removes the slop in favor of a deterministic toggle, and doesn't rely on directory names or similar.

There is some code reaching into `dist` to load specific modules, primarily in the voice-call extension, which I simplified into loading an "officially" exported `extensionAPI.js` file. With tsdown, entry points need to be explicitly configured, so we should be able to avoid sloppy code reaching into internals from now on. This might break some existing users, but if it does, it's because they were using "private" APIs.
2026-02-03 20:18:16 +09:00
Shakker
e3b85b9829 fix: shell completion and drop onboarding prompt 2026-02-03 08:45:49 +00:00
Shakker
3014a91b07 chore: update changelog for completion caching 2026-02-03 08:43:25 +00:00
Shakker
981de05181 Onboarding: drop completion prompt 2026-02-03 08:43:25 +00:00
Shakker
9950440cf6 Install: cache completion scripts on install/update 2026-02-03 08:43:25 +00:00
Shakker
80d8fe7786 CLI: cache shell completion scripts 2026-02-03 08:43:25 +00:00
Vignesh Natarajan
b37626ce6b docs: finish renaming memory state dir references 2026-02-03 00:24:13 -08:00
Vignesh Natarajan
1ee57cf727 fix: changelog entry for QMD memory (#3160) (thanks @vignesh07) 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
4322ca6f4a chore: oxfmt 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
afbb1af6c5 fix: restore safety + session_status hints 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
600c46b5a4 chore: oxfmt 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
7d5ca1176d fix: restore session_status and CLI examples 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
5915d479dc chore: oxfmt 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
30098b04d7 chore: fix lint warnings 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
f72214725d chore: restore OpenClaw branding 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
9bef525944 chore: apply formatter 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
edd6289f26 fix: derive citations chat type via session parser 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
d0b98c75e5 fix: make QMD cache key deterministic 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
e332a717a8 Lint: add braces for single-line ifs 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
23cfcd60df Fix build regressions after merge 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
465536e811 QMD: use OpenClaw config types 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
3d1c3b78ec Tests: cover QMD scope, reads, and citation clamp 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
1861e76360 Memory: clamp QMD citations to injected budget 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
c248da0317 Memory: harden QMD memory_get path checks 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
b7f4755020 Memory: fix QMD scope channel parsing 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
3e82cbd55b Memory: parse quoted qmd command 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
11a968f5c3 Docs: align QMD state dir with OpenClaw 2026-02-02 23:45:05 -08:00
Benjamin Jesuiter
5d8c665baf Tests: use OPENCLAW_STATE_DIR in qmd manager 2026-02-02 23:45:05 -08:00
vignesh07
9df78b3379 fix(memory/qmd): throttle embed + citations auto + restore --force 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
20578da204 Add how to trigger model downloads for qmd in documentation 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
564fe6f089 fix(memory-qmd): create collections via qmd CLI (no YAML) 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
dd8373a424 fix(memory-qmd): write XDG index.yml + legacy compat 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
9be3c27bb7 fix(qmd): use XDG dirs for qmd home; drop ollama docs 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
e12184661e Fix build errors 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
3a57106c1e Add more tests; make fall back more resilient and visible 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
2c30ba400b Make memory more resilient to failure 2026-02-02 23:45:05 -08:00
Vignesh Natarajan
5d3af3bc62 feat (memory): Implement new (opt-in) QMD memory backend 2026-02-02 23:45:05 -08:00
Shakker
e9f182def7 fix: error handling in restore failure reporting 2026-02-03 06:22:51 +00:00
Shakker
1b31e2f345 Onboarding/TUI: prevent prompt overlap and auto-open
- Stop onboarding output once TUI launches
- Avoid background Web UI open on TUI path
- Restore terminal state on exit
- Add terminal restore helper
2026-02-03 06:18:33 +00:00
Shakker
58d5b39c9a Onboarding: keep TUI flow exclusive 2026-02-03 06:11:11 +00:00
Shakker
157d6d2db7 CLI: restore terminal state on exit 2026-02-03 06:10:19 +00:00
Tak Hoffman
d5593d647c chore: fix formatting 2026-02-02 22:58:04 -06:00
Tak Hoffman
83715eca49 Security: tune bootstrap healthcheck prompt + healthcheck wording 2026-02-02 22:33:43 -06:00
Gustavo Madeira Santana
7dfa99a6f7 chore: fix formatting 2026-02-02 21:49:15 -05:00
Gustavo Madeira Santana
ac2b71f240 chore: fix CI 2026-02-02 21:44:31 -05:00
Tak Hoffman
578bde1e0d Security: healthcheck skill (#7641) (thanks @Takhoffman) 2026-02-02 20:36:58 -06:00
Tak Hoffman
e2c03845c7 Security: refine healthcheck workflow 2026-02-02 20:36:58 -06:00
Tak Hoffman
1523ef2494 Security: remove openclaw-system-admin skill path 2026-02-02 20:36:58 -06:00
Tak Hoffman
cdec53b22b Security: rename openclaw-system-admin skill to healthcheck 2026-02-02 20:36:58 -06:00
Tak Hoffman
a6afcb4c1d Security: new openclaw-system-admin skill + bootstrap audit 2026-02-02 20:36:58 -06:00
Gustavo Madeira Santana
2a68bcbeb3 feat(ui): add Agents dashboard 2026-02-02 21:31:17 -05:00
Aldo
c8af8e9555 Docs: clarify whats new FAQ heading (#7394) 2026-02-02 21:16:31 -05:00
cpojer
e77988f747 chore: Fix CI. 2026-02-03 10:25:32 +09:00
Peter Steinberger
96ad19a627 style(ui): format resizable divider 2026-02-02 17:01:17 -08:00
Peter Steinberger
fe81b1d712 fix(gateway): require shared auth before device bypass 2026-02-02 16:56:38 -08:00
Peter Steinberger
d1ecb46076 fix: harden exec allowlist parsing 2026-02-02 16:53:15 -08:00
Peter Steinberger
fff59da962 fix(slack): fail closed on slash command channel type lookup 2026-02-02 16:53:07 -08:00
cpojer
9e3ea2687c chore: Update deps. 2026-02-03 09:09:03 +09:00
Shakker
cfd6b21d0e fix: repair malformed tool calls and session transcripts (#7473) (thanks @justinhuangcode) 2026-02-02 23:56:27 +00:00
Shakker
118507953b Docs: simplify transcript hygiene scope 2026-02-02 23:56:27 +00:00
Shakker
befa421a57 Agents: flush pending tool results on drop 2026-02-02 23:56:27 +00:00
Shakker
e6fdac7bfb Agents: harden session file repair 2026-02-02 23:56:27 +00:00
Justin
67f90dae54 Agents: fix lint in tool-call sanitizers 2026-02-02 23:56:27 +00:00
Justin
31face5740 Changelog: note tool call repair 2026-02-02 23:56:27 +00:00
Justin
0da6de6624 Agent: repair malformed tool calls and session files 2026-02-02 23:56:27 +00:00
Tak Hoffman
0eae9f456c Docs: fix compatibility shim note 2026-02-02 17:22:22 -06:00
Shakker
561a10c491 fix(telegram): recover from grammY long-poll timeouts (#7466) (thanks @macmimi23) 2026-02-02 22:38:57 +00:00
mac mimi
c6b4de520a fix(telegram): recover from grammY "timed out" long-poll errors (#7239)
grammY getUpdates returns "Request to getUpdates timed out after 500 seconds"
but RECOVERABLE_MESSAGE_SNIPPETS only had "timeout". Since
"timed out".includes("timeout") === false, the error was not classified as
recoverable, causing the polling loop to exit permanently.

Add "timed out" to RECOVERABLE_MESSAGE_SNIPPETS so the polling loop retries
instead of dying silently.

Fixes #7239
Fixes #7255
2026-02-02 22:37:22 +00:00
Ji
f49297e2c1 fix: skip audio files from text extraction to prevent binary processing (#7475)
* fix: skip audio files from text extraction early

Audio files should not be processed through extractFileBlocks for text
extraction - they are handled by the dedicated audio transcription
capability (STT).

Previously, audio files were only skipped if they didn't "look like text"
(looksLikeUtf8Text check). This caused issues where some audio binary
data (e.g., long Telegram voice messages) could accidentally pass the
heuristic check and get processed as text content.

This fix:
1. Adds audio to the early skip alongside image/video (more efficient)
2. Removes the redundant secondary check that had the flawed condition

Fixes audio binary being incorrectly processed as text in Telegram and
other platforms.

* Media: skip binary media in file extraction (#7475) (thanks @AlexZhangji)

---------

Co-authored-by: Shakker <shakkerdroid@gmail.com>
2026-02-02 22:20:04 +00:00
bqcfjwhz85-arch
966228a6a9 fix(tools): ensure file_path alias passes validation in read/write tools (#7451)
Co-authored-by: lotusfall <lotusfall@outlook.com>
2026-02-02 21:33:36 +00:00
Shakker
5fb8f779ca fix: validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001) 2026-02-02 20:42:40 +00:00
Elarwei
88e29c728c refactor: use structural typing instead of instanceof for AbortSignal check
Address P1 review feedback from Greptile: instanceof AbortSignal may be
unreliable across different realms (VM, iframe, etc.) where the AbortSignal
constructor may differ. Use structural typing (checking for aborted property
and addEventListener method) for more robust cross-realm compatibility.
2026-02-02 20:42:40 +00:00
Elarwei
a63ec41a7b fix: validate AbortSignal instances before calling AbortSignal.any()
Fixes #7269
2026-02-02 20:42:40 +00:00
Tyler Yust
64849e81f5 feat(config): default thinking for sessions_spawn subagents (#7372)
* feat(config): add subagent default thinking

* fix: accept config subagents.thinking + stabilize test mocks (#7372) (thanks @tyler6204)

* fix: use findLast instead of clearAllMocks in test (#7372)

* fix: correct test assertions for tool result structure (#7372)

* fix: remove unnecessary type assertion after rebase
2026-02-02 12:14:17 -08:00
Shakker
d3bb32273e fix: resolve check errors in nodes-tool and commands-ptt 2026-02-02 20:05:17 +00:00
Mariano Belinky
7113dc21a9 Revert "Core: update shared gateway models"
This reverts commit 37eaca719a.
2026-02-02 17:36:49 +00:00
Mariano Belinky
4ab814fd50 Revert "iOS: wire node services and tests"
This reverts commit 7b0a0f3dac.
2026-02-02 17:36:49 +00:00
Josh Palmer
c83bdb73a4 Docs: expand zh-CN landing note 2026-02-02 18:35:01 +01:00
Josh Palmer
ea9eed14f8 Docs: add zh-CN landing note (#7303) (thanks @joshp123) 2026-02-02 18:35:01 +01:00
Josh Palmer
91e445c260 Docs: add zh-CN landing notice + AI image 2026-02-02 18:35:01 +01:00
Mariano Belinky
6cd3bc3a46 iOS: improve gateway auto-connect and voice permissions 2026-02-02 16:42:18 +00:00
Mariano Belinky
37eaca719a Core: update shared gateway models 2026-02-02 16:42:18 +00:00
Mariano Belinky
ff6114599e iOS: update onboarding and gateway UI 2026-02-02 16:42:18 +00:00
Mariano Belinky
532b9653be iOS: wire node commands and incremental TTS 2026-02-02 16:42:18 +00:00
Mariano Belinky
b7aac92ac4 Gateway: add PTT chat + nodes CLI 2026-02-02 16:42:18 +00:00
Mariano Belinky
1a48bce294 iOS: add PTT once/cancel 2026-02-02 16:42:18 +00:00
Mariano Belinky
17b18971f1 iOS: pause voice wake during PTT 2026-02-02 16:42:18 +00:00
Mariano Belinky
9f101d3a9a iOS: add push-to-talk node commands 2026-02-02 16:42:18 +00:00
Mariano Belinky
a884955cd6 iOS: add write commands for contacts/calendar/reminders 2026-02-02 16:42:18 +00:00
Mariano Belinky
f72ac60b01 iOS: streamline notify timeouts 2026-02-02 16:42:18 +00:00
Mariano Belinky
761188cd1d iOS: fix node notify and identity 2026-02-02 16:42:18 +00:00
Mariano Belinky
d9cadf9737 Agents: add nodes invoke action 2026-02-02 16:42:17 +00:00
Mariano Belinky
a4382607d7 Gateway: wait for snapshot before connect 2026-02-02 16:42:17 +00:00
Mariano Belinky
84e115834f Gateway: fix node invoke receive loop 2026-02-02 16:42:17 +00:00
Mariano Belinky
78f7e5147b iOS: stabilize talk mode tests 2026-02-02 16:42:17 +00:00
Mariano Belinky
7b0a0f3dac iOS: wire node services and tests 2026-02-02 16:42:17 +00:00
Shakker
3711143549 chore: fix formatting and CI 2026-02-02 16:41:49 +00:00
Shakker
777756e1c2 fix(webchat): respect user scroll position during streaming and refresh (thanks @marcomarandiz)
Merges #7226
2026-02-02 16:22:28 +00:00
Shakker
1b34446bf5 docs: update changelog for PR #7226 2026-02-02 16:19:40 +00:00
Shakker
13db0489c8 feat(ui): add new messages indicator button 2026-02-02 16:17:09 +00:00
Shakker
2af977f947 fix(ui): add core state and logic for scroll control 2026-02-02 16:17:01 +00:00
Josh Palmer
7cee8c2345 Docs: expand zh-Hans nav (#7242) (thanks @joshp123) 2026-02-02 17:07:34 +01:00
Josh Palmer
e0aa8457c2 Docs: expand zh-Hans nav and fix assets 2026-02-02 17:07:34 +01:00
Marco Marandiz
822388fe92 fix: address review feedback — retryDelay uses effectiveForce, default overrides param, @state() on chatNewMessagesBelow 2026-02-02 16:06:03 +00:00
Marco Marandiz
e18f43ddad fix(webchat): respect user scroll position during streaming and refresh
- Increase near-bottom threshold from 200px to 450px so one long message
  doesn't falsely register as 'near bottom'
- Make force=true only override on initial load (chatHasAutoScrolled=false),
  not on subsequent refreshChat() calls
- refreshChat() no longer passes force=true to scheduleChatScroll
- Add chatNewMessagesBelow flag for future 'scroll to bottom' button UI
- Clear chatNewMessagesBelow when user scrolls back to bottom
- Add 13 unit tests covering threshold, force behavior, streaming, and reset
2026-02-02 16:06:03 +00:00
Josh Palmer
991ed3ab58 Tests: stub SSRF DNS pinning (#6619) (thanks @joshp123) 2026-02-02 16:38:25 +01:00
Josh Palmer
5676a6b38d Docs: normalize zh-CN terminology + tone
What: switch to 你/你的 tone; standardize Skills/Gateway网关/local loopback/私信 wording
Why: align zh-CN docs with issue 6995 feedback + idiomatic tech style
Tests: pnpm docs:build
2026-02-02 16:38:25 +01:00
Josh Palmer
2b1f68c928 Docs i18n: tune zh-CN prompt + glossary
What: enforce zh-CN tone (你/你的), Skills/local loopback/Tailscale terms, Gateway网关
Why: keep future translation output consistent with issue feedback
Tests: not run (prompt/glossary change)
2026-02-02 16:38:25 +01:00
Josh Palmer
673583a38b Docs: use explicit ClawHub markdown link
What: switch clawhub.com reference to explicit Markdown link syntax
Why: MDX parser rejects angle-bracket autolinks
Tests: not run (doc text change)
2026-02-02 16:38:25 +01:00
Josh Palmer
b4cce3ac7a Docs: fix zh-CN ClawHub link
What: wrap clawhub.com in an explicit URL link in zh-CN skills doc
Why: avoid Mintlify broken-link parser treating trailing punctuation as part of the URL
Tests: not run (doc text change)
2026-02-02 16:38:25 +01:00
CLAWDINATOR Bot
4023b76ed3 docs: add changelog for zh-CN translations (#6619) (thanks @joshp123) 2026-02-02 16:38:25 +01:00
Josh Palmer
e9d117d221 Docs: fix zh-CN template time wording
What: replace <2/<30 text in zh-CN AGENTS template with safe wording
Why: avoid MDX parse errors during docs build
Tests: not run (doc text change)
2026-02-02 16:38:25 +01:00
Josh Palmer
149dc7c4e7 Docs: add zh-CN translations 2026-02-02 16:38:25 +01:00
Josh Palmer
e70984745b Docs i18n: harden doc-mode pipeline 2026-02-02 16:38:25 +01:00
Shadow
da9f28d270 CI: label maintainer issues 2026-02-02 09:26:46 -06:00
Christian Klotz
99b4f2a24e fix(telegram): handle Grammy HttpError network failures (#3815) (#7195)
* fix(telegram): handle Grammy HttpError network failures (#3815)

Grammy wraps fetch errors in an .error property (not .cause). Added .error
traversal to collectErrorCandidates in network-errors.ts.

Registered scoped unhandled rejection handler in monitorTelegramProvider
to catch network errors that escape the polling loop (e.g., from setMyCommands
during bot setup). Handler is unregistered when the provider stops.

* fix(telegram): address review feedback for Grammy HttpError handling

- Gate .error traversal on HttpError name to avoid widening search graph
- Use runtime logger instead of console.warn for consistency
- Add isGrammyHttpError check to scope unhandled rejection handler
- Consolidate isNetworkRelatedError into isRecoverableTelegramNetworkError
- Add 'timeout' to recoverable message snippets for full coverage
2026-02-02 15:25:41 +00:00
Peter Steinberger
f9fae2c439 fix: stabilize docker e2e flows 2026-02-02 13:11:55 +00:00
Peter Steinberger
9bd64c8a1f fix: expand SSRF guard coverage 2026-02-02 04:58:32 -08:00
cpojer
c429ccb64f chore: fix broken test. 2026-02-02 21:51:37 +09:00
Peter Steinberger
57d008a33d fix(update): harden global updates 2026-02-02 04:45:14 -08:00
cpojer
6b0d6e2540 chore: We have a sleep at home. The sleep at home: 2026-02-02 21:44:02 +09:00
Peter Steinberger
dfef943f0a fix: polish docker setup flow 2026-02-02 04:26:03 -08:00
Peter Steinberger
39c682219e test: cover SSRF blocking for attachment URLs 2026-02-02 04:21:10 -08:00
Ayaan Zaidi
66307695eb fix: start gateway in docker CMD (#6635) (thanks @kaizen403) 2026-02-02 17:38:37 +05:30
Ayaan Zaidi
d134a8c7f3 docs: note docker allow-unconfigured behavior 2026-02-02 17:38:37 +05:30
Rishi Vhavle
bb3d7343f4 fix(docker): remove --bind lan from default CMD to work out of the box
Addresses review feedback: --bind lan requires auth token, so default
CMD should bind to loopback only.

For container platforms needing LAN binding for health checks:
1. Set OPENCLAW_GATEWAY_TOKEN env var
2. Override CMD: ["node","dist/index.js","gateway","--allow-unconfigured","--bind","lan"]
2026-02-02 17:38:37 +05:30
Rishi Vhavle
1a05ee941e fix(docker): add gateway subcommand and cloud-compatible flags
The Dockerfile CMD runs without arguments, causing the CLI to print
help and exit with code 1. This breaks deployment on container
platforms (Render, Railway, Fly.io, etc.) that rely on the CMD.

Changes:
- Add `gateway` subcommand to start the server
- Add `--allow-unconfigured` to allow startup without config file
- Add `--bind lan` to bind to 0.0.0.0 instead of localhost
  (required for container health checks)

Fixes #5685
2026-02-02 17:38:37 +05:30
Peter Steinberger
1b3f987c4d style: format fetch guard signatures 2026-02-02 04:07:29 -08:00
Peter Steinberger
81c68f582d fix: guard remote media fetches with SSRF checks 2026-02-02 04:07:29 -08:00
Peter Steinberger
d842b28a15 docs: update appcast for 2026.2.1 2026-02-02 03:53:21 -08:00
Peter Steinberger
ed4529e246 docs: update 2026.2.1 changelog 2026-02-02 03:25:32 -08:00
Peter Steinberger
bf08b485bd fix: satisfy tool adapter lint 2026-02-02 03:14:34 -08:00
Peter Steinberger
845d97b6a5 fix: handle legacy tool execute signatures 2026-02-02 02:51:52 -08:00
Peter Steinberger
8b64705e05 docs: fold 2026.2.2 into 2026.2.1 2026-02-02 02:43:54 -08:00
Peter Steinberger
bcb0ed0866 fix: normalize tool execute args 2026-02-02 02:41:21 -08:00
Peter Steinberger
9ae1b732ef fix: align tool definition adapter 2026-02-02 02:28:22 -08:00
Peter Steinberger
385e66cbd5 Docs: expand ClawHub overview 2026-02-02 02:26:11 -08:00
Peter Steinberger
2d317ce423 fix: align tool execute parameter order 2026-02-02 10:20:13 +00:00
Peter Steinberger
284d24209b fix: align tool execute signature 2026-02-02 10:14:29 +00:00
Peter Steinberger
b8174decf3 fix: resolve system prompt overrides 2026-02-02 02:10:13 -08:00
Peter Steinberger
41cc5bcd4f fix: gate Teams media auth retries 2026-02-02 02:08:13 -08:00
Peter Steinberger
f6d98a908a docs: add changelog entry for plugin install hardening 2026-02-02 02:07:47 -08:00
Peter Steinberger
d03eca8450 fix: harden plugin and hook install paths 2026-02-02 02:07:47 -08:00
Peter Steinberger
be9a2fb134 docs: clarify docker power-user setup 2026-02-02 02:07:08 -08:00
Tyler Yust
8d2f98fb01 Fix subagent announce failover race (always emit lifecycle end + treat timeout=0 as no-timeout) (#6621)
* Fix subagent announce race and timeout handling

Bug 1: Subagent announce fires before model failover retries finish
- Problem: CLI provider emitted lifecycle error on each attempt, causing
  subagent registry to prematurely call beginSubagentCleanup() and announce
  with incorrect status before failover retries completed
- Fix: Removed lifecycle error emission from CLI provider's attempt-level
  .catch() in agent-runner-execution.ts. Errors still propagate to
  runWithModelFallback for retry, but no intermediate lifecycle events
  are emitted. Only the final outcome (after all retries) emits lifecycle
  events.

Bug 2: Hard 600s per-prompt timeout ignores runTimeoutSeconds=0
- Problem: When runTimeoutSeconds=0 (meaning 'no timeout'), the code
  returned the default 600s timeout instead of respecting the 0 setting
- Fix: Modified resolveAgentTimeoutMs() to treat 0 as 'no timeout' and
  return a very large timeout value (30 days) instead of the default.
  This avoids setTimeout issues with Infinity while effectively providing
  unlimited time for long-running tasks.

* fix: emit lifecycle:error for CLI failures (#6621) (thanks @tyler6204)

* chore: satisfy format/lint gates (#6621) (thanks @tyler6204)

* fix: restore build after upstream type changes (#6621) (thanks @tyler6204)

* test: fix createSystemPromptOverride tests to match new return type (#6621) (thanks @tyler6204)
2026-02-02 02:06:14 -08:00
Peter Steinberger
d5f6caba3f docs: merge 2026.2.2 changelog into 2026.2.1 2026-02-02 01:31:24 -08:00
Peter Steinberger
4682c2e3e2 docs: add ClawHub registry overview 2026-02-02 01:26:29 -08:00
Peter Steinberger
34dd7324d9 fix: restore lint/build gates 2026-02-02 01:25:40 -08:00
Tyler Yust
9ef24fd400 fix: flush block streaming on paragraph boundaries for chunkMode=newline (#7014)
* feat: Implement paragraph boundary flushing in block streaming

- Added `flushOnParagraph` option to `BlockReplyChunking` for immediate flushing on paragraph breaks.
- Updated `EmbeddedBlockChunker` to handle paragraph boundaries during chunking.
- Enhanced `createBlockReplyCoalescer` to support flushing on enqueue.
- Added tests to verify behavior of flushing with and without `flushOnEnqueue` set.
- Updated relevant types and interfaces to include `flushOnParagraph` and `flushOnEnqueue` options.

* fix: Improve streaming behavior and enhance block chunking logic

- Resolved issue with stuck typing indicator after streamed BlueBubbles replies.
- Refactored `EmbeddedBlockChunker` to streamline fence-split handling and ensure maxChars fallback for newline chunking.
- Added tests to validate new chunking behavior, including handling of paragraph breaks and fence scenarios.
- Updated changelog to reflect these changes.

* test: Add test for clamping long paragraphs in EmbeddedBlockChunker

- Introduced a new test case to verify that long paragraphs are correctly clamped to maxChars when flushOnParagraph is enabled.
- Updated logic in EmbeddedBlockChunker to handle cases where the next paragraph break exceeds maxChars, ensuring proper chunking behavior.

* refactor: streamline logging and improve error handling in message processing

- Removed verbose logging statements from the `processMessage` function to reduce clutter.
- Enhanced error handling by using `runtime.error` for typing restart failures.
- Updated the `applySystemPromptOverrideToSession` function to accept a string directly instead of a function, simplifying the prompt application process.
- Adjusted the `runEmbeddedAttempt` function to directly use the system prompt override without invoking it as a function.
2026-02-02 01:22:41 -08:00
Peter Steinberger
85cd55e22b chore: bump to 2026.2.1 2026-02-02 08:51:54 +00:00
David Iach
4e4ed2ea17 fix(security): cap Slack media downloads and validate Slack file URLs (#6639)
* Security: cap Slack media downloads and validate Slack file URLs

* Security: relax web media fetch cap for compression

* Fixes: sync pi-coding-agent options

* Fixes: align system prompt override type

* Slack: clarify fetchImpl assumptions

* fix: respect raw media fetch cap (#6639) (thanks @davidiach)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-02 00:48:07 -08:00
Peter Steinberger
521b121815 fix: treat '*' tool allowlist as valid 2026-02-02 08:45:51 +00:00
vignesh07
e74235fdce ci(formal): compute drift for generated/ before model checking 2026-02-02 00:43:28 -08:00
vignesh07
f37b79cf4f ci(formal): add routing-trirule + proxy-header-spoof targets 2026-02-02 00:43:28 -08:00
vignesh07
889480cef9 ci(formal): include latest reliability/conformance model targets 2026-02-02 00:43:28 -08:00
Ayaan Zaidi
01449a2f41 fix: add telegram download timeouts (#6914) (thanks @hclsys) 2026-02-02 13:39:39 +05:30
chenglun.hu
d46b489e21 fix(telegram): add timeout to file download to prevent DoS (CWE-400)
Add AbortSignal.timeout() to both fetch calls in download.ts to prevent
indefinite hangs when Telegram API is slow or unresponsive.

- getTelegramFile(): 30s timeout for metadata API call
- downloadTelegramFile(): 60s timeout for file download

Both functions now accept optional timeoutMs parameter for configurability.

Fixes #6849

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 13:39:39 +05:30
cpojer
935a0e5708 chore: Enable typescript/no-explicit-any rule. 2026-02-02 16:18:09 +09:00
cpojer
baa1e95b9d chore: Enable no-unnecessary-template-expression lint rule. 2026-02-02 15:37:05 +09:00
cpojer
87a61c3b88 chore: Re-enable no-redundant-type-constituents rule. 2026-02-02 15:32:03 +09:00
cpojer
e9a32b83c2 chore: Manually fix lint issues in ui. 2026-02-02 15:23:36 +09:00
cpojer
5ba4586e58 chore: lint the ui folder. 2026-02-02 15:08:47 +09:00
Ayaan Zaidi
e25f8ed56c fix: add changelog for telegram thread spec (#6833) (thanks @obviyus) 2026-02-02 09:26:59 +05:30
Ayaan Zaidi
0bc8a592a6 fix: inline telegram thread scope type 2026-02-02 09:26:59 +05:30
Ayaan Zaidi
1d7dd5f261 fix: require thread specs for telegram sends 2026-02-02 09:26:59 +05:30
Ayaan Zaidi
19b8416a81 fix: unify telegram thread handling 2026-02-02 09:26:59 +05:30
Sk Akram
5020bfa2a9 fix: L2-normalize local embedding vectors to fix semantic search (#5332)
* fix: L2-normalize local embedding vectors to fix semantic search

* fix: handle non‑finite magnitude in L2 normalization and remove stale test reset

* refactor: add braces to l2Normalize guard clause in embeddings

* fix: sanitize local embeddings (#5332) (thanks @akramcodez)

---------

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-01 22:56:44 -05:00
Seb Slight
b9910ab037 Docs: fix Moonshot sync markers (#6789)
* Docs: fix Moonshot sync markers

* Docs: use MDX comment markers for Moonshot sync

* Docs: use markdown comment markers for Moonshot sync

* Docs: hide Moonshot sync markers in MDX
2026-02-02 03:38:14 +01:00
cpojer
902f968056 chore: Add pnpm check for fast repo checks. 2026-02-02 11:16:13 +09:00
cpojer
bd259eeb23 chore: Update deps. 2026-02-02 11:11:12 +09:00
Tyler Yust
476f367cf1 Gateway: avoid writing host config in tools invoke test 2026-02-01 17:19:23 -08:00
Mario Zechner
dda8a2b238 fix: format docs 2026-02-02 02:08:24 +01:00
Mario Zechner
7ee99af9f8 fix: convert HTML comments to MDX comments in docs 2026-02-02 02:05:02 +01:00
Mario Zechner
4347d2468c fix: format issues and lint error in oauth.ts 2026-02-02 01:59:42 +01:00
Mario Zechner
cf1d3f7a7c fix: update pi packages to 0.51.0, remove bogus type augmentation
- Update @mariozechner/pi-agent-core, pi-ai, pi-coding-agent, pi-tui to 0.51.0
- Delete src/types/pi-coding-agent.d.ts (declared additionalExtensionPaths which SDK never supported)
- Fix ToolDefinition.execute signature (parameter order changed in 0.51.0)
- Remove dead additionalExtensionPaths from createAgentSession calls
2026-02-02 01:52:33 +01:00
Sebastian
0fa55ed2b4 Docs: update clawtributors 2026-02-01 19:51:48 -05:00
Sebastian
63c9fac9fc Docs: clarify node host SSH tunnel flow
Co-authored-by: Dmytro Semchuk <x0m4ek@users.noreply.github.com>
2026-02-01 19:50:33 -05:00
Peter Steinberger
8c7901c984 fix(twitch): enforce allowFrom allowlist 2026-02-02 00:16:35 +00:00
Peter Steinberger
aa2eb48b9c fix: align pi-coding-agent typings and docs 2026-02-01 16:08:01 -08:00
Peter Steinberger
7aeabbabd4 fix: refine oauth provider guard 2026-02-01 15:52:56 -08:00
Peter Steinberger
e58291e070 fix: align embedded runner with pi-coding-agent API 2026-02-01 15:51:46 -08:00
hcl
411d5fda58 fix(tlon): add timeout to SSE client fetch calls (CWE-400) (#5926)
Add timeout protection to prevent indefinite hangs when Urbit server
becomes unresponsive or network partition occurs.

Changes:
- Add AbortSignal.timeout(30_000) to 7 one-shot fetch calls
- Add AbortController with 60s connection timeout to SSE stream fetch
  (clears timeout after headers received to avoid aborting active stream)

Affected methods: sendSubscription, connect, openStream, poke, scry, close

Fixes #5266

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:40:27 -08:00
Peter Steinberger
19775abdda fix: clean up plugin linting and types 2026-02-01 15:38:32 -08:00
Peter Steinberger
a87a07ec8a fix: harden host exec env validation (#4896) (thanks @HassanFleyah) 2026-02-01 15:37:19 -08:00
Hasan FLeyah
0a5821a811 fix(security): enforce strict environment variable validation in exec tool (#4896) 2026-02-01 15:36:24 -08:00
VACInc
b796f6ec01 Security: harden web tools and file parsing (#4058)
* feat: web content security wrapping + gkeep/simple-backup skills

* fix: harden web fetch + media text detection (#4058) (thanks @VACInc)

---------

Co-authored-by: VAC <vac@vacs-mac-mini.localdomain>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-01 15:23:25 -08:00
Peter Steinberger
92112a61db chore: add TLS 1.3 minimum changelog (#5970) (thanks @loganaden) 2026-02-01 15:14:55 -08:00
Loganaden Velvindron
a2b00495cd require TLS 1.3 as minimum
TLS 1.2 is not getting any protocol update anytime soon.
https://www.ietf.org/archive/id/draft-ietf-tls-tls12-frozen-08.html
2026-02-01 15:14:11 -08:00
Tyler Yust
f8575c401c feat: update chat layout and session management
- Added max-width to chat controls and session select for better layout.
- Increased CHAT_SESSIONS_ACTIVE_MINUTES from 10 to 120 for extended session duration.
- Changed brand logo source to a local favicon for improved asset management.
2026-02-01 15:09:56 -08:00
Peter Steinberger
3367b2aa27 fix: align embedded runner with session API changes 2026-02-01 15:06:55 -08:00
Tyler Yust
bcbb447357 feat: extend CreateAgentSessionOptions with new properties
- Added systemPrompt for overriding the default system prompt.
- Introduced skills for pre-loaded skills management.
- Added contextFiles for handling pre-loaded context files with path and content attributes.
2026-02-01 14:53:33 -08:00
Peter Steinberger
8eb11bd304 fix: wire before_tool_call hook into tool execution (#6570) (thanks @ryancnelson) (#6660) 2026-02-01 14:52:11 -08:00
Ryan Nelson
6c6f1e9660 Fix missing before_tool_call hook integration (#6570)
* Fix missing before_tool_call hook integration

- Add hook call in handleToolExecutionStart before tool execution begins
- Support parameter modification via hookResult.params
- Support tool call blocking via hookResult.block with custom blockReason
- Fix try/catch logic to properly re-throw blocking errors using __isHookBlocking flag
- Maintain tool event consistency by emitting start/end events when blocked
- Addresses GitHub issue #6535 (1 of 8 unimplemented hooks now working)

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>

* Add comprehensive test suite for before_tool_call hook

- 9 tests covering all hook scenarios: no hooks, parameter passing, modification, blocking, error handling
- Tests tool name normalization and different argument types
- Verifies proper error re-throwing and logging behavior
- Maintained in fork for regression testing

* Fix all issues identified by Greptile code review

Address P0/P1/P3 bugs:

P0 - Fix parameter mutation crash for non-object args:
- Normalize args to objects before passing to hooks (maintains hook contract)
- Handle parameter merging safely for both object and non-object args

P1 - Add missing internal state updates when blocking tools:
- Set toolMetaById metadata like normal flow
- Call onAgentEvent callback to maintain consistency
- Emit events in same order as normal tool execution

P1 - Fix test expectations to match implementation reality:
- Non-object args normalized to {} for hook params (not passed as-is)
- Add test for safe parameter modification with various arg types
- Update mocks to verify state updates when blocking

P3 - Replace magic __isHookBlocking property with dedicated ToolBlockedError class:
- More robust error handling without property collision risk
- Cleaner control flow that's serialization-safe

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4 <noreply@anthropic.com>
2026-02-01 14:49:14 -08:00
Peter Steinberger
e550e252a7 Revert "fix: override request dependency"
This reverts commit e4d572192d.
2026-02-01 14:35:04 -08:00
Peter Steinberger
e4d572192d fix: override request dependency 2026-02-01 14:33:45 -08:00
Peter Steinberger
2601f413c3 fix: override vulnerable transitive deps 2026-02-01 14:33:45 -08:00
Leszek Szpunar
1bdd9e313f security(web): sanitize WhatsApp accountId to prevent path traversal (#4610)
* security(web): sanitize WhatsApp accountId to prevent path traversal

Apply normalizeAccountId() from routing/session-key to
resolveDefaultAuthDir() so that malicious config values like
"../../../etc" cannot escape the intended auth directory.

Fixes #2692

* fix(web): check sanitized segment instead of full path in Windows test

* style(web): fix oxfmt formatting in accounts test
2026-02-01 14:29:53 -08:00
Peter Steinberger
9d2784cdb9 test: speed up telegram suites 2026-02-01 22:23:16 +00:00
Peter Steinberger
bcde2fca5a fix: align embedded agent session setup 2026-02-01 22:23:16 +00:00
Leszek Szpunar
9b6fffd00a security(message-tool): validate filePath/path against sandbox root (#6398)
* security(message-tool): validate filePath/path against sandbox root

* style: translate Polish comments to English for consistency
2026-02-01 14:19:09 -08:00
Peter Steinberger
99346314f5 chore: trim docs changelog 2026-02-01 14:17:47 -08:00
Peter Steinberger
1968a4b7d2 chore: expand changelog 2026-02-01 14:12:39 -08:00
Peter Steinberger
a68e32d95b chore: update changelog 2026-02-01 14:08:09 -08:00
Peter Steinberger
6360809310 style: format extension relay imports 2026-02-01 14:03:57 -08:00
Peter Steinberger
238200f652 chore: update changelog and relay formatting 2026-02-01 14:03:57 -08:00
Justin Ling
d54605bd82 docs: improve exe.dev setup instructions (#4675)
* improve exe.dev setup instructions

1. Fix device approval command
2. Clarify where Gateway token can be found

* Update device approval instructions in exe-dev.md

Clarify instructions for approving devices in OpenClaw.
2026-02-01 15:46:31 -05:00
CLAWDINATOR Bot
92803facf6 docs: preserve moonshot sync markers 2026-02-01 20:34:42 +00:00
CLAWDINATOR Bot
443ee26af3 chore: oxfmt fixes 2026-02-01 20:34:42 +00:00
Christian Klotz
395810a60b chore: fix Pi prompt template argument syntax (#6543)
- Fix @1 -> $1 in landpr.md
- Fix $@ -> $1 in reviewpr.md
- Remove stray /reviewpr line from reviewpr.md
- Delete old pr.md (replaced by reviewpr.md and landpr.md)
2026-02-01 20:14:18 +00:00
Sebastian
6c03fe1a4d Docs: update clawtributors 2026-02-01 15:04:42 -05:00
Sebastian
a863ac9862 Docs: clarify Moonshot endpoints (#4763)
Co-authored-by: hansbbans <hansbbans@users.noreply.github.com>
2026-02-01 15:03:55 -05:00
Seb Slight
8f366babe4 docs(discord): clarify exec approvals UI (#6550)
* docs(discord): clarify exec approvals UI

* Add link for slash command in Discord exec approvals

Updated documentation to include a link for the slash command used in Discord exec approvals.

* docs(discord): move exec approvals note

* docs(discord): document exec approvals config

* docs(discord): reorder exec approvals config

---------

Co-authored-by: Luke K (pr-0f3t) <2609441+lc0rp@users.noreply.github.com>
2026-02-01 14:43:54 -05:00
Peter Steinberger
083ec9325e fix: cover OpenRouter attribution headers 2026-02-01 19:30:33 +00:00
Alex Atallah
74039fc0f1 Add openrouter attribution headers 2026-02-01 19:24:55 +00:00
CLAWDINATOR Bot
17287bc8d0 docs: add zh-CN titles (#6487) (thanks @joshp123) 2026-02-01 18:21:15 +00:00
Josh Palmer
964b14d59c Docs: add zh-CN titles 2026-02-01 18:21:15 +00:00
bonald
d3e53eaf27 fix(skill): update session-logs paths from .clawdbot to .openclaw (#4502)
Co-authored-by: Jarvis <jarvis@openclaw.ai>
Co-authored-by: CLAWDINATOR Bot <clawdinator[bot]@users.noreply.github.com>
Co-authored-by: Shadow <shadow@openclaw.ai>
2026-02-01 11:42:28 -06:00
Shadow
20a603de01 Update auto-response messages with new links 2026-02-01 11:25:55 -06:00
CLAWDINATOR Bot
6453f5c395 docs: add Mintlify language navigation (#6416) (thanks @joshp123) 2026-02-01 17:20:44 +00:00
Josh Palmer
3cf35b0710 Docs: add Mintlify language navigation 2026-02-01 17:20:44 +00:00
CLAWDINATOR Bot
bc5b0c82ac fix(docker): avoid using host port in gateway command (#5110) (thanks @mise42) 2026-02-01 17:17:18 +00:00
baccula
63b13c7e2f docs: add device pairing section to Control UI docs (#5003)
* docs: add device pairing section to Control UI docs

Explains that new browser connections require one-time pairing approval,
what error message users will see, and how to approve devices using the
CLI. This was a gap in the documentation that caused confusion for users
connecting via Tailscale Serve.

* docs: clarify Control UI pairing error

* docs: clarify device revoke flags

---------

Co-authored-by: Lucifer (via OpenClaw) <lucy@neuwirth.cc>
Co-authored-by: Sebastian <sebslight@gmail.com>
2026-02-01 11:03:55 -05:00
shatner
8ff75eaf12 Docs: Direct link to BotFather on Telegram (#4064)
* Docs: Direct link to BotFather on Telegram, sparing users from searching and potentially encountering impostors.

* Update numbering syntax

Update numbering syntax to match PR to latest doc layout.

* Docs: add BotFather verification note

---------

Co-authored-by: Sebastian <sebslight@gmail.com>
2026-02-01 10:15:40 -05:00
Ozgur Polat
76211500e8 docs: fix heading numbering and add missing section onboarding.md (#3461) 2026-02-01 10:09:05 -05:00
Dan Ballance
28a05f9940 Docs: Fix typo in docs/tools/skills.md (#3050) 2026-02-01 10:05:46 -05:00
Glucksberg
701d43892f docs(skills): update canvas URL prefix to /__openclaw__/ (#4729)
Update remaining __moltbot__ references in canvas skill documentation
to match the CANVAS_HOST_PATH constant (/__openclaw__/canvas).
2026-02-01 10:01:51 -05:00
sfo2001
3ae049b501 docs(install): add pnpm approve-builds step for global installs (#5663)
* docs(install): add pnpm approve-builds step for global installs

pnpm requires explicit approval for packages with build scripts.
Without running `pnpm approve-builds -g`, openclaw and its dependencies
(node-llama-cpp, sharp, protobufjs) won't have their postinstall scripts
executed, causing runtime errors.

Fixes #5579

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(install): clarify pnpm reinstall step after approve-builds

Address review feedback: after running `pnpm approve-builds -g`,
users need to re-run the install command for postinstall scripts
to actually execute.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 09:36:19 -05:00
Ayaan Zaidi
e9f70e8585 fix: satisfy lint curly rule (#6310)
* fix: satisfy lint curly rule

* docs: apply oxfmt formatting
2026-02-01 20:04:53 +05:30
Seb Slight
8582ed4d4f Docs: fix Moonshot MDX comment marker (#6311) 2026-02-01 09:28:25 -05:00
Eric Su
7fabe03a8b docs: fix anchor link for Google Vertex/Antigravity/Gemini section (#5967)
* docs: fix anchor link for Google Vertex/Antigravity/Gemini section

* Docs: fix model provider MDX markers

---------

Co-authored-by: Sebastian <sebslight@gmail.com>
2026-02-01 09:27:31 -05:00
Ayaan Zaidi
0992c5a809 fix: cap context window resolution (#6187) (thanks @iamEvanYT) 2026-02-01 19:52:56 +05:30
Evan
5d3c898a94 fix: update compaction safeguard to respect context window tokens 2026-02-01 19:52:56 +05:30
Josh Palmer
0e0e395b9e Docs: add zh-CN entrypoint translations (#6300)
* Docs: add zh-CN entrypoint translations

* Docs: harden docs-i18n parsing
2026-02-01 15:22:05 +01:00
Kimitaka Watanabe
7a8a39a141 docs: document cacheRetention parameter (#6270)
* docs: document cacheRetention parameter (#6240)

* docs: standardize cacheRetention value quoting style

* style: format anthropic.md table

* Docs: align cacheRetention inline example

---------

Co-authored-by: Sebastian <sebslight@gmail.com>
2026-02-01 09:16:37 -05:00
clawdinator[bot]
b897389b87 fix: friendlier Windows onboarding message (#6242)
Co-authored-by: CLAWDINATOR <clawdinator@openclaw.ai>
Co-authored-by: Scott Hanselman <scott@hanselman.com>
2026-02-01 12:55:22 +00:00
Peter Steinberger
a1e89afcc1 fix: secure chrome extension relay cdp 2026-02-01 02:25:14 -08:00
Peter Steinberger
e4f7155369 fix(ci): repair lint/build checks 2026-02-01 10:20:27 +00:00
vignesh07
9d9378436b ci(formal): fix formal models checkout repo (vignesh07/clawdbot-formal-models) 2026-02-01 01:59:16 -08:00
vignesh07
141dc1af4b ci(formal): checkout formal models from canonical repo main 2026-02-01 01:59:16 -08:00
vignesh07
c83c19d9cd ci(formal): run TLC model suite (green) + negative suite (non-blocking) 2026-02-01 01:59:16 -08:00
Vignesh
35dc417b18 agents: add tool policy conformance snapshot (no runtime behavior change) (#6011) 2026-02-01 01:57:49 -08:00
Ayaan Zaidi
1f3afa38e8 fix: use shared pairing store for telegram (#6127) (thanks @obviyus) 2026-02-01 15:22:37 +05:30
Ayaan Zaidi
633f848481 fix: use telegram user id for pairing request 2026-02-01 15:22:37 +05:30
Ayaan Zaidi
24fbafa9a7 refactor: use shared pairing store for telegram 2026-02-01 15:22:37 +05:30
Armin Ronacher
ca92597e1f Merge commit from fork 2026-02-01 10:43:54 +01:00
Mario Zechner
c621c80afc fix(tui): prevent crash when searching with digits in model selector
highlightMatch() was replacing tokens inside ANSI escape codes,
corrupting sequences like [38;2;123;127;135m when searching for '2'.
Fix: apply highlighting to plain text before theme styling.
2026-02-01 09:50:57 +01:00
Mario Zechner
ba4a55f6d9 fix(agents): update cacheControlTtl to cacheRetention for pi-ai 0.50.9
- Update @mariozechner/pi-ai and pi-agent-core to 0.50.9
- Rename cacheControlTtl to cacheRetention with values none/short/long
- Add backwards compatibility mapping: 5m->short, 1h->long
- Remove dead OpenRouter check (uses openai-completions API)
- Default new configs to cacheRetention: short
2026-02-01 09:50:52 +01:00
xiaose
511b2c91e3 feat: mr 2026-02-01 01:11:22 -05:00
cpojer
b48d72a2b8 chore: fix lint, and format after lint to catch reformats triggered by autofixes. 2026-02-01 13:19:06 +09:00
bravostation
b4e2e746b3 /new: use agent personality in session greeting (#5706)
* Slash new: use agent personality in session greeting

Previously /new and /reset used a generic greeting prompt. Agents with
personality files (IDENTITY.md, SOUL.md, etc) would respond out of
character until the conversation got going.

Now the prompt instructs the agent to greet users as their character,
using their defined voice, mannerisms, and mood from the start.

* Auto-reply: avoid workspace references in reset prompt

* fix: avoid workspace references in reset greeting (#5706) (thanks @bravostation)

---------

Co-authored-by: MoltBot <bot@moltbot.com>
Co-authored-by: Shadow <shadow@clawd.bot>
2026-01-31 20:43:19 -06:00
Tak Hoffman
3d5c03ec29 fix: resolve Windows npm spawn ENOENT (#5815) (thanks @thejhinvirtuoso) 2026-01-31 20:39:33 -06:00
Jhin
dc8a63cb8b fix: skip extension append if command already has one
Addresses review feedback - now checks path.extname() before
appending .cmd to avoid producing invalid paths like npm.cmd.cmd
2026-01-31 20:39:33 -06:00
Jhin
5c8880ed3f fix(process): resolve npm/pnpm spawn ENOENT on Windows
On Windows, non-.exe commands like npm, pnpm, yarn, npx require
their .cmd extension when using spawn(). This adds a resolveCommand()
helper that automatically appends .cmd on Windows for these commands.

Fixes #5773
2026-01-31 20:39:33 -06:00
Lalit Singh
01d76e4799 feat(routing): add thread parent binding inheritance for Discord (#3892)
* feat(routing): add thread parent binding inheritance for Discord

When a Discord thread message doesn't match a direct peer binding,
now checks if the parent channel has a binding and uses that agent.

This enables multi-agent setups where threads inherit their parent
channel's agent binding automatically.

Changes:
- Add parentPeer parameter to ResolveAgentRouteInput
- Add binding.peer.parent match type
- Resolve thread parent early in Discord preflight
- Pass parentPeer to resolveAgentRoute for threads

Fixes thread routing in Discord multi-agent configurations where
threads were incorrectly routed to the default agent instead of
inheriting from their parent channel's binding.

* ci: trigger fresh macOS runners

* Discord: inherit thread bindings in reactions

* fix: add changelog for thread parent binding (#3892) (thanks @aerolalit)

---------

Co-authored-by: Lalit Singh <lalit@clawd.bot>
Co-authored-by: OSS Agent <oss-agent@clawdbot.ai>
Co-authored-by: Shadow <shadow@clawd.bot>
2026-01-31 20:30:45 -06:00
Tak Hoffman
a393ae79d2 Merge pull request #3677 from conroywhitney/fix/1897-session-status-time-hint
fix(system-prompt): hint session_status for date/time instead of embedding it
2026-01-31 20:22:41 -06:00
Shadow
abcca0f9bd Discord: fix PK sender identity context 2026-01-31 20:20:17 -06:00
Gustavo Madeira Santana
73c405f74a Docs: note MiniMax OAuth updates (#5402) (thanks @Maosghoul) 2026-01-31 21:08:56 -05:00
xiaose
1f6a446f6c feat: note 2026-01-31 21:08:56 -05:00
xiaose
b2aff036ad feat: code 2026-01-31 21:08:56 -05:00
cpojer
58f4185925 fix: Failing tests due to import sorting. 2026-02-01 11:05:46 +09:00
jonisjongithub
96c9ffdedc docs: fix Venice AI typo (Venius → Venice)
Co-authored-by: jonisjongithub <jonisjongithub@users.noreply.github.com>

Co-authored-by: Clawdbot <bot@clawd.bot>
2026-01-31 21:04:37 -05:00
Tak Hoffman
1aeaf811b0 Merge branch 'main' into fix/1897-session-status-time-hint 2026-01-31 19:58:05 -06:00
Shadow
8e2b17e0c5 Discord: add PluralKit sender identity resolver (#5838)
* Discord: add PluralKit sender identity resolver

* fix: resolve PluralKit sender identities (#5838) (thanks @thewilloftheshadow)
2026-01-31 19:50:06 -06:00
Aldo
66e33abd7b Docs: mention weak gateway auth tokens 2026-01-31 20:48:00 -05:00
Shadow
29de43d307 CI: auto-label moltbook issues 2026-01-31 19:45:48 -06:00
Stefan Förster
a10603f9f0 fix(docs): remove invalid channels. prefix from Discord URL 2026-01-31 20:40:59 -05:00
Vignesh
7387bc574f Merge pull request #5807 from openclaw/ci/formal-conformance-alias-check 2026-01-31 17:39:53 -08:00
Stefan Förster
bce8c0eb12 fix(docs): update MiniMax plugin URL from legacy moltbot org 2026-01-31 20:36:21 -05:00
Stefan Förster
7a2c4d3cf1 fix(docs): use canonical openclaw.ai domain instead of openclaw.bot 2026-01-31 20:34:51 -05:00
Stefan Förster
9297ea48e5 fix(docs): update Twitter URLs to X for consistency 2026-01-31 20:33:19 -05:00
cpojer
147eba11fd chore: Manually fix TypeScript errors uncovered by sorting imports.
Some TypeScript checks are order dependent, and the fixed types were `any`/`unknown`, TypeScript just didn't report it before for some reason.
2026-02-01 10:24:09 +09:00
cpojer
f06dd8df06 chore: Enable "experimentalSortImports" in Oxfmt and reformat all imorts. 2026-02-01 10:03:47 +09:00
vignesh07
367372f526 ci: run formal model alias consistency check 2026-01-31 16:52:15 -08:00
Vignesh
ad943bd8cf Merge pull request #5723 from openclaw/ci/formal-conformance
CI: informational formal models conformance (clawdbot-formal-models)
2026-01-31 14:23:25 -08:00
vignesh07
baf9505bfd ci: add informational formal models conformance check 2026-01-31 14:06:17 -08:00
Seb Slight
e6c38e078a Docs: point nav groups at index pages (#5694) 2026-01-31 15:08:39 -06:00
Seb Slight
abcaa8c7a9 Docs: add nav titles across docs (#5689) 2026-01-31 15:04:03 -06:00
Vignesh
1295b67057 fix(lobster): block arbitrary exec via lobsterPath/cwd (GHSA-4mhr-g7xj-cg8j) (#5335)
* fix(lobster): prevent arbitrary exec via lobsterPath/cwd

* fix(lobster): harden lobsterPath errors + normalize cwd sandboxing

* fix(lobster): ignore tool-provided lobsterPath; validate + use plugin config

* fix(lobster): use plugin config lobsterPath + add tests (#5335) (thanks @vignesh07)

* fix(lobster): make Windows spawn fallback handle ENOENT (#5335) (thanks @vignesh07)

---------

Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-01-31 12:46:20 -08:00
Glucksberg
34e2425b4d fix(security): restrict MEDIA path extraction to prevent LFI (#4930)
* fix(security): restrict inbound media staging to media directory

* docs: update MEDIA path guidance for security restrictions

- Update agent hint to warn against absolute/~ paths
- Update docs example to use https:// instead of /tmp/

---------

Co-authored-by: Evan Otero <evanotero@google.com>
2026-01-31 10:55:37 -08:00
Ayaan Zaidi
f1de88c198 fix: restore telegram draft streaming partials (#5543) (thanks @obviyus) 2026-01-31 22:46:19 +05:30
Ayaan Zaidi
57ea4e8897 docs: format cron jobs doc 2026-01-31 22:46:19 +05:30
Ayaan Zaidi
b5c2b1880d fix: stabilize partial streaming filters 2026-01-31 22:46:19 +05:30
Ayaan Zaidi
a64d8d2d66 fix: harden telegram streaming state 2026-01-31 22:46:19 +05:30
Ayaan Zaidi
37721ebd7c fix: restore telegram draft streaming partials 2026-01-31 22:46:19 +05:30
Tak Hoffman
35988d77ec Merge pull request #3705 from conroywhitney/fix/3658-gateway-timestamp-injection
feat(gateway): inject timestamps into agent + chat.send messages (#3658)
2026-01-31 10:09:00 -06:00
Tak Hoffman
6750696874 Merge branch 'main' into fix/3658-gateway-timestamp-injection 2026-01-31 10:02:01 -06:00
Tak Hoffman
9c29853014 Gateway: inject timestamps into agent/chat.send (#3705) (thanks @conroywhitney, @CashWilliams) 2026-01-31 09:47:27 -06:00
Conroy Whitney
8a5b139a9f revert: drop "Current Date:" label, keep [Wed YYYY-MM-DD HH:MM TZ]
Small model testing showed the label did not meaningfully help:
- Sub-3B models fail regardless of format
- 8B models untested with label specifically
- Frontier models never needed it

The bracket convention [Wed 2026-01-28 22:30 EST] matches existing
channel envelope format and is widely present in training data.
Saves ~2-3 tokens per message vs the labeled version.
2026-01-31 09:47:27 -06:00
Conroy Whitney
b6c8c1e89d feat: add "Current Date:" label to timestamp prefix
Changes [Wed 2026-01-28 20:30 EST] to [Current Date: Wed 2026-01-28 20:30 EST].

Tested with qwen3-1.7B: even with DOW in the timestamp, the model
ignored it and tried to compute the day using Zeller's Congruence.
The "Current Date:" semantic label is widely present in training data
and gives small models the best chance of recognizing the timestamp
as authoritative context rather than metadata to parse.

Cost: ~18 tokens per message. Prevents hallucination spirals that
burn hundreds or thousands of tokens on date derivation.
2026-01-31 09:47:27 -06:00
Conroy Whitney
a6c68e8690 feat: add 3-letter DOW prefix to injected timestamps
Changes [2026-01-28 20:30 EST] to [Wed 2026-01-28 20:30 EST].
Costs ~1 extra token but provides day-of-week for smaller models
that can't derive DOW from a date. Frontier models already handle
it, but this is cheap insurance for 7B-class models.
2026-01-31 09:47:27 -06:00
Conroy Whitney
76391bba3f refactor: use compact formatZonedTimestamp for injection
Replace verbose formatUserTime (Wednesday, January 28th, 2026 — 8:30 PM)
with the same formatZonedTimestamp used by channel envelopes (2026-01-28
20:30 EST). This:

- Saves ~4 tokens per message (~7 vs ~11)
- Uses globally unambiguous YYYY-MM-DD 24h format
- Removes 12/24h config option (always 24h, agent-facing)
- Anchors envelope detection to the actual format function — if channels
  change their timestamp format, our injection + detection change too
- Adds test that compares injection output to formatZonedTimestamp directly

Exported formatZonedTimestamp from auto-reply/envelope.ts for reuse.
2026-01-31 09:47:27 -06:00
Conroy Whitney
08886eaaa3 test: add DST boundary test for timestamp injection
Verifies that America/New_York correctly resolves to midnight for
both EST (winter, UTC-5) and EDT (summer, UTC-4) using the same
IANA timezone. Intl.DateTimeFormat handles the DST transition.
2026-01-31 09:47:27 -06:00
Conroy Whitney
bbf2205640 feat(gateway): inject timestamps into chat.send (webchat/TUI)
The chat.send handler (used by webchat and TUI) is a separate path
from the agent handler. Inject timestamp into BodyForAgent (what the
model sees) while keeping Body raw for UI display.

This completes timestamp coverage for all non-channel paths:
- agent handler: spawned subagents, sessions_send, heartbeats
- chat.send: webchat, TUI
2026-01-31 09:47:27 -06:00
Conroy Whitney
582a4e261a feat(gateway): inject timestamps into agent handler messages
Messages arriving through the gateway agent method (TUI, web, spawned
subagents, sessions_send, heartbeats) now get a timestamp prefix
automatically. This gives all agent contexts date/time awareness
without modifying the system prompt (which is cached for stability).

Channel messages (Discord, Telegram, etc.) already have timestamps
via envelope formatting in a separate code path and never reach
the agent handler, so there is no double-stamping risk.

Cron jobs also inject their own 'Current time:' prefix and are
detected and skipped.

Extracted as a pure function (injectTimestamp) with 12 unit tests
covering: timezone handling, 12/24h format, midnight boundaries,
envelope detection, cron detection, and empty messages.

Integration test verifies the agent handler wires it in correctly.

Closes #3658
Refs: #1897, #1928, #2108
2026-01-31 09:47:27 -06:00
Peter Steinberger
83e64c1ac9 docs: start 2026.1.31 changelog 2026-01-31 16:28:19 +01:00
Seb Slight
8978d16659 Docs: fix index logo dark mode (#5474) 2026-01-31 15:55:59 +01:00
Josh Palmer
7a6c40872d Agents: add system prompt safety guardrails (#5445)
* 🤖 agents: add system prompt safety guardrails

What:
- add safety guardrails to system prompt
- update system prompt docs
- update prompt tests

Why:
- discourage power-seeking or self-modification behavior
- clarify safety/oversight priority when conflicts arise

Tests:
- pnpm lint (pass)
- pnpm build (fails: DefaultResourceLoader missing in pi-coding-agent)
- pnpm test (not run; build failed)

* 🤖 agents: tighten safety wording for prompt guardrails

What:
- scope safety wording to system prompts/safety/tool policy changes
- document Safety inclusion in minimal prompt mode
- update safety prompt tests

Why:
- avoid blocking normal code changes or PR workflows
- keep prompt mode docs consistent with implementation

Tests:
- pnpm lint (pass)
- pnpm build (fails: DefaultResourceLoader missing in pi-coding-agent)
- pnpm test (not run; build failed)

* 🤖 docs: note safety guardrails are soft

What:
- document system prompt safety guardrails as advisory
- add security note on prompt guardrails vs hard controls

Why:
- clarify threat model and operator expectations
- avoid implying prompt text is an enforcement layer

Tests:
- pnpm lint (pass)
- pnpm build (fails: DefaultResourceLoader missing in pi-coding-agent)
- pnpm test (not run; build failed)
2026-01-31 15:50:15 +01:00
Seb Slight
75093ebe1c Docs: add actionable cron quick start (#5446)
* Docs: add cron quick start examples

* Docs: de-duplicate cron tool-call examples

* Docs: fix cron code block fences
2026-01-31 15:21:31 +01:00
cpojer
fcf08299fa chore: Enable experimentalSortPackageJson in oxfmtrc.jsonc. 2026-01-31 23:19:40 +09:00
cpojer
1f2fb823a3 fix: CI: Run build and lint together since lint depends on build now. 2026-01-31 23:15:15 +09:00
cpojer
36b0070b71 fix: Build before linting in CI since we are now linting the extensions folder with --type-aware. 2026-01-31 23:08:24 +09:00
cpojer
230ca789e2 chore: Lint extensions folder. 2026-01-31 22:42:45 +09:00
Peter Steinberger
4f2166c503 chore: update appcast for 2026.1.30 2026-01-31 14:31:00 +01:00
Peter Steinberger
7d89855c55 fix: align npm publish metadata 2026-01-31 14:21:21 +01:00
cpojer
aa91f6e700 chore: Add openclaw to devDependencies for all extensions so that types resolve. 2026-01-31 22:06:51 +09:00
cpojer
59cfff02f6 chore: Emit TypeScript declaration files so that we can type-check the extensions folder soon. 2026-01-31 21:57:21 +09:00
cpojer
1838ab019b chore: Enable linting in scripts. 2026-01-31 21:29:14 +09:00
cpojer
0ffc251704 chore: Updated lint config using ignorePatterns. 2026-01-31 21:24:21 +09:00
cpojer
76b5208b11 chore: Also format scripts and skills. 2026-01-31 21:21:25 +09:00
Armin Ronacher
a767c584c7 Add prompt injection attacks to out of scope section 2026-01-31 13:17:24 +01:00
cpojer
8cab78abbc chore: Run pnpm format:fix. 2026-01-31 21:13:13 +09:00
cpojer
dcc2de15a6 chore: Enable formatting of files other than src and test. 2026-01-31 21:12:46 +09:00
Peter Steinberger
1287328b6f feat: add MiniMax OAuth plugin (#4521) (thanks @Maosghoul) 2026-01-31 12:42:45 +01:00
Peter Steinberger
b9b94715fa fix: avoid stderr backpressure in macOS discovery (#3304) (thanks @abhijeet117) 2026-01-31 12:03:30 +01:00
abhijeet117
efb93d18cf Fix potential subprocess hang by discarding stderr pipe 2026-01-31 12:03:30 +01:00
Peter Steinberger
c3a8a5374f fix: sync docker-compose gateway command 2026-01-31 10:54:46 +00:00
Peter Steinberger
247fab47ca chore: bump version to 2026.1.30 2026-01-31 11:37:36 +01:00
cpojer
dae00fe184 fix: Update CONTRIBUTING.md + adjust watch-node.mjs again to be faster with tsc. 2026-01-31 19:36:07 +09:00
cpojer
76361ae3ab revert: Switch back to tsc for compiling. 2026-01-31 18:31:49 +09:00
Peter Steinberger
e25fedf932 fix: retry gateway watch after dist rebuild 2026-01-31 09:18:33 +00:00
Peter Steinberger
ddc5683c67 fix: resolve workspace templates from package root 2026-01-31 09:07:49 +00:00
cpojer
68ba1afb34 fix: Fix scripts/watch-node.mjs and use tsdown --watch. 2026-01-31 17:55:49 +09:00
cpojer
4b7406719c fix: Update a few more entry.js to entry.mjs paths. 2026-01-31 17:45:00 +09:00
Mariano Belinky
821ed35be1 Revert "iOS: align node permissions and notifications"
This reverts commit b17e6fdd07.
2026-01-31 09:32:36 +01:00
cpojer
ed65131c1c fix: Also build entry.ts into dist/entry.mjs. 2026-01-31 17:26:39 +09:00
Peter Steinberger
1766cd4123 build: add typescript for a2ui bundling 2026-01-31 08:23:10 +00:00
cpojer
d4ed79ffd0 chore: signal-utils is actually used too. 2026-01-31 17:15:03 +09:00
cpojer
86d38c2d82 chore: Oops, "long" is actually used + fix TypeScript error. 2026-01-31 17:12:28 +09:00
cpojer
88fe4de151 chore: Remove unused deps. 2026-01-31 17:11:19 +09:00
Peter Steinberger
ee26b68fe1 fix: lint cleanups 2026-01-31 07:59:01 +00:00
Peter Steinberger
a42e1c82d9 fix: restore tsc build and plugin install tests 2026-01-31 07:54:15 +00:00
cpojer
c4feb7a457 chore: Fix TypeScript errors 5/n. 2026-01-31 16:49:55 +09:00
cpojer
9e908ad6be chore: Fix TypeScript errors 4/n. 2026-01-31 16:48:44 +09:00
cpojer
3282d22dd9 chore: Fix TypeScript errors 3/n. 2026-01-31 16:47:03 +09:00
cpojer
952b0f8c48 chore: Fix TypeScript errors 2/n. 2026-01-31 16:42:40 +09:00
cpojer
e5eb9610dc chore: Fix TypeScript errors 1/n. 2026-01-31 16:38:03 +09:00
cpojer
2957d4306d chore: Update tsconfig.json target, oops. 2026-01-31 16:28:44 +09:00
cpojer
b56e7e66cc chore: Update deps + pnpm dedupe. 2026-01-31 16:26:05 +09:00
cpojer
0fc4d7f52a chore: Remove unused file. 2026-01-31 16:25:06 +09:00
cpojer
5ceff756e1 chore: Enable "curly" rule to avoid single-statement if confusion/errors. 2026-01-31 16:19:20 +09:00
cpojer
009b16fab8 chore: more lint cleanup. 2026-01-31 16:16:13 +09:00
cpojer
b7e401b6b6 chore: Enable no-useless-concat (removing the lint rule from the disabled list automatically enables it because it is in the "suspicious" group). 2026-01-31 16:08:56 +09:00
cpojer
9c4cbaab7b chore: Enable eslint/no-unused-vars. 2026-01-31 16:06:39 +09:00
cpojer
15792b153f chore: Enable more lint rules, disable some that trigger a lot. Will clean up later. 2026-01-31 16:04:04 +09:00
Peter Steinberger
481f696a87 chore: reorder auth provider list 2026-01-31 08:02:45 +01:00
cpojer
7a9ddcd590 chore: Enable some "perf" lint rules. 2026-01-31 15:58:24 +09:00
cpojer
f99e3ddd6d Run tsgo on CI. 2026-01-31 15:44:36 +09:00
Mario Zechner
cbc405c9e3 Agents: update pi-coding-agent API usage 2026-01-31 07:35:52 +01:00
Peter Steinberger
51e72d41c2 fix: restore embedded extension discovery typings 2026-01-31 06:33:32 +00:00
cpojer
762652279b Switch from TypeScript to TypeScript Go. Use pnpm tsgo for Typechecks. 2026-01-31 15:33:15 +09:00
Mario Zechner
72ea3eedc9 Tests: inline mock classes in vi.mock factory 2026-01-31 07:31:00 +01:00
cpojer
a00e0bc189 Lint. 2026-01-31 15:28:11 +09:00
cpojer
67945e8d62 chore: Switch from TypeScript to build with tsdown, speeds up pnpm build by 5-10x. 2026-01-31 15:25:37 +09:00
Peter Steinberger
d2a852b982 fix: align embedded session setup with sdk 2026-01-31 06:22:24 +00:00
Peter Steinberger
ded95d5c70 test: update config candidate order expectation 2026-01-31 06:18:17 +00:00
Mario Zechner
a441059761 Tests: fix models list mock lint 2026-01-31 07:16:34 +01:00
cpojer
84ac889e22 Fix lint. 2026-01-31 15:10:32 +09:00
Peter Steinberger
e9f0be06eb fix: repair docker build typing 2026-01-31 06:50:56 +01:00
Peter Steinberger
08ed62852a chore: update deps and pi model discovery 2026-01-31 06:45:57 +01:00
Peter Steinberger
85dd070dea chore: pin workflow actions + tighten permissions 2026-01-31 06:22:48 +01:00
Shakker
0b95efff27 Merge pull request #5177 from openclaw/feat_shell_completion
feat: Add shell completion for Zsh, Bash, Fish, and PowerShell
2026-01-31 05:18:41 +00:00
Shakker
3c8fa0f913 fix: remove unused variables and fix template literal type 2026-01-31 05:18:27 +00:00
Shakker
b1d25ed0dd feat: automated completion setup in postinstall and onboarding 2026-01-31 05:18:27 +00:00
Shakker
48aaf6ce4e fix: suppress banner and doctor checks for completion command 2026-01-31 05:18:27 +00:00
Shakker
beafaef92f feat: implement completion command and shell generators 2026-01-31 05:18:27 +00:00
Shakker
14c77f8295 feat: add completion subcommand to generate shell completion scripts for Zsh, Bash, PowerShell, and Fish. 2026-01-31 05:18:27 +00:00
Mario Zechner
bf15d0a3f5 Auth: switch Kimi Coding to built-in provider 2026-01-31 06:04:10 +01:00
Mario Zechner
ca47b0d79c OAuth: normalize profile email 2026-01-31 05:26:18 +01:00
Mario Zechner
9b1a6b30d9 Tests: update pi SDK mocks 2026-01-31 05:23:53 +01:00
Ayaan Zaidi
310eed825e fix: preserve delivery thread fallback (#4911) (thanks @yevhen) 2026-01-31 09:31:40 +05:30
Yevhen Bobrov
a642ca4ea8 Fix telegram threadId in deliveryContext 2026-01-31 09:31:40 +05:30
Ayaan Zaidi
e849df64dc fix: normalize telegram account token lookup (#5055) (thanks @jasonsschin) 2026-01-31 08:58:04 +05:30
jasonsschin
e913de0720 Fix Telegram token resolution for non-normalized accountId keys 2026-01-31 08:58:04 +05:30
Mario Zechner
c0a6e675a3 Agents: update pi dependencies to 0.50.7 2026-01-31 04:20:12 +01:00
Mario Zechner
cc366f4baa chore: remove changelog section from pr.md prompt 2026-01-31 04:20:12 +01:00
Mario Zechner
4b1956ab49 chore: add .pi folder 2026-01-31 04:20:12 +01:00
Mario Zechner
9cb5e22861 docs: add pi and pi-dev documentation 2026-01-31 04:20:12 +01:00
Mario Zechner
aa6b9e3a20 CLI: fix tui:dev port resolution 2026-01-31 04:20:12 +01:00
Evan Otero
c67df653b6 fix(security): restrict local path extraction in media parser to prevent LFI (#4880)
* Media: restrict local path extraction to prevent LFI

* Lint: remove unused variable hasValidMediaOnLine
2026-01-31 03:44:11 +01:00
Mariano Belinky
b17e6fdd07 iOS: align node permissions and notifications 2026-01-31 02:28:48 +01:00
Yuri Chukhlib
65dedef65b fix(bluebubbles): debounce by messageId to preserve attachments in text+image messages (#4984)
* fix(bluebubbles): debounce by messageId to preserve attachments in text+image messages

BlueBubbles fires multiple webhook events for a single message - first
without attachment metadata, then ~350ms later with it. Previously,
messages with attachments bypassed debouncing and were processed
immediately, while the text-only version was also queued.

Now the debouncer uses messageId as the key when available, coalescing
all webhook events for the same message. The existing combineDebounceEntries
function merges attachments from all events.

Closes #4848

* fix: increase debounce and handle balloon messages

- Increase DEFAULT_INBOUND_DEBOUNCE_MS from 350ms to 500ms
- Update buildKey to use associatedMessageGuid for balloon messages
- Add regression test for debouncing behavior

Fixes issues identified in code review.

---------

Co-authored-by: Yurii Chukhlib <yurii.chukhlib@viber.com>
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-01-30 15:53:14 -08:00
Tyler Yust
57248a7ca1 fix: prefer requesterOrigin over stale session entry in subagent announce routing (#4957)
* fix: prefer requesterOrigin over stale session entry in subagent announce routing

When a subagent finishes and announces results back, resolveAnnounceOrigin
merged the session entry (primary) with requesterOrigin (fallback). If the
session store had a stale lastChannel (e.g. whatsapp) from a previous
interaction but the user was now on a different channel (e.g. bluebubbles),
the announce would route to the wrong channel.

Swap the merge order so requesterOrigin (captured at spawn time, reflecting
the actual current channel) takes priority, with the session entry as
fallback for any missing fields.

Error before fix:
  Delivery failed (whatsapp to bluebubbles:chat_guid:...): Unknown channel: whatsapp

Adds regression test for the stale-channel scenario.

* fix: match test to exact failure scenario and improve reliability (#4957) (thanks @tyler6204)

- Remove lastTo from stale session store to match the exact mismatch scenario described in the PR
- Replace 5ms setTimeout sleeps with expect.poll for better test reliability
- Prevents flakiness on slower CI machines
2026-01-30 15:52:19 -08:00
Mario Zechner
6a978aa1bc Merge pull request #5021 from mitsuhiko/patch-3
Fix typo from 'p-mono' to 'pi-mono' in agent.md
2026-01-31 00:27:14 +01:00
Armin Ronacher
97895a0239 Fix typo from 'p-mono' to 'pi-mono' in agent.md 2026-01-31 00:23:45 +01:00
Tyler Yust
57c34a324c UI: introduce active minutes constant for chat sessions and enhance session display names 2026-01-30 14:59:08 -08:00
Tyler Yust
0b7aa8cf1d feat(ui): refresh session list after chat commands in Web UI 2026-01-30 14:29:04 -08:00
Gustavo Madeira Santana
34bdbdb405 fix: resolve Control UI assets for global installs (#4909) (thanks @YuriNachos)
Co-authored-by: YuriNachos <YuriNachos@users.noreply.github.com>
2026-01-30 17:08:40 -05:00
Yurii Chukhlib
aa3a8ea869 fix(infra): resolve control-ui assets on npm global install (#4855) 2026-01-30 17:06:58 -05:00
Vignesh
2f0592dbc6 Update deployment link in railway documentation 2026-01-30 14:06:12 -08:00
Gustavo Madeira Santana
39eb0b7bc0 fix: prevent undefined gateway token defaults (#4873) (thanks @Hisleren)
Co-authored-by: Hisleren <Hisleren@users.noreply.github.com>
2026-01-30 16:16:35 -05:00
Gustavo Madeira Santana
e5a95b5b66 fix: local updates for PR #4873
Co-authored-by: Hisleren <Hisleren@users.noreply.github.com>
2026-01-30 16:16:35 -05:00
Hisleren
201d7fa956 fix(security): prevent gateway token from defaulting to 'undefined' string 2026-01-30 16:16:35 -05:00
Glucksberg
0175cedf0e docs: update remaining clawdbot/moltbot references to openclaw
- .pre-commit-config.yaml: update comment header
- AGENTS.md: update plugin-sdk import path and state directory paths
- skills/bluebubbles/SKILL.md: update plugin-sdk import reference
- skills/discord/SKILL.md: update example sticker name
2026-01-30 16:05:22 -05:00
Ubuntu
7c96bde3b3 docs: fix GitHub branding capitalization 2026-01-30 15:52:21 -05:00
Amit Biswal
9334dd8017 docs: Internal linking of channel pages 2026-01-30 15:52:06 -05:00
Peter Steinberger
2cdfecdde3 docs: clarify security scope 2026-01-30 21:51:28 +01:00
Ubuntu
49a3e3795a docs: fix missing apostrophes in FAQ headers 2026-01-30 15:50:42 -05:00
Ubuntu
23f0efbf09 docs: use straight quotes for code terms in installer guide 2026-01-30 15:50:10 -05:00
Arnav Gupta
9a1b440968 Fix typo in agent.md from p-mono to pi-mono 2026-01-30 15:48:29 -05:00
Gustavo Madeira Santana
daf27dd37e fix: add per-agent models status (#4780) (thanks @jlowin) 2026-01-30 15:47:05 -05:00
Gustavo Madeira Santana
f24e3cdae5 fix: local updates for PR #4780
Co-authored-by: jlowin <jlowin@users.noreply.github.com>
2026-01-30 15:47:05 -05:00
Jeremiah Lowin
dd4715a2c4 CLI: add --agent flag to models status 2026-01-30 15:47:05 -05:00
Peter Steinberger
192a6ee870 chore: remove legacy clawdhub files 2026-01-30 21:03:12 +01:00
Peter Steinberger
fd00d5688a chore: update openclaw naming 2026-01-30 21:03:11 +01:00
Josh Palmer
6522de6ce0 Tests: use agents_list in tools invoke gateway test
What: swap sessions_list for agents_list in /tools/invoke tests
Why: avoid nested gateway calls that can hang under CI; still validates tool invocation + allowlist
Tests: not run (CI should cover)
2026-01-30 18:17:12 +01:00
Shakker
09be5d45d5 Merge pull request #4651 from yuting0624/fix/status-command-line-crash
fix(line): resolve TypeError in status command when LINE is enabled
2026-01-30 15:41:40 +00:00
Yuting Lin
3fbf99d725 fix(line): resolve TypeError in status command 2026-01-30 15:41:22 +00:00
Ayush Ojha
37e295fc02 fix: don't warn about expired OAuth tokens with valid refresh tokens (#4593)
OAuth credentials with a refresh token auto-renew on first API call,
so the doctor should not warn about access token expiration when a
refresh token is present. This avoids unnecessary "expired" warnings
that prompt users to re-auth when no action is needed.

Fixes #3032

Co-authored-by: Ayush Ojha <ayushozha@outlook.com>
2026-01-30 15:39:17 +00:00
Ayaan Zaidi
da71eaebd2 fix: correct telegram html nesting (#4578) (thanks @ThanhNguyxn) 2026-01-30 16:53:39 +05:30
ThanhNguyxn
8e5a684445 style: format test file 2026-01-30 16:53:39 +05:30
ThanhNguyxn
b05d57964b fix(telegram): properly nest overlapping HTML tags (#4071)
Unify style and link closing in render.ts to use LIFO order across
both element types, fixing cases where bold/italic spans containing
autolinks produced invalid HTML like <b><a></b></a>.
2026-01-30 16:53:39 +05:30
Ayaan Zaidi
fa9ec6e854 fix: add docker ui install changelog entry (#4584) (thanks @obviyus) 2026-01-30 16:25:24 +05:30
Ayaan Zaidi
1168f59890 perf: skip redundant ui install in Dockerfile 2026-01-30 16:25:24 +05:30
Ayaan Zaidi
bc432d8435 fix: accept numeric Telegram react ids (#4533) (thanks @Ayush10) 2026-01-30 15:01:18 +05:30
Ayush Ojha
f760aa302c fix(telegram): react action accepts numeric messageId and chatId
The react action used readStringParam for messageId and chatId, which
rejected numeric values with a misleading "messageId required" error.
Switched to readStringOrNumberParam to match the delete/edit actions.

Closes #1459

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 14:56:53 +05:30
Ayaan Zaidi
3a85cb1833 fix: honor Telegram proxy dispatcher (#4456) (thanks @spiceoogway) 2026-01-30 14:38:39 +05:30
spiceoogway
7150268f84 fix(telegram): use undici fetch for proxy to fix dispatcher option
Fixes #4038

The global fetch in Node.js doesn't support undici's dispatcher option,
which is required for ProxyAgent to work. This fix imports fetch from
undici directly to enable proper proxy support for Telegram API calls.

Root cause: makeProxyFetch() was using global fetch with { dispatcher: agent },
but Node.js's global fetch ignores the dispatcher option. Using undici.fetch
ensures the ProxyAgent dispatcher is properly respected.

Tested: Build passes, TypeScript compilation successful.
2026-01-30 14:37:47 +05:30
Peter Steinberger
6af205a13a docs: update lore with final form 2026-01-30 07:26:07 +00:00
Ayaan Zaidi
9025da2296 fix: scope telegram skill commands per bot (#4360) (thanks @robhparker) 2026-01-30 12:00:29 +05:30
robhparker
c6ddc95fc0 fix(telegram): scope skill commands to bound agent per bot
registerTelegramNativeCommands() calls listSkillCommandsForAgents()
without passing agentIds, causing ALL agents' skill commands to be
registered on EVERY Telegram bot. When multiple agents share skill
names (e.g. two agents both have a "butler" skill), the shared `used`
Set in listSkillCommandsForAgents causes de-duplication suffixes
(_2, _3) and all commands appear on every bot regardless of agent
binding.

This fix uses the existing resolveAgentRoute() (already imported) to
find the bound agent for the current Telegram accountId, then passes
that agentId to listSkillCommandsForAgents(). The function already
accepts an optional agentIds parameter — it just wasn't wired from
the Telegram registration path.

Before: All agents' skill commands registered on every Telegram bot,
causing /butler_2, /housekeeper_2 dedup suffixes and potential
BOT_COMMANDS_TOO_MUCH errors when total exceeds 100.

After: Each Telegram bot only registers skill commands for its own
bound agent. No cross-agent dedup, no command limit overflow.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 11:58:23 +05:30
Nate
28f8d00e9f fix: update install URLs from clawd.bot to openclaw.ai 2026-01-30 00:19:20 -06:00
Manik Vahsith
5e635c9656 feat: add Kimi K2.5 model to synthetic catalog (#4407)
* feat: add Kimi K2.5 model to synthetic catalog

Add hf:moonshotai/Kimi-K2.5 to the synthetic model catalog.
This model is available via dev.synthetic.new API.

- 256k context window
- 8192 max tokens
- Supports reasoning

* chore: fix formatting in onboard-helpers.ts

* fix: update config candidate ordering test (#4407) (thanks @manikv12)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-01-30 07:17:42 +01:00
Peter Steinberger
87267fad4f docs: move WhatsApp image below dashboard 2026-01-30 06:55:15 +01:00
Gustavo Madeira Santana
613724c26e Update index.md 2026-01-30 00:33:06 -05:00
Peter Steinberger
77e703c69b chore: update appcast for 2026.1.29 2026-01-30 06:25:45 +01:00
Peter Steinberger
62e4ad23d3 chore: release 2026.1.29 2026-01-30 06:25:45 +01:00
Peter Steinberger
23c424899c docs: reorder 2026.1.29 changelog 2026-01-30 06:25:21 +01:00
Peter Steinberger
c5d7d1110b chore: update pnpm lockfile 2026-01-30 05:15:50 +00:00
Gustavo Madeira Santana
12e8a8410f Update logo and contributor name in docs
Replaced the static image with a responsive logo using the <picture> element for light/dark mode support. Updated contributor name from 'Clawd' to 'Molty'.
2026-01-30 00:13:44 -05:00
Gustavo Madeira Santana
4de0bae45a Update README with responsive logo for dark mode 2026-01-29 23:38:32 -05:00
Gustavo Madeira Santana
ddad65588f Add files via upload 2026-01-29 23:37:32 -05:00
Peter Steinberger
bf6ec64fd9 docs: move deepwiki link 2026-01-30 05:33:05 +01:00
Gustavo Madeira Santana
4ec9d98821 Update ASCII art banners for CLI and wizard header
Replaces the previous ASCII art in both the CLI banner and the wizard header with a new, wider design and updates the label to 'OPENCLAW' for consistency.
2026-01-29 23:29:47 -05:00
Peter Steinberger
151ddd624b fix: detect legacy gateway launchd labels 2026-01-30 05:01:46 +01:00
Peter Steinberger
b9afa3d33f fix: migrate symlinked legacy state dirs 2026-01-30 04:48:04 +01:00
Peter Steinberger
d9c81991b1 chore: bump beta 2026-01-30 04:37:46 +01:00
Peter Steinberger
9886fd1a5a fix: migrate legacy state dirs 2026-01-30 04:26:00 +01:00
Peter Steinberger
67918dc41b chore: bump beta 2026-01-30 04:10:47 +01:00
Peter Steinberger
a155e2f8ae fix: migrate legacy config 2026-01-30 04:09:49 +01:00
Peter Steinberger
02576615cb fix: migrate legacy gateway services 2026-01-30 04:01:31 +01:00
Peter Steinberger
d47b4e6f81 fix: update config types 2026-01-30 03:20:28 +01:00
Peter Steinberger
7d03cae66a chore: bump npm version 2026-01-30 03:16:48 +01:00
Peter Steinberger
9a7160786a refactor: rename to openclaw 2026-01-30 03:16:21 +01:00
Shakker
4583f88626 fix: preserve reasoning tags inside code blocks (#4118) (thanks @vinaygit18) 2026-01-29 18:53:05 +00:00
Peter Steinberger
c9fe062824 chore: update clawtributors 2026-01-29 17:31:39 +00:00
Peter Steinberger
78b9876641 feat: add Xiaomi MiMo provider onboarding (#3454)
Thanks @WqyJh.

Co-authored-by: Qiying Wang <15232241+WqyJh@users.noreply.github.com>
2026-01-29 17:29:58 +00:00
Vibe Kanban
50d44d0bd9 feat: support xiaomi/mimo-v2-flash 2026-01-29 17:15:51 +00:00
Peter Steinberger
cb4b3f74b5 chore(release): bump versions to 2026.1.29 2026-01-29 16:48:13 +00:00
Peter Steinberger
5152060121 docs(changelog): rewrite 2026.1.29 notes 2026-01-29 16:48:05 +00:00
Peter Steinberger
06289b36da fix(security): harden SSH target handling (#4001)
Thanks @YLChen-007.

Co-authored-by: Edward-x <YLChen-007@users.noreply.github.com>
2026-01-29 16:33:36 +00:00
Josh Palmer
4b5514a259 Tests: default-disable plugins in VITEST 2026-01-29 17:14:14 +01:00
Josh Palmer
5f4715acfc fix flaky gateway tests in CI
What:
- resolve shell from PATH in bash-tools tests (avoid /bin/bash dependency)
- mock DNS for web-fetch SSRF tests (no real network)
- stub a2ui bundle in canvas-host server test when missing

Why:
- keep gateway test suite deterministic on Nix/Garnix Linux

Tests:
- not run locally (known missing deps in unit test run)
2026-01-29 12:14:27 +01:00
Josh Palmer
c41ea252b0 fix flaky web-fetch tests + lock cleanup
What:
- stub resolvePinnedHostname in web-fetch tests to avoid DNS flake
- close lock file handles via FileHandle.close during cleanup to avoid EBADF

Why:
- make CI deterministic without network/DNS dependence
- prevent double-close errors from GC

Tests:
- pnpm vitest run --config vitest.unit.config.ts src/agents/tools/web-tools.fetch.test.ts src/agents/session-write-lock.test.ts (failed: missing @aws-sdk/client-bedrock)
2026-01-29 11:05:11 +01:00
Tyler Yust
6372242da7 fix(ui): improve chat session dropdown and refresh behavior (#3682)
* refactor(ui): enhance loadSessions function to accept overrides for session loading parameters

- Updated loadSessions to include optional parameters for activeMinutes, limit, includeGlobal, and includeUnknown.
- Modified refreshChat to use the new activeMinutes parameter when loading sessions.
- Removed duplicate applySettingsFromUrl call in handleConnected function.

* feat(ui): implement session refresh functionality after chat

- Added `refreshSessionsAfterChat` property to `ChatHost` and `GatewayHost` types.
- Introduced `isChatResetCommand` function to identify chat reset commands.
- Updated `handleSendChat` to set `refreshSessions` based on chat reset commands.
- Modified `handleGatewayEventUnsafe` to load sessions when chat is finalized and `refreshSessionsAfterChat` is true.
- Enhanced `refreshChat` to load sessions with `activeMinutes` set to 0 for immediate refresh.
2026-01-28 23:24:46 -08:00
Ayaan Zaidi
718bc3f9c8 fix: avoid silent telegram empty replies (#3796) (#3796) 2026-01-29 11:34:47 +05:30
Conroy Whitney
b603d9c6ba revert: drop "Current Date:" label, keep [Wed YYYY-MM-DD HH:MM TZ]
Small model testing showed the label did not meaningfully help:
- Sub-3B models fail regardless of format
- 8B models untested with label specifically
- Frontier models never needed it

The bracket convention [Wed 2026-01-28 22:30 EST] matches existing
channel envelope format and is widely present in training data.
Saves ~2-3 tokens per message vs the labeled version.
2026-01-29 05:49:11 +00:00
Conroy Whitney
5ca1b96988 feat: add "Current Date:" label to timestamp prefix
Changes [Wed 2026-01-28 20:30 EST] to [Current Date: Wed 2026-01-28 20:30 EST].

Tested with qwen3-1.7B: even with DOW in the timestamp, the model
ignored it and tried to compute the day using Zeller's Congruence.
The "Current Date:" semantic label is widely present in training data
and gives small models the best chance of recognizing the timestamp
as authoritative context rather than metadata to parse.

Cost: ~18 tokens per message. Prevents hallucination spirals that
burn hundreds or thousands of tokens on date derivation.
2026-01-29 05:49:11 +00:00
Conroy Whitney
a007ba7b1b feat: add 3-letter DOW prefix to injected timestamps
Changes [2026-01-28 20:30 EST] to [Wed 2026-01-28 20:30 EST].
Costs ~1 extra token but provides day-of-week for smaller models
that can't derive DOW from a date. Frontier models already handle
it, but this is cheap insurance for 7B-class models.
2026-01-29 05:49:11 +00:00
Conroy Whitney
ff713a41e0 refactor: use compact formatZonedTimestamp for injection
Replace verbose formatUserTime (Wednesday, January 28th, 2026 — 8:30 PM)
with the same formatZonedTimestamp used by channel envelopes (2026-01-28
20:30 EST). This:

- Saves ~4 tokens per message (~7 vs ~11)
- Uses globally unambiguous YYYY-MM-DD 24h format
- Removes 12/24h config option (always 24h, agent-facing)
- Anchors envelope detection to the actual format function — if channels
  change their timestamp format, our injection + detection change too
- Adds test that compares injection output to formatZonedTimestamp directly

Exported formatZonedTimestamp from auto-reply/envelope.ts for reuse.
2026-01-29 05:49:11 +00:00
Conroy Whitney
9d28763753 test: add DST boundary test for timestamp injection
Verifies that America/New_York correctly resolves to midnight for
both EST (winter, UTC-5) and EDT (summer, UTC-4) using the same
IANA timezone. Intl.DateTimeFormat handles the DST transition.
2026-01-29 05:49:11 +00:00
Conroy Whitney
3696532f04 feat(gateway): inject timestamps into chat.send (webchat/TUI)
The chat.send handler (used by webchat and TUI) is a separate path
from the agent handler. Inject timestamp into BodyForAgent (what the
model sees) while keeping Body raw for UI display.

This completes timestamp coverage for all non-channel paths:
- agent handler: spawned subagents, sessions_send, heartbeats
- chat.send: webchat, TUI
2026-01-29 05:49:11 +00:00
Conroy Whitney
cbe388ece3 feat(gateway): inject timestamps into agent handler messages
Messages arriving through the gateway agent method (TUI, web, spawned
subagents, sessions_send, heartbeats) now get a timestamp prefix
automatically. This gives all agent contexts date/time awareness
without modifying the system prompt (which is cached for stability).

Channel messages (Discord, Telegram, etc.) already have timestamps
via envelope formatting in a separate code path and never reach
the agent handler, so there is no double-stamping risk.

Cron jobs also inject their own 'Current time:' prefix and are
detected and skipped.

Extracted as a pure function (injectTimestamp) with 12 unit tests
covering: timezone handling, 12/24h format, midnight boundaries,
envelope detection, cron detection, and empty messages.

Integration test verifies the agent handler wires it in correctly.

Closes #3658
Refs: #1897, #1928, #2108
2026-01-29 05:49:11 +00:00
Conroy Whitney
c20035094d fix: use & instead of <> in XML escaping test for Windows NTFS compatibility (#3750)
NTFS does not allow < or > in filenames, causing the XML filename
escaping test to fail on Windows CI with ENOENT.

Replace file<test>.txt with file&test.txt — & is valid on all platforms
and still requires XML escaping (&amp;), preserving the test's intent.

Fixes #3748
2026-01-29 05:46:50 +00:00
kiranjd
0761652701 fix(telegram): handle empty reply array in notifyEmptyResponse
Previous fix only checked skippedEmpty > 0, but when model returns
content: [] no payloads are created at all. Now also checks
replies.length === 0 to catch this case.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:13:39 +05:30
kiranjd
a2d06e75b0 fix(telegram): notify users when agent returns empty response
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 11:13:39 +05:30
Ayaan Zaidi
34291321b4 chore: update clawtributors (add @HirokiKobayashi-R) 2026-01-29 10:33:25 +05:30
Ayaan Zaidi
16a5549ec0 docs: update changelog for mention patterns (#3303) (thanks @HirokiKobayashi-R) 2026-01-29 10:31:47 +05:30
HirokiKobayashi-R
22b59d24ce fix(mentions): check mentionPatterns even when explicit mention is available 2026-01-29 10:31:47 +05:30
Ayaan Zaidi
fcc53bcf1b fix: include AccountId in telegram native command context (#2942) (thanks @Chloe-VP) 2026-01-29 10:17:25 +05:30
Chloe
6132c3d014 fix(telegram): include AccountId in native command context for multi-agent routing
When running multiple Telegram bot accounts bound to different agents,
the /new command (and other slash commands) would send confirmation
messages via the wrong bot because the context was missing AccountId.

The fix adds AccountId: route.accountId to the context payload in
registerTelegramNativeCommands, matching how bot-message-context.ts
handles regular messages.

Fixes #2537
2026-01-29 10:17:25 +05:30
Ayaan Zaidi
4ac7aa4a48 fix: handle telegram video notes (#2905) (thanks @mylukin) 2026-01-29 10:07:21 +05:30
Lukin
78722d0b4f fix(telegram): add video_note support to Telegram channel
- Add msg.video_note to media extraction chain in bot/delivery.ts
- Add placeholder detection for video notes in bot-message-context.ts
- Video notes (rounded square video messages) are now processed and downloaded like regular videos

Fixes issue where video note messages were silently dropped because they weren't in the media handling logic.
2026-01-29 10:07:21 +05:30
Clawdbot
c13c39f121 fix: exclude native slash commands from onToolResult
Native slash commands (e.g. /verbose, /status) should not emit tool
summaries. Gate onToolResult behind CommandSource !== 'native' in
addition to the existing ChatType !== 'group' check.

Add test for native command exclusion.
2026-01-29 09:50:39 +05:30
Clawdbot
e1ecfb25b8 test: add tests for onToolResult in DM vs group sessions
- provides onToolResult in DM sessions (ChatType=direct)
- does not provide onToolResult in group sessions (ChatType=group)
- sends tool results via dispatcher in DM sessions

Replaces the old cross-provider test that expected onToolResult to
always be undefined.
2026-01-29 09:50:39 +05:30
Clawdbot
f27a5030d8 fix: restore verbose tool summaries in DM sessions
875b018ea removed onToolResult from dispatch-from-config.ts to prevent
tool summaries leaking into group channels. However, this also broke
verbose tool summaries in DM/private sessions where they are expected.

This restores onToolResult but gates it behind ChatType !== 'group',
so group channels remain unaffected while DM verbose works again.

mirror=false is passed to sendPayloadAsync to avoid duplicating tool
summaries in the session transcript (matching the block reply behavior).

Fixes #2665
2026-01-29 09:50:39 +05:30
Gustavo Madeira Santana
699784dbee chore: remove stray package-lock.json 2026-01-28 22:00:55 -05:00
Gustavo Madeira Santana
a44da67069 fix: local updates for PR #3600
Co-authored-by: kira-ariaki <kira-ariaki@users.noreply.github.com>
2026-01-28 22:00:11 -05:00
Kira
0fd9d3abd1 feat(memory): add explicit paths config for memory search
Add a `paths` option to `memorySearch` config, allowing users to
explicitly specify additional directories or files to include in
memory search.

Follow-up to #2961 as suggested by @gumadeiras — instead of auto-following
symlinks (which has security implications), users can now explicitly
declare additional search paths.

- Add `memorySearch.paths` config option (array of strings)
- Paths can be absolute or relative (resolved from workspace)
- Directories are recursively scanned for `.md` files
- Single `.md` files can also be specified
- Paths from defaults and agent overrides are merged
- Added 4 test cases for listMemoryFiles
2026-01-28 22:00:11 -05:00
Shakker
b717724275 fix: add security hardening for media text attachments (#3700)
* fix: Prevent XML attribute injection by escaping special characters in file name and MIME type attributes.

* fix: text attachment MIME misclassification with security hardening (#3628)

- Fix CSV/TSV inference from content heuristics
- Add UTF-16 detection and BOM handling
- Add XML attribute escaping for file output (security)
- Add MIME override logging for auditability
- Add comprehensive test coverage for edge cases

Thanks @frankekn
2026-01-29 02:39:01 +00:00
Frank Yang
cb18ce7a85 Fix text attachment MIME misclassification (#3628)
* Fix text file attachment detection

* Add file attachment extraction tests
2026-01-29 02:33:03 +00:00
Gustavo Madeira Santana
a109b7f1a9 Update self message trust policy in WhatsApp docs
Clarified that self messages from the linked WhatsApp number bypass DM policy and allowFrom checks.
2026-01-28 20:31:33 -05:00
tewatia
4f554a1e31 docs(whatsapp): clarify self-message dmPolicy bypass
Self messages from the linked WhatsApp number bypass dmPolicy and allowFrom
checks automatically. Clarified that users don't need to add their own
number to the allowlist.

Self messages from the linked WhatsApp number bypass dmPolicy checks
entirely (via isSamePhone check in access-control.ts)...
2026-01-28 20:31:33 -05:00
Conroy Whitney
034d4513d9 fix(system-prompt): hint session_status for date/time instead of embedding it
The system prompt intentionally excludes the current date/time for cache
stability (see 66eec295b). This leaves agents without date awareness,
causing wrong day-of-week claims (#1897, #1928, #2108).

Instead of reverting the cache optimization, add a one-line hint directing
agents to use session_status when they need the current date/time. This
keeps the prompt stable while teaching frontier models where to look.

Also adds a negative test ensuring the date/time is NOT re-added to the
system prompt, with comments explaining why and pointing to #3658 for the
complementary gateway-level timestamp injection approach.

Refs: #1897, #1928, #3658
2026-01-28 20:08:31 -05:00
jonisjongithub
fdcac0ccf4 fix: correct 'Venius' typo to 'Venice' in provider docs (#3638) - thanks (@jonisjongithub) 2026-01-28 23:51:43 +00:00
Shakker
3a9cfd787d Merge pull request #3635 from moltbot/fix-token-input-trim
fix: trim whitespace from config input fields on change
2026-01-28 23:46:14 +00:00
Shakker
1c98b9dec8 fix(ui): trim whitespace from config input fields on change 2026-01-28 23:41:33 +00:00
Shakker
67f1402703 fix: tts base url runtime read (#3341) (thanks @hclsys) 2026-01-28 23:30:29 +00:00
Tyler Yust
a7534dc223 fix(ui): gateway URL confirmation modal (based on #2880) (#3578)
* fix: adding confirmation modal to confirm gateway url change

* refactor: added modal instead of confirm prompt

* fix(ui): reconnect after confirming gateway url (#2880) (thanks @0xacb)

---------

Co-authored-by: 0xacb <amccbaptista@gmail.com>
2026-01-28 13:32:10 -08:00
Gustavo Madeira Santana
109ac1c549 fix: banner spacing 2026-01-28 11:39:35 -05:00
Akshay
01e0d3a320 fix(cli): initialize plugins before pairing CLI registration (#3272)
The pairing CLI calls listPairingChannels() at registration time,
which requires the plugin registry to be populated. Without this,
plugin-provided channels like Matrix fail with "does not support
pairing" even though they have pairing adapters defined.

This mirrors the existing pattern used by the plugins CLI entry.

Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
2026-01-28 13:26:25 +00:00
4555 changed files with 252409 additions and 66269 deletions

View File

@@ -29,10 +29,12 @@ git log --oneline --left-right main...upstream/main | head -20
```
This shows:
- `<` = your local commits (ahead)
- `>` = upstream commits you're missing (behind)
**Decision point:**
- Few local commits, many upstream → **Rebase** (cleaner history)
- Many local commits or shared branch → **Merge** (preserves history)
@@ -70,12 +72,12 @@ git rebase --abort
### Common Conflict Patterns
| File | Resolution |
|------|------------|
| `package.json` | Take upstream deps, keep local scripts if needed |
| `pnpm-lock.yaml` | Accept upstream, regenerate with `pnpm install` |
| `*.patch` files | Usually take upstream version |
| Source files | Merge logic carefully, prefer upstream structure |
| File | Resolution |
| ---------------- | ------------------------------------------------ |
| `package.json` | Take upstream deps, keep local scripts if needed |
| `pnpm-lock.yaml` | Accept upstream, regenerate with `pnpm install` |
| `*.patch` files | Usually take upstream version |
| Source files | Merge logic carefully, prefer upstream structure |
---
@@ -88,6 +90,7 @@ git merge upstream/main --no-edit
```
Resolve conflicts same as rebase, then:
```bash
git add <resolved-files>
git commit
@@ -170,6 +173,7 @@ pnpm clawdbot agent --message "Verification: macOS app rebuild successful - agen
Upstream updates may introduce Swift 6.2 / macOS 26 SDK incompatibilities. Use analyze-mode for systematic debugging:
### Analyze-Mode Investigation
```bash
# Gather context with parallel agents
morph-mcp_warpgrep_codebase_search search_string="Find deprecated FileManager.default and Thread.isMainThread usages in Swift files" repo_path="/Volumes/Main SSD/Developer/clawdis"
@@ -179,6 +183,7 @@ morph-mcp_warpgrep_codebase_search search_string="Locate Peekaboo submodule and
### Common Swift 6.2 Fixes
**FileManager.default Deprecation:**
```bash
# Search for deprecated usage
grep -r "FileManager\.default" src/ apps/ --include="*.swift"
@@ -189,6 +194,7 @@ grep -r "FileManager\.default" src/ apps/ --include="*.swift"
```
**Thread.isMainThread Deprecation:**
```bash
# Search for deprecated usage
grep -r "Thread\.isMainThread" src/ apps/ --include="*.swift"
@@ -199,6 +205,7 @@ grep -r "Thread\.isMainThread" src/ apps/ --include="*.swift"
```
### Peekaboo Submodule Fixes
```bash
# Check Peekaboo for concurrency issues
cd src/canvas-host/a2ui
@@ -210,6 +217,7 @@ pnpm canvas:a2ui:bundle
```
### macOS App Concurrency Fixes
```bash
# Check macOS app for issues
grep -r "Thread\.isMainThread\|FileManager\.default" apps/macos/ --include="*.swift"
@@ -220,7 +228,9 @@ cd apps/macos && rm -rf .build .swiftpm
```
### Model Configuration Updates
If upstream introduced new model configurations:
```bash
# Check for OpenRouter API key requirements
grep -r "openrouter\|OPENROUTER" src/ --include="*.ts" --include="*.js"
@@ -265,6 +275,7 @@ Common issue: `fetch.preconnect` type mismatch. Fix by using `FetchLike` type in
### macOS App Crashes on Launch
Usually resource bundle mismatch. Full rebuild required:
```bash
cd apps/macos && rm -rf .build .swiftpm
./scripts/restart-mac.sh
@@ -285,12 +296,14 @@ pnpm install 2>&1 | grep -i patch
**Symptoms:** Build fails with deprecation warnings about `FileManager.default` or `Thread.isMainThread`
**Search-Mode Investigation:**
```bash
# Exhaustive search for deprecated APIs
morph-mcp_warpgrep_codebase_search search_string="Find all Swift files using deprecated FileManager.default or Thread.isMainThread" repo_path="/Volumes/Main SSD/Developer/clawdis"
```
**Quick Fix Commands:**
```bash
# Find all affected files
find . -name "*.swift" -exec grep -l "FileManager\.default\|Thread\.isMainThread" {} \;
@@ -303,6 +316,7 @@ grep -rn "Thread\.isMainThread" --include="*.swift" .
```
**Rebuild After Fixes:**
```bash
# Clean all build artifacts
rm -rf apps/macos/.build apps/macos/.swiftpm

View File

@@ -0,0 +1,181 @@
# PR Workflow for Maintainers
Please read this in full and do not skip sections.
This is the single source of truth for the maintainer PR workflow.
## Triage order
Process PRs **oldest to newest**. Older PRs are more likely to have merge conflicts and stale dependencies; resolving them first keeps the queue healthy and avoids snowballing rebase pain.
## Working rule
Skills execute workflow. Maintainers provide judgment.
Always pause between skills to evaluate technical direction, not just command success.
These three skills must be used in order:
1. `review-pr` — review only, produce findings
2. `prepare-pr` — rebase, fix, gate, push to PR head branch
3. `merge-pr` — squash-merge, verify MERGED state, clean up
They are necessary, but not sufficient. Maintainers must steer between steps and understand the code before moving forward.
Treat PRs as reports first, code second.
If submitted code is low quality, ignore it and implement the best solution for the problem.
Do not continue if you cannot verify the problem is real or test the fix.
## Coding Agent
Use ChatGPT 5.3 Codex High. Fall back to 5.2 Codex High or 5.3 Codex Medium if necessary.
## PR quality bar
- Do not trust PR code by default.
- Do not merge changes you cannot validate with a reproducible problem and a tested fix.
- Keep types strict. Do not use `any` in implementation code.
- Keep external-input boundaries typed and validated, including CLI input, environment variables, network payloads, and tool output.
- Keep implementations properly scoped. Fix root causes, not local symptoms.
- Identify and reuse canonical sources of truth so behavior does not drift across the codebase.
- Harden changes. Always evaluate security impact and abuse paths.
- Understand the system before changing it. Never make the codebase messier just to clear a PR queue.
## Rebase and conflict resolution
Before any substantive review or prep work, **always rebase the PR branch onto current `main` and resolve merge conflicts first**. A PR that cannot cleanly rebase is not ready for review — fix conflicts before evaluating correctness.
- During `prepare-pr`: rebase onto `main` as the first step, before fixing findings or running gates.
- If conflicts are complex or touch areas you do not understand, stop and escalate.
- Prefer **rebase** for linear history; **squash** when commit history is messy or unhelpful.
## Commit and changelog rules
- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
- During `prepare-pr`, use this commit subject format: `fix: <summary> (openclaw#<PR>) thanks @<pr-author>`.
- Group related changes; avoid bundling unrelated refactors.
- Changelog workflow: keep the latest released version at the top (no `Unreleased`); after publishing, bump the version and start a new top section.
- When working on a PR: add a changelog entry with the PR number and thank the contributor.
- When working on an issue: reference the issue in the changelog entry.
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
## Co-contributor and clawtributors
- If we squash, add the PR author as a co-contributor in the commit body using a `Co-authored-by:` trailer.
- When maintainer prepares and merges the PR, add the maintainer as an additional `Co-authored-by:` trailer too.
- Avoid `--auto` merges for maintainer landings. Merge only after checks are green so the maintainer account is the actor and attribution is deterministic.
- For squash merges, set `--author-email` to a reviewer-owned email with fallback candidates; if merge fails due to author-email validation, retry once with the next candidate.
- If you review a PR and later do work on it, land via merge/squash (no direct-main commits) and always add the PR author as a co-contributor.
- When merging a PR: leave a PR comment that explains exactly what we did, include the SHA hashes, and record the comment URL in the final report.
- When merging a PR from a new contributor: run `bun scripts/update-clawtributors.ts` to add their avatar to the README "Thanks to all clawtributors" list, then commit the regenerated README.
## Review mode vs landing mode
- **Review mode (PR link only):** read `gh pr view`/`gh pr diff`; **do not** switch branches; **do not** change code.
- **Landing mode (exception path):** use only when normal `review-pr -> prepare-pr -> merge-pr` flow cannot safely preserve attribution or cannot satisfy branch protection. Create an integration branch from `main`, bring in PR commits (**prefer rebase** for linear history; **merge allowed** when complexity/conflicts make it safer), apply fixes, add changelog (+ thanks + PR #), run full gate **locally before committing** (`pnpm build && pnpm check && pnpm test`), commit, merge back to `main`, then `git switch main` (never stay on a topic branch after landing). Important: the contributor needs to be in the git graph after this!
## Pre-review safety checks
- Before starting a review when a GH Issue/PR is pasted: use an isolated `.worktrees/pr-<PR>` checkout from `origin/main`. Do not require a clean main checkout, and do not run `git pull` in a dirty main checkout.
- PR review calls: prefer a single `gh pr view --json ...` to batch metadata/comments; run `gh pr diff` only when needed.
- PRs should summarize scope, note testing performed, and mention any user-facing changes or new flags.
- Read `docs/help/submitting-a-pr.md` ([Submitting a PR](https://docs.openclaw.ai/help/submitting-a-pr)) for what we expect from contributors.
## Unified workflow
Entry criteria:
- PR URL/number is known.
- Problem statement is clear enough to attempt reproduction.
- A realistic verification path exists (tests, integration checks, or explicit manual validation).
### 1) `review-pr`
Purpose:
- Review only: correctness, value, security risk, tests, docs, and changelog impact.
- Produce structured findings and a recommendation.
Expected output:
- Recommendation: ready, needs work, needs discussion, or close.
- `.local/review.md` with actionable findings.
Maintainer checkpoint before `prepare-pr`:
```
What problem are they trying to solve?
What is the most optimal implementation?
Can we fix up everything?
Do we have any questions?
```
Stop and escalate instead of continuing if:
- The problem cannot be reproduced or confirmed.
- The proposed PR scope does not match the stated problem.
- The design introduces unresolved security or trust-boundary concerns.
### 2) `prepare-pr`
Purpose:
- Make the PR merge-ready on its head branch.
- Rebase onto current `main` first, then fix blocker/important findings, then run gates.
- In fresh worktrees, bootstrap dependencies before local gates (`pnpm install --frozen-lockfile`).
Expected output:
- Updated code and tests on the PR head branch.
- `.local/prep.md` with changes, verification, and current HEAD SHA.
- Final status: `PR is ready for /mergepr`.
Maintainer checkpoint before `merge-pr`:
```
Is this the most optimal implementation?
Is the code properly scoped?
Is the code properly reusing existing logic in the codebase?
Is the code properly typed?
Is the code hardened?
Do we have enough tests?
Do we need regression tests?
Are tests using fake timers where appropriate? (e.g., debounce/throttle, retry backoff, timeout branches, delayed callbacks, polling loops)
Do not add performative tests, ensure tests are real and there are no regressions.
Do you see any follow-up refactors we should do?
Take your time, fix it properly, refactor if necessary.
Did any changes introduce any potential security vulnerabilities?
```
Stop and escalate instead of continuing if:
- You cannot verify behavior changes with meaningful tests or validation.
- Fixing findings requires broad architecture changes outside safe PR scope.
- Security hardening requirements remain unresolved.
### 3) `merge-pr`
Purpose:
- Merge only after review and prep artifacts are present and checks are green.
- Use deterministic squash merge flow (`--match-head-commit` + explicit subject/body with co-author trailer), then verify the PR ends in `MERGED` state.
- If no required checks are configured on the PR, treat that as acceptable and continue after branch-up-to-date validation.
Go or no-go checklist before merge:
- All BLOCKER and IMPORTANT findings are resolved.
- Verification is meaningful and regression risk is acceptably low.
- Docs and changelog are updated when required.
- Required CI checks are green and the branch is not behind `main`.
Expected output:
- Successful merge commit and recorded merge SHA.
- Worktree cleanup after successful merge.
- Comment on PR indicating merge was successful.
Maintainer checkpoint after merge:
- Were any refactors intentionally deferred and now need follow-up issue(s)?
- Did this reveal broader architecture or test gaps we should address?
- Run `bun scripts/update-clawtributors.ts` if the contributor is new.

View File

@@ -0,0 +1,304 @@
---
name: merge-pr
description: Merge a GitHub PR via squash after /prepare-pr. Use when asked to merge a ready PR. Do not push to main or modify code. Ensure the PR ends in MERGED state and clean up worktrees after success.
---
# Merge PR
## Overview
Merge a prepared PR via deterministic squash merge (`--match-head-commit` + explicit co-author trailer), then clean up the worktree after success.
## Inputs
- Ask for PR number or URL.
- If missing, use `.local/prep.env` from the worktree if present.
- If ambiguous, ask.
## Safety
- Use `gh pr merge --squash` as the only path to `main`.
- Do not run `git push` at all during merge.
- Do not use `gh pr merge --auto` for maintainer landings.
- Do not run gateway stop commands. Do not kill processes. Do not touch port 18792.
## Execution Rule
- Execute the workflow. Do not stop after printing the TODO checklist.
- If delegating, require the delegate to run commands and capture outputs.
## Known Footguns
- If you see "fatal: not a git repository", you are in the wrong directory. Move to the repo root and retry.
- Read `.local/review.md`, `.local/prep.md`, and `.local/prep.env` in the worktree. Do not skip.
- Always merge with `--match-head-commit "$PREP_HEAD_SHA"` to prevent racing stale or changed heads.
- Clean up `.worktrees/pr-<PR>` only after confirmed `MERGED`.
## Completion Criteria
- Ensure `gh pr merge` succeeds.
- Ensure PR state is `MERGED`, never `CLOSED`.
- Record the merge SHA.
- Leave a PR comment with merge SHA and prepared head SHA, and capture the comment URL.
- Run cleanup only after merge success.
## First: Create a TODO Checklist
Create a checklist of all merge steps, print it, then continue and execute the commands.
## Setup: Use a Worktree
Use an isolated worktree for all merge work.
```sh
repo_root=$(git rev-parse --show-toplevel)
cd "$repo_root"
gh auth status
WORKTREE_DIR=".worktrees/pr-<PR>"
cd "$WORKTREE_DIR"
```
Run all commands inside the worktree directory.
## Load Local Artifacts (Mandatory)
Expect these files from earlier steps:
- `.local/review.md` from `/review-pr`
- `.local/prep.md` from `/prepare-pr`
- `.local/prep.env` from `/prepare-pr`
```sh
ls -la .local || true
for required in .local/review.md .local/prep.md .local/prep.env; do
if [ ! -f "$required" ]; then
echo "Missing $required. Stop and run /review-pr then /prepare-pr."
exit 1
fi
done
sed -n '1,120p' .local/review.md
sed -n '1,120p' .local/prep.md
source .local/prep.env
```
## Steps
1. Identify PR meta and verify prepared SHA still matches
```sh
pr_meta_json=$(gh pr view <PR> --json number,title,state,isDraft,author,headRefName,headRefOid,baseRefName,headRepository,body)
printf '%s\n' "$pr_meta_json" | jq '{number,title,state,isDraft,author:.author.login,head:.headRefName,headSha:.headRefOid,base:.baseRefName,headRepo:.headRepository.nameWithOwner,body}'
pr_title=$(printf '%s\n' "$pr_meta_json" | jq -r .title)
pr_number=$(printf '%s\n' "$pr_meta_json" | jq -r .number)
pr_head_sha=$(printf '%s\n' "$pr_meta_json" | jq -r .headRefOid)
contrib=$(printf '%s\n' "$pr_meta_json" | jq -r .author.login)
is_draft=$(printf '%s\n' "$pr_meta_json" | jq -r .isDraft)
if [ "$is_draft" = "true" ]; then
echo "ERROR: PR is draft. Stop and run /prepare-pr after draft is cleared."
exit 1
fi
if [ "$pr_head_sha" != "$PREP_HEAD_SHA" ]; then
echo "ERROR: PR head changed after /prepare-pr (expected $PREP_HEAD_SHA, got $pr_head_sha). Re-run /prepare-pr."
exit 1
fi
```
2. Run sanity checks
Stop if any are true:
- PR is a draft.
- Required checks are failing.
- Branch is behind main.
If checks are pending, wait for completion before merging. Do not use `--auto`.
If no required checks are configured, continue.
```sh
gh pr checks <PR> --required --watch --fail-fast || true
checks_json=$(gh pr checks <PR> --required --json name,bucket,state 2>/tmp/gh-checks.err || true)
if [ -z "$checks_json" ]; then
checks_json='[]'
fi
required_count=$(printf '%s\n' "$checks_json" | jq 'length')
if [ "$required_count" -eq 0 ]; then
echo "No required checks configured for this PR."
fi
printf '%s\n' "$checks_json" | jq -r '.[] | "\(.bucket)\t\(.name)\t\(.state)"'
failed_required=$(printf '%s\n' "$checks_json" | jq '[.[] | select(.bucket=="fail")] | length')
pending_required=$(printf '%s\n' "$checks_json" | jq '[.[] | select(.bucket=="pending")] | length')
if [ "$failed_required" -gt 0 ]; then
echo "Required checks are failing, run /prepare-pr."
exit 1
fi
if [ "$pending_required" -gt 0 ]; then
echo "Required checks are still pending, retry /merge-pr when green."
exit 1
fi
git fetch origin main
git fetch origin pull/<PR>/head:pr-<PR> --force
git merge-base --is-ancestor origin/main pr-<PR> || (echo "PR branch is behind main, run /prepare-pr" && exit 1)
```
If anything is failing or behind, stop and say to run `/prepare-pr`.
3. Merge PR with explicit attribution metadata
```sh
reviewer=$(gh api user --jq .login)
reviewer_id=$(gh api user --jq .id)
coauthor_email=${COAUTHOR_EMAIL:-"$contrib@users.noreply.github.com"}
if [ -z "$coauthor_email" ] || [ "$coauthor_email" = "null" ]; then
contrib_id=$(gh api users/$contrib --jq .id)
coauthor_email="${contrib_id}+${contrib}@users.noreply.github.com"
fi
gh_email=$(gh api user --jq '.email // ""' || true)
git_email=$(git config user.email || true)
mapfile -t reviewer_email_candidates < <(
printf '%s\n' \
"$gh_email" \
"$git_email" \
"${reviewer_id}+${reviewer}@users.noreply.github.com" \
"${reviewer}@users.noreply.github.com" | awk 'NF && !seen[$0]++'
)
[ "${#reviewer_email_candidates[@]}" -gt 0 ] || { echo "ERROR: could not resolve reviewer author email"; exit 1; }
reviewer_email="${reviewer_email_candidates[0]}"
cat > .local/merge-body.txt <<EOF
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: $PREP_HEAD_SHA
Co-authored-by: $contrib <$coauthor_email>
Co-authored-by: $reviewer <$reviewer_email>
Reviewed-by: @$reviewer
EOF
run_merge() {
local email="$1"
local stderr_file
stderr_file=$(mktemp)
if gh pr merge <PR> \
--squash \
--delete-branch \
--match-head-commit "$PREP_HEAD_SHA" \
--author-email "$email" \
--subject "$pr_title (#$pr_number)" \
--body-file .local/merge-body.txt \
2> >(tee "$stderr_file" >&2)
then
rm -f "$stderr_file"
return 0
fi
merge_err=$(cat "$stderr_file")
rm -f "$stderr_file"
return 1
}
merge_err=""
selected_merge_author_email="$reviewer_email"
if ! run_merge "$selected_merge_author_email"; then
if printf '%s\n' "$merge_err" | rg -qi 'author.?email|email.*associated|associated.*email|invalid.*email' && [ "${#reviewer_email_candidates[@]}" -ge 2 ]; then
selected_merge_author_email="${reviewer_email_candidates[1]}"
echo "Retrying once with fallback author email: $selected_merge_author_email"
run_merge "$selected_merge_author_email" || { echo "ERROR: merge failed after fallback retry"; exit 1; }
else
echo "ERROR: merge failed"
exit 1
fi
fi
```
Retry is allowed exactly once when the error is clearly author-email validation.
4. Verify PR state and capture merge SHA
```sh
state=$(gh pr view <PR> --json state --jq .state)
if [ "$state" != "MERGED" ]; then
echo "Merge not finalized yet (state=$state), waiting up to 15 minutes..."
for _ in $(seq 1 90); do
sleep 10
state=$(gh pr view <PR> --json state --jq .state)
if [ "$state" = "MERGED" ]; then
break
fi
done
fi
if [ "$state" != "MERGED" ]; then
echo "ERROR: PR state is $state after waiting. Leave worktree and retry /merge-pr later."
exit 1
fi
merge_sha=$(gh pr view <PR> --json mergeCommit --jq '.mergeCommit.oid')
if [ -z "$merge_sha" ] || [ "$merge_sha" = "null" ]; then
echo "ERROR: merge commit SHA missing."
exit 1
fi
commit_body=$(gh api repos/:owner/:repo/commits/$merge_sha --jq .commit.message)
contrib=${contrib:-$(gh pr view <PR> --json author --jq .author.login)}
reviewer=${reviewer:-$(gh api user --jq .login)}
printf '%s\n' "$commit_body" | rg -q "^Co-authored-by: $contrib <" || { echo "ERROR: missing PR author co-author trailer"; exit 1; }
printf '%s\n' "$commit_body" | rg -q "^Co-authored-by: $reviewer <" || { echo "ERROR: missing reviewer co-author trailer"; exit 1; }
echo "merge_sha=$merge_sha"
```
5. PR comment
Use a multiline heredoc with interpolation enabled.
```sh
ok=0
comment_output=""
for _ in 1 2 3; do
if comment_output=$(gh pr comment <PR> -F - <<EOF
Merged via squash.
- Prepared head SHA: $PREP_HEAD_SHA
- Merge commit: $merge_sha
Thanks @$contrib!
EOF
); then
ok=1
break
fi
sleep 2
done
[ "$ok" -eq 1 ] || { echo "ERROR: failed to post PR comment after retries"; exit 1; }
comment_url=$(printf '%s\n' "$comment_output" | rg -o 'https://github.com/[^ ]+/pull/[0-9]+#issuecomment-[0-9]+' -m1 || true)
[ -n "$comment_url" ] || comment_url="unresolved"
echo "comment_url=$comment_url"
```
6. Clean up worktree only on success
Run cleanup only if step 4 returned `MERGED`.
```sh
cd "$repo_root"
git worktree remove ".worktrees/pr-<PR>" --force
git branch -D temp/pr-<PR> 2>/dev/null || true
git branch -D pr-<PR> 2>/dev/null || true
git branch -D pr-<PR>-prep 2>/dev/null || true
```
## Guardrails
- Worktree only.
- Do not close PRs.
- End in MERGED state.
- Clean up only after merge success.
- Never push to main. Use `gh pr merge --squash` only.
- Do not run `git push` at all in this command.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Merge PR"
short_description: "Merge GitHub PRs via squash"
default_prompt: "Use $merge-pr to merge a GitHub PR via squash after preparation."

View File

@@ -0,0 +1,336 @@
---
name: prepare-pr
description: Prepare a GitHub PR for merge by rebasing onto main, fixing review findings, running gates, committing fixes, and pushing to the PR head branch. Use after /review-pr. Never merge or push to main.
---
# Prepare PR
## Overview
Prepare a PR head branch for merge with review fixes, green gates, and deterministic merge handoff artifacts.
## Inputs
- Ask for PR number or URL.
- If missing, use `.local/pr-meta.env` from the PR worktree if present.
- If ambiguous, ask.
## Safety
- Never push to `main` or `origin/main`. Push only to the PR head branch.
- Never run `git push` without explicit remote and branch. Do not run bare `git push`.
- Do not run gateway stop commands. Do not kill processes. Do not touch port 18792.
- Do not run `git clean -fdx`.
- Do not run `git add -A` or `git add .`.
## Execution Rule
- Execute the workflow. Do not stop after printing the TODO checklist.
- If delegating, require the delegate to run commands and capture outputs.
## Completion Criteria
- Rebase PR commits onto `origin/main`.
- Fix all BLOCKER and IMPORTANT items from `.local/review.md`.
- Commit prep changes with required subject format.
- Run required gates and pass (`pnpm test` may be skipped only for high-confidence docs-only changes).
- Push the updated HEAD back to the PR head branch.
- Write `.local/prep.md` and `.local/prep.env`.
- Output exactly: `PR is ready for /mergepr`.
## First: Create a TODO Checklist
Create a checklist of all prep steps, print it, then continue and execute the commands.
## Setup: Use a Worktree
Use an isolated worktree for all prep work.
```sh
repo_root=$(git rev-parse --show-toplevel)
cd "$repo_root"
gh auth status
WORKTREE_DIR=".worktrees/pr-<PR>"
if [ ! -d "$WORKTREE_DIR" ]; then
git fetch origin main
git worktree add "$WORKTREE_DIR" -b temp/pr-<PR> origin/main
fi
cd "$WORKTREE_DIR"
mkdir -p .local
```
Run all commands inside the worktree directory.
## Load Review Artifacts (Mandatory)
```sh
if [ ! -f .local/review.md ]; then
echo "Missing .local/review.md. Run /review-pr first and save findings."
exit 1
fi
if [ ! -f .local/pr-meta.env ]; then
echo "Missing .local/pr-meta.env. Run /review-pr first and save metadata."
exit 1
fi
sed -n '1,220p' .local/review.md
source .local/pr-meta.env
```
## Steps
1. Identify PR meta with one API call
```sh
pr_meta_json=$(gh pr view <PR> --json number,title,author,headRefName,headRefOid,baseRefName,headRepository,headRepositoryOwner,body)
printf '%s\n' "$pr_meta_json" | jq '{number,title,author:.author.login,head:.headRefName,headSha:.headRefOid,base:.baseRefName,headRepo:.headRepository.nameWithOwner,headRepoOwner:.headRepositoryOwner.login,headRepoName:.headRepository.name,body}'
pr_number=$(printf '%s\n' "$pr_meta_json" | jq -r .number)
contrib=$(printf '%s\n' "$pr_meta_json" | jq -r .author.login)
head=$(printf '%s\n' "$pr_meta_json" | jq -r .headRefName)
pr_head_sha_before=$(printf '%s\n' "$pr_meta_json" | jq -r .headRefOid)
head_owner=$(printf '%s\n' "$pr_meta_json" | jq -r '.headRepositoryOwner.login // empty')
head_repo_name=$(printf '%s\n' "$pr_meta_json" | jq -r '.headRepository.name // empty')
head_repo_url=$(printf '%s\n' "$pr_meta_json" | jq -r '.headRepository.url // empty')
if [ -n "${PR_HEAD:-}" ] && [ "$head" != "$PR_HEAD" ]; then
echo "ERROR: PR head branch changed from $PR_HEAD to $head. Re-run /review-pr."
exit 1
fi
```
2. Fetch PR head and rebase on latest `origin/main`
```sh
git fetch origin pull/<PR>/head:pr-<PR> --force
git checkout -B pr-<PR>-prep pr-<PR>
git fetch origin main
git rebase origin/main
```
If conflicts happen:
- Resolve each conflicted file.
- Run `git add <resolved_file>` for each file.
- Run `git rebase --continue`.
If the rebase gets confusing or you resolve conflicts 3 or more times, stop and report.
3. Fix issues from `.local/review.md`
- Fix all BLOCKER and IMPORTANT items.
- NITs are optional.
- Keep scope tight.
Keep a running log in `.local/prep.md`:
- List which review items you fixed.
- List which files you touched.
- Note behavior changes.
4. Optional quick feedback tests before full gates
Targeted tests are optional quick feedback, not a substitute for full gates.
If running targeted tests in a fresh worktree:
```sh
if [ ! -x node_modules/.bin/vitest ]; then
pnpm install --frozen-lockfile
fi
```
5. Commit prep fixes with required subject format
Use `scripts/committer` with explicit file paths.
Required subject format:
- `fix: <summary> (openclaw#<PR>) thanks @<author>`
```sh
commit_msg="fix: <summary> (openclaw#$pr_number) thanks @$contrib"
scripts/committer "$commit_msg" <changed file 1> <changed file 2> ...
```
If there are no local changes, do not create a no-op commit.
Post-commit validation (mandatory):
```sh
subject=$(git log -1 --pretty=%s)
echo "$subject" | rg -q "openclaw#$pr_number" || { echo "ERROR: commit subject missing openclaw#$pr_number"; exit 1; }
echo "$subject" | rg -q "thanks @$contrib" || { echo "ERROR: commit subject missing thanks @$contrib"; exit 1; }
```
6. Decide verification mode and run required gates before pushing
If you are highly confident the change is docs-only, you may skip `pnpm test`.
High-confidence docs-only criteria (all must be true):
- Every changed file is documentation-only (`docs/**`, `README*.md`, `CHANGELOG.md`, `*.md`, `*.mdx`, `mintlify.json`, `docs.json`).
- No code, runtime, test, dependency, or build config files changed (`src/**`, `extensions/**`, `apps/**`, `package.json`, lockfiles, TS/JS config, test files, scripts).
- `.local/review.md` does not call for non-doc behavior fixes.
Suggested check:
```sh
changed_files=$(git diff --name-only origin/main...HEAD)
non_docs=$(printf "%s\n" "$changed_files" | grep -Ev '^(docs/|README.*\.md$|CHANGELOG\.md$|.*\.md$|.*\.mdx$|mintlify\.json$|docs\.json$)' || true)
docs_only=false
if [ -n "$changed_files" ] && [ -z "$non_docs" ]; then
docs_only=true
fi
echo "docs_only=$docs_only"
```
Bootstrap dependencies in a fresh worktree before gates:
```sh
if [ ! -d node_modules ]; then
pnpm install --frozen-lockfile
fi
```
Run required gates:
```sh
pnpm build
pnpm check
if [ "$docs_only" = "true" ]; then
echo "Docs-only change detected with high confidence; skipping pnpm test." | tee -a .local/prep.md
else
pnpm test
fi
```
Require all required gates to pass. If something fails, fix, commit, and rerun. Allow at most 3 fix-and-rerun cycles.
7. Push safely to the PR head branch
Build `prhead` from owner/name first, then validate remote branch SHA before push.
```sh
if [ -n "$head_owner" ] && [ -n "$head_repo_name" ]; then
head_repo_push_url="https://github.com/$head_owner/$head_repo_name.git"
elif [ -n "$head_repo_url" ] && [ "$head_repo_url" != "null" ]; then
case "$head_repo_url" in
*.git) head_repo_push_url="$head_repo_url" ;;
*) head_repo_push_url="$head_repo_url.git" ;;
esac
else
echo "ERROR: unable to determine PR head repo push URL"
exit 1
fi
git remote add prhead "$head_repo_push_url" 2>/dev/null || git remote set-url prhead "$head_repo_push_url"
echo "Pushing to branch: $head"
if [ "$head" = "main" ] || [ "$head" = "master" ]; then
echo "ERROR: head branch is main/master. This is wrong. Stopping."
exit 1
fi
remote_sha=$(git ls-remote prhead "refs/heads/$head" | awk '{print $1}')
if [ -z "$remote_sha" ]; then
echo "ERROR: remote branch refs/heads/$head not found on prhead"
exit 1
fi
if [ "$remote_sha" != "$pr_head_sha_before" ]; then
echo "ERROR: expected remote SHA $pr_head_sha_before, got $remote_sha. Re-fetch metadata and rebase first."
exit 1
fi
git push --force-with-lease=refs/heads/$head:$pr_head_sha_before prhead HEAD:$head || push_failed=1
```
If lease push fails because head moved, perform one automatic retry:
```sh
if [ "${push_failed:-0}" = "1" ]; then
echo "Lease push failed, retrying once with fresh PR head..."
pr_head_sha_before=$(gh pr view <PR> --json headRefOid --jq .headRefOid)
git fetch origin pull/<PR>/head:pr-<PR>-latest --force
git rebase pr-<PR>-latest
pnpm build
pnpm check
if [ "$docs_only" != "true" ]; then
pnpm test
fi
git push --force-with-lease=refs/heads/$head:$pr_head_sha_before prhead HEAD:$head
fi
```
8. Verify PR head and base relation (Mandatory)
```sh
prep_head_sha=$(git rev-parse HEAD)
pr_head_sha_after=$(gh pr view <PR> --json headRefOid --jq .headRefOid)
if [ "$prep_head_sha" != "$pr_head_sha_after" ]; then
echo "ERROR: pushed head SHA does not match PR head SHA."
exit 1
fi
git fetch origin main
git fetch origin pull/<PR>/head:pr-<PR>-verify --force
git merge-base --is-ancestor origin/main pr-<PR>-verify && echo "PR is up to date with main" || (echo "ERROR: PR is still behind main, rebase again" && exit 1)
git branch -D pr-<PR>-verify 2>/dev/null || true
```
9. Write prep summary artifacts (Mandatory)
Write `.local/prep.md` and `.local/prep.env` for merge handoff.
```sh
contrib_id=$(gh api users/$contrib --jq .id)
coauthor_email="${contrib_id}+${contrib}@users.noreply.github.com"
cat > .local/prep.env <<EOF_ENV
PR_NUMBER=$pr_number
PR_AUTHOR=$contrib
PR_HEAD=$head
PR_HEAD_SHA_BEFORE=$pr_head_sha_before
PREP_HEAD_SHA=$prep_head_sha
COAUTHOR_EMAIL=$coauthor_email
EOF_ENV
ls -la .local/prep.md .local/prep.env
wc -l .local/prep.md .local/prep.env
```
10. Output
Include a diff stat summary:
```sh
git diff --stat origin/main..HEAD
git diff --shortstat origin/main..HEAD
```
Report totals: X files changed, Y insertions(+), Z deletions(-).
If gates passed and push succeeded, print exactly:
```
PR is ready for /mergepr
```
Otherwise, list remaining failures and stop.
## Guardrails
- Worktree only.
- Do not delete the worktree on success. `/mergepr` may reuse it.
- Do not run `gh pr merge`.
- Never push to main. Only push to the PR head branch.
- Run and pass all required gates before pushing. `pnpm test` may be skipped only for high-confidence docs-only changes, and the skip must be explicitly recorded in `.local/prep.md`.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Prepare PR"
short_description: "Prepare GitHub PRs for merge"
default_prompt: "Use $prepare-pr to prep a GitHub PR for merge without merging."

View File

@@ -0,0 +1,253 @@
---
name: review-pr
description: Review-only GitHub pull request analysis with the gh CLI. Use when asked to review a PR, provide structured feedback, or assess readiness to land. Do not merge, push, or make code changes you intend to keep.
---
# Review PR
## Overview
Perform a thorough review-only PR assessment and return a structured recommendation on readiness for /prepare-pr.
## Inputs
- Ask for PR number or URL.
- If missing, always ask. Never auto-detect from conversation.
- If ambiguous, ask.
## Safety
- Never push to `main` or `origin/main`, not during review, not ever.
- Do not run `git push` at all during review. Treat review as read only.
- Do not stop or kill the gateway. Do not run gateway stop commands. Do not kill processes on port 18792.
## Execution Rule
- Execute the workflow. Do not stop after printing the TODO checklist.
- If delegating, require the delegate to run commands and capture outputs, not a plan.
## Known Failure Modes
- If you see "fatal: not a git repository", you are in the wrong directory. Move to the repository root and retry.
- Do not stop after printing the checklist. That is not completion.
## Writing Style for Output
- Write casual and direct.
- Avoid em dashes and en dashes. Use commas or separate sentences.
## Completion Criteria
- Run the commands in the worktree and inspect the PR directly.
- Produce the structured review sections A through J.
- Save the full review to `.local/review.md` inside the worktree.
- Save PR metadata handoff to `.local/pr-meta.env` inside the worktree.
## First: Create a TODO Checklist
Create a checklist of all review steps, print it, then continue and execute the commands.
## Setup: Use a Worktree
Use an isolated worktree for all review work.
```sh
repo_root=$(git rev-parse --show-toplevel)
cd "$repo_root"
gh auth status
WORKTREE_DIR=".worktrees/pr-<PR>"
git fetch origin main
# Reuse existing worktree if it exists, otherwise create new
if [ -d "$WORKTREE_DIR" ]; then
git worktree list
cd "$WORKTREE_DIR"
git fetch origin main
git checkout -B temp/pr-<PR> origin/main
else
git worktree add "$WORKTREE_DIR" -b temp/pr-<PR> origin/main
cd "$WORKTREE_DIR"
fi
# Create local scratch space that persists across /review-pr to /prepare-pr to /merge-pr
mkdir -p .local
```
Run all commands inside the worktree directory.
Start on `origin/main` so you can check for existing implementations before looking at PR code.
## Steps
1. Identify PR meta and context
```sh
pr_meta_json=$(gh pr view <PR> --json number,title,state,isDraft,author,baseRefName,headRefName,headRefOid,headRepository,url,body,labels,assignees,reviewRequests,files,additions,deletions,statusCheckRollup)
printf '%s\n' "$pr_meta_json" | jq '{number,title,url,state,isDraft,author:.author.login,base:.baseRefName,head:.headRefName,headSha:.headRefOid,headRepo:.headRepository.nameWithOwner,additions,deletions,files:(.files|length),body}'
cat > .local/pr-meta.env <<EOF
PR_NUMBER=$(printf '%s\n' "$pr_meta_json" | jq -r .number)
PR_URL=$(printf '%s\n' "$pr_meta_json" | jq -r .url)
PR_AUTHOR=$(printf '%s\n' "$pr_meta_json" | jq -r .author.login)
PR_BASE=$(printf '%s\n' "$pr_meta_json" | jq -r .baseRefName)
PR_HEAD=$(printf '%s\n' "$pr_meta_json" | jq -r .headRefName)
PR_HEAD_SHA=$(printf '%s\n' "$pr_meta_json" | jq -r .headRefOid)
PR_HEAD_REPO=$(printf '%s\n' "$pr_meta_json" | jq -r .headRepository.nameWithOwner)
EOF
ls -la .local/pr-meta.env
```
2. Check if this already exists in main before looking at the PR branch
- Identify the core feature or fix from the PR title and description.
- Search for existing implementations using keywords from the PR title, changed file paths, and function or component names from the diff.
```sh
# Use keywords from the PR title and changed files
rg -n "<keyword_from_pr_title>" -S src packages apps ui || true
rg -n "<function_or_component_name>" -S src packages apps ui || true
git log --oneline --all --grep="<keyword_from_pr_title>" | head -20
```
If it already exists, call it out as a BLOCKER or at least IMPORTANT.
3. Claim the PR
Assign yourself so others know someone is reviewing. Skip if the PR looks like spam or is a draft you plan to recommend closing.
```sh
gh_user=$(gh api user --jq .login)
gh pr edit <PR> --add-assignee "$gh_user" || echo "Could not assign reviewer, continuing"
```
4. Read the PR description carefully
Use the body from step 1. Summarize goal, scope, and missing context.
5. Read the diff thoroughly
Minimum:
```sh
gh pr diff <PR>
```
If you need full code context locally, fetch the PR head to a local ref and diff it. Do not create a merge commit.
```sh
git fetch origin pull/<PR>/head:pr-<PR> --force
mb=$(git merge-base origin/main pr-<PR>)
# Show only this PR patch relative to merge-base, not total branch drift
git diff --stat "$mb"..pr-<PR>
git diff "$mb"..pr-<PR>
```
If you want to browse the PR version of files directly, temporarily check out `pr-<PR>` in the worktree. Do not commit or push. Return to `temp/pr-<PR>` and reset to `origin/main` afterward.
```sh
# Use only if needed
# git checkout pr-<PR>
# git branch --show-current
# ...inspect files...
git checkout temp/pr-<PR>
git checkout -B temp/pr-<PR> origin/main
git branch --show-current
```
6. Validate the change is needed and valuable
Be honest. Call out low value AI slop.
7. Evaluate implementation quality
Review correctness, design, performance, and ergonomics.
8. Perform a security review
Assume OpenClaw subagents run with full disk access, including git, gh, and shell. Check auth, input validation, secrets, dependencies, tool safety, and privacy.
9. Review tests and verification
Identify what exists, what is missing, and what would be a minimal regression test.
If you run local tests in the worktree, bootstrap dependencies first:
```sh
if [ ! -x node_modules/.bin/vitest ]; then
pnpm install --frozen-lockfile
fi
```
10. Check docs
Check if the PR touches code with related documentation such as README, docs, inline API docs, or config examples.
- If docs exist for the changed area and the PR does not update them, flag as IMPORTANT.
- If the PR adds a new feature or config option with no docs, flag as IMPORTANT.
- If the change is purely internal with no user-facing impact, skip this.
11. Check changelog
Check if `CHANGELOG.md` exists and whether the PR warrants an entry.
- If the project has a changelog and the PR is user-facing, flag missing entry as IMPORTANT.
- Leave the change for /prepare-pr, only flag it here.
12. Answer the key question
Decide if /prepare-pr can fix issues or the contributor must update the PR.
13. Save findings to the worktree
Write the full structured review sections A through J to `.local/review.md`.
Create or overwrite the file and verify it exists and is non-empty.
```sh
ls -la .local/review.md
wc -l .local/review.md
```
14. Output the structured review
Produce a review that matches what you saved to `.local/review.md`.
A) TL;DR recommendation
- One of: READY FOR /prepare-pr | NEEDS WORK | NEEDS DISCUSSION | NOT USEFUL (CLOSE)
- 1 to 3 sentences.
B) What changed
C) What is good
D) Security findings
E) Concerns or questions (actionable)
- Numbered list.
- Mark each item as BLOCKER, IMPORTANT, or NIT.
- For each, point to file or area and propose a concrete fix.
F) Tests
G) Docs status
- State if related docs are up to date, missing, or not applicable.
H) Changelog
- State if `CHANGELOG.md` needs an entry and which category.
I) Follow ups (optional)
J) Suggested PR comment (optional)
## Guardrails
- Worktree only.
- Do not delete the worktree after review.
- Review only, do not merge, do not push.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Review PR"
short_description: "Review GitHub PRs without merging"
default_prompt: "Use $review-pr to perform a thorough, review-only GitHub PR review."

View File

@@ -0,0 +1,249 @@
# PR Workflow for Maintainers
Please read this in full and do not skip sections.
This is the single source of truth for the maintainer PR workflow.
## Triage order
Process PRs **oldest to newest**. Older PRs are more likely to have merge conflicts and stale dependencies; resolving them first keeps the queue healthy and avoids snowballing rebase pain.
## Working rule
Skills execute workflow. Maintainers provide judgment.
Always pause between skills to evaluate technical direction, not just command success.
These three skills must be used in order:
1. `review-pr` — review only, produce findings
2. `prepare-pr` — rebase, fix, gate, push to PR head branch
3. `merge-pr` — squash-merge, verify MERGED state, clean up
They are necessary, but not sufficient. Maintainers must steer between steps and understand the code before moving forward.
Treat PRs as reports first, code second.
If submitted code is low quality, ignore it and implement the best solution for the problem.
Do not continue if you cannot verify the problem is real or test the fix.
## Script-first contract
Skill runs should invoke these wrappers automatically. You only need to run them manually when debugging or doing an explicit script-only run:
- `scripts/pr-review <PR>`
- `scripts/pr review-checkout-main <PR>` or `scripts/pr review-checkout-pr <PR>` while reviewing
- `scripts/pr review-guard <PR>` before writing review outputs
- `scripts/pr review-validate-artifacts <PR>` after writing outputs
- `scripts/pr-prepare init <PR>`
- `scripts/pr-prepare validate-commit <PR>`
- `scripts/pr-prepare gates <PR>`
- `scripts/pr-prepare push <PR>`
- Optional one-shot prepare: `scripts/pr-prepare run <PR>`
- `scripts/pr-merge <PR>` (verify-only; short form remains backward compatible)
- `scripts/pr-merge verify <PR>` (verify-only)
- Optional one-shot merge: `scripts/pr-merge run <PR>`
These wrappers run shared preflight checks and generate deterministic artifacts. They are designed to work from repo root or PR worktree cwd.
## Required artifacts
- `.local/pr-meta.json` and `.local/pr-meta.env` from review init.
- `.local/review.md` and `.local/review.json` from review output.
- `.local/prep-context.env` and `.local/prep.md` from prepare.
- `.local/prep.env` from prepare completion.
## Structured review handoff
`review-pr` must write `.local/review.json`.
In normal skill runs this is handled automatically. Use `scripts/pr review-artifacts-init <PR>` and `scripts/pr review-tests <PR> ...` manually only for debugging or explicit script-only runs.
Minimum schema:
```json
{
"recommendation": "READY FOR /prepare-pr",
"findings": [
{
"id": "F1",
"severity": "IMPORTANT",
"title": "Missing changelog entry",
"area": "CHANGELOG.md",
"fix": "Add a Fixes entry for PR #<PR>"
}
],
"tests": {
"ran": ["pnpm test -- ..."],
"gaps": ["..."],
"result": "pass"
}
}
```
`prepare-pr` resolves all `BLOCKER` and `IMPORTANT` findings from this file.
## Coding Agent
Use ChatGPT 5.3 Codex High. Fall back to 5.2 Codex High or 5.3 Codex Medium if necessary.
## PR quality bar
- Do not trust PR code by default.
- Do not merge changes you cannot validate with a reproducible problem and a tested fix.
- Keep types strict. Do not use `any` in implementation code.
- Keep external-input boundaries typed and validated, including CLI input, environment variables, network payloads, and tool output.
- Keep implementations properly scoped. Fix root causes, not local symptoms.
- Identify and reuse canonical sources of truth so behavior does not drift across the codebase.
- Harden changes. Always evaluate security impact and abuse paths.
- Understand the system before changing it. Never make the codebase messier just to clear a PR queue.
## Rebase and conflict resolution
Before any substantive review or prep work, **always rebase the PR branch onto current `main` and resolve merge conflicts first**. A PR that cannot cleanly rebase is not ready for review — fix conflicts before evaluating correctness.
- During `prepare-pr`: rebase onto `main` as the first step, before fixing findings or running gates.
- If conflicts are complex or touch areas you do not understand, stop and escalate.
- Prefer **rebase** for linear history; **squash** when commit history is messy or unhelpful.
## Commit and changelog rules
- In normal `prepare-pr` runs, commits are created via `scripts/committer "<msg>" <file...>`. Use it manually only when operating outside the skill flow; avoid manual `git add`/`git commit` so staging stays scoped.
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
- During `prepare-pr`, use this commit subject format: `fix: <summary> (openclaw#<PR>) thanks @<pr-author>`.
- Group related changes; avoid bundling unrelated refactors.
- Changelog workflow: keep the latest released version at the top (no `Unreleased`); after publishing, bump the version and start a new top section.
- When working on a PR: add a changelog entry with the PR number and thank the contributor.
- When working on an issue: reference the issue in the changelog entry.
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
## Gate policy
In fresh worktrees, dependency bootstrap is handled by wrappers before local gates. Manual equivalent:
```sh
pnpm install --frozen-lockfile
```
Gate set:
- Always: `pnpm build`, `pnpm check`
- `pnpm test` required unless high-confidence docs-only criteria pass.
## Co-contributor and clawtributors
- If we squash, add the PR author as a co-contributor in the commit body using a `Co-authored-by:` trailer.
- When maintainer prepares and merges the PR, add the maintainer as an additional `Co-authored-by:` trailer too.
- Avoid `--auto` merges for maintainer landings. Merge only after checks are green so the maintainer account is the actor and attribution is deterministic.
- For squash merges, set `--author-email` to a reviewer-owned email with fallback candidates; if merge fails due to author-email validation, retry once with the next candidate.
- If you review a PR and later do work on it, land via merge/squash (no direct-main commits) and always add the PR author as a co-contributor.
- When merging a PR: leave a PR comment that explains exactly what we did, include the SHA hashes, and record the comment URL in the final report.
- Manual post-merge step for new contributors: run `bun scripts/update-clawtributors.ts` to add their avatar to the README "Thanks to all clawtributors" list, then commit the regenerated README.
## Review mode vs landing mode
- **Review mode (PR link only):** read `gh pr view`/`gh pr diff`; **do not** switch branches; **do not** change code.
- **Landing mode (exception path):** use only when normal `review-pr -> prepare-pr -> merge-pr` flow cannot safely preserve attribution or cannot satisfy branch protection. Create an integration branch from `main`, bring in PR commits (**prefer rebase** for linear history; **merge allowed** when complexity/conflicts make it safer), apply fixes, add changelog (+ thanks + PR #), run full gate **locally before committing** (`pnpm build && pnpm check && pnpm test`), commit, merge back to `main`, then `git switch main` (never stay on a topic branch after landing). Important: the contributor needs to be in the git graph after this!
## Pre-review safety checks
- Before starting a review when a GH Issue/PR is pasted: `review-pr`/`scripts/pr-review` should create and use an isolated `.worktrees/pr-<PR>` checkout from `origin/main` automatically. Do not require a clean main checkout, and do not run `git pull` in a dirty main checkout.
- PR review calls: prefer a single `gh pr view --json ...` to batch metadata/comments; run `gh pr diff` only when needed.
- PRs should summarize scope, note testing performed, and mention any user-facing changes or new flags.
- Read `docs/help/submitting-a-pr.md` ([Submitting a PR](https://docs.openclaw.ai/help/submitting-a-pr)) for what we expect from contributors.
## Unified workflow
Entry criteria:
- PR URL/number is known.
- Problem statement is clear enough to attempt reproduction.
- A realistic verification path exists (tests, integration checks, or explicit manual validation).
### 1) `review-pr`
Purpose:
- Review only: correctness, value, security risk, tests, docs, and changelog impact.
- Produce structured findings and a recommendation.
Expected output:
- Recommendation: ready, needs work, needs discussion, or close.
- `.local/review.md` with actionable findings.
Maintainer checkpoint before `prepare-pr`:
```
What problem are they trying to solve?
What is the most optimal implementation?
Can we fix up everything?
Do we have any questions?
```
Stop and escalate instead of continuing if:
- The problem cannot be reproduced or confirmed.
- The proposed PR scope does not match the stated problem.
- The design introduces unresolved security or trust-boundary concerns.
### 2) `prepare-pr`
Purpose:
- Make the PR merge-ready on its head branch.
- Rebase onto current `main` first, then fix blocker/important findings, then run gates.
- In fresh worktrees, bootstrap dependencies before local gates (`pnpm install --frozen-lockfile`).
Expected output:
- Updated code and tests on the PR head branch.
- `.local/prep.md` with changes, verification, and current HEAD SHA.
- Final status: `PR is ready for /merge-pr`.
Maintainer checkpoint before `merge-pr`:
```
Is this the most optimal implementation?
Is the code properly scoped?
Is the code properly reusing existing logic in the codebase?
Is the code properly typed?
Is the code hardened?
Do we have enough tests?
Do we need regression tests?
Are tests using fake timers where appropriate? (e.g., debounce/throttle, retry backoff, timeout branches, delayed callbacks, polling loops)
Do not add performative tests, ensure tests are real and there are no regressions.
Do you see any follow-up refactors we should do?
Did any changes introduce any potential security vulnerabilities?
Take your time, fix it properly, refactor if necessary.
```
Stop and escalate instead of continuing if:
- You cannot verify behavior changes with meaningful tests or validation.
- Fixing findings requires broad architecture changes outside safe PR scope.
- Security hardening requirements remain unresolved.
### 3) `merge-pr`
Purpose:
- Merge only after review and prep artifacts are present and checks are green.
- Use deterministic squash merge flow (`--match-head-commit` + explicit subject/body with co-author trailer), then verify the PR ends in `MERGED` state.
- If no required checks are configured on the PR, treat that as acceptable and continue after branch-up-to-date validation.
Go or no-go checklist before merge:
- All BLOCKER and IMPORTANT findings are resolved.
- Verification is meaningful and regression risk is acceptably low.
- Docs and changelog are updated when required.
- Required CI checks are green and the branch is not behind `main`.
Expected output:
- Successful merge commit and recorded merge SHA.
- Worktree cleanup after successful merge.
- Comment on PR indicating merge was successful.
Maintainer checkpoint after merge:
- Were any refactors intentionally deferred and now need follow-up issue(s)?
- Did this reveal broader architecture or test gaps we should address?
- Run `bun scripts/update-clawtributors.ts` if the contributor is new.

View File

@@ -0,0 +1,98 @@
---
name: merge-pr
description: Script-first deterministic squash merge with strict required-check gating, head-SHA pinning, and reliable attribution/commenting.
---
# Merge PR
## Overview
Merge a prepared PR only after deterministic validation.
## Inputs
- Ask for PR number or URL.
- If missing, use `.local/prep.env` from the PR worktree.
## Safety
- Never use `gh pr merge --auto` in this flow.
- Never run `git push` directly.
- Require `--match-head-commit` during merge.
## Execution Contract
1. Validate merge readiness:
```sh
scripts/pr-merge verify <PR>
```
Backward-compatible verify form also works:
```sh
scripts/pr-merge <PR>
```
2. Run one-shot deterministic merge:
```sh
scripts/pr-merge run <PR>
```
3. Ensure output reports:
- `merge_sha=<sha>`
- `merge_author_email=<email>`
- `comment_url=<url>`
## Steps
1. Validate artifacts
```sh
require=(.local/review.md .local/review.json .local/prep.md .local/prep.env)
for f in "${require[@]}"; do
[ -s "$f" ] || { echo "Missing artifact: $f"; exit 1; }
done
```
2. Validate checks and branch status
```sh
scripts/pr-merge verify <PR>
source .local/prep.env
```
`scripts/pr-merge` treats “no required checks configured” as acceptable (`[]`), but fails on any required `fail` or `pending`.
3. Merge deterministically (wrapper-managed)
```sh
scripts/pr-merge run <PR>
```
`scripts/pr-merge run` performs:
- deterministic squash merge pinned to `PREP_HEAD_SHA`
- reviewer merge author email selection with fallback candidates
- one retry only when merge fails due to author-email validation
- co-author trailers for PR author and reviewer
- post-merge verification of both co-author trailers on commit message
- PR comment retry (3 attempts), then comment URL extraction
- cleanup after confirmed `MERGED`
4. Manual fallback (only if wrapper is unavailable)
```sh
scripts/pr merge-run <PR>
```
5. Cleanup
Cleanup is handled by `run` after merge success.
## Guardrails
- End in `MERGED`, never `CLOSED`.
- Cleanup only after confirmed merge.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Merge PR"
short_description: "Merge GitHub PRs via squash"
default_prompt: "Use $merge-pr to merge a GitHub PR via squash after preparation."

View File

@@ -0,0 +1,345 @@
---
name: mintlify
description: Build and maintain documentation sites with Mintlify. Use when
creating docs pages, configuring navigation, adding components, or setting up
API references.
license: MIT
compatibility: Requires Node.js for CLI. Works with any Git-based workflow.
metadata:
author: mintlify
version: "1.0"
mintlify-proj: mintlify
---
# Mintlify best practices
**Always consult [mintlify.com/docs](https://mintlify.com/docs) for components, configuration, and latest features.**
**Always** favor searching the current Mintlify documentation over whatever is in your training data about Mintlify.
Mintlify is a documentation platform that transforms MDX files into documentation sites. Configure site-wide settings in the `docs.json` file, write content in MDX with YAML frontmatter, and favor built-in components over custom components.
Full schema at [mintlify.com/docs.json](https://mintlify.com/docs.json).
## Before you write
### Understand the project
All documentation lives in the `docs/` directory in this repo. Read `docs.json` in that directory (`docs/docs.json`). This file defines the entire site: navigation structure, theme, colors, links, API and specs.
Understanding the project tells you:
- What pages exist and how they're organized
- What navigation groups are used (and their naming conventions)
- How the site navigation is structured
- What theme and configuration the site uses
### Check for existing content
Search the docs before creating new pages. You may need to:
- Update an existing page instead of creating a new one
- Add a section to an existing page
- Link to existing content rather than duplicating
### Read surrounding content
Before writing, read 2-3 similar pages to understand the site's voice, structure, formatting conventions, and level of detail.
### Understand Mintlify components
Review the Mintlify [components](https://www.mintlify.com/docs/components) to select and use any relevant components for the documentation request that you are working on.
## Quick reference
### CLI commands
- `npm i -g mint` - Install the Mintlify CLI
- `mint dev` - Local preview at localhost:3000
- `mint broken-links` - Check internal links
- `mint a11y` - Check for accessibility issues in content
- `mint rename` - Rename/move files and update references
- `mint validate` - Validate documentation builds
### Required files
- `docs.json` - Site configuration (navigation, theme, integrations, etc.). See [global settings](https://mintlify.com/docs/settings/global) for all options.
- `*.mdx` files - Documentation pages with YAML frontmatter
### Example file structure
```
project/
├── docs.json # Site configuration
├── introduction.mdx
├── quickstart.mdx
├── guides/
│ └── example.mdx
├── openapi.yml # API specification
├── images/ # Static assets
│ └── example.png
└── snippets/ # Reusable components
└── component.jsx
```
## Page frontmatter
Every page requires `title` in its frontmatter. Include `description` for SEO and navigation.
```yaml theme={null}
---
title: "Clear, descriptive title"
description: "Concise summary for SEO and navigation."
---
```
Optional frontmatter fields:
- `sidebarTitle`: Short title for sidebar navigation.
- `icon`: Lucide or Font Awesome icon name, URL, or file path.
- `tag`: Label next to the page title in the sidebar (for example, "NEW").
- `mode`: Page layout mode (`default`, `wide`, `custom`).
- `keywords`: Array of terms related to the page content for local search and SEO.
- Any custom YAML fields for use with personalization or conditional content.
## File conventions
- Match existing naming patterns in the directory
- If there are no existing files or inconsistent file naming patterns, use kebab-case: `getting-started.mdx`, `api-reference.mdx`
- Use root-relative paths without file extensions for internal links: `/getting-started/quickstart`
- Do not use relative paths (`../`) or absolute URLs for internal pages
- When you create a new page, add it to `docs.json` navigation or it won't appear in the sidebar
## Organize content
When a user asks about anything related to site-wide configurations, start by understanding the [global settings](https://www.mintlify.com/docs/organize/settings). See if a setting in the `docs.json` file can be updated to achieve what the user wants.
### Navigation
The `navigation` property in `docs.json` controls site structure. Choose one primary pattern at the root level, then nest others within it.
**Choose your primary pattern:**
| Pattern | When to use |
| ------------- | ---------------------------------------------------------------------------------------------- |
| **Groups** | Default. Single audience, straightforward hierarchy |
| **Tabs** | Distinct sections with different audiences (Guides vs API Reference) or content types |
| **Anchors** | Want persistent section links at sidebar top. Good for separating docs from external resources |
| **Dropdowns** | Multiple doc sections users switch between, but not distinct enough for tabs |
| **Products** | Multi-product company with separate documentation per product |
| **Versions** | Maintaining docs for multiple API/product versions simultaneously |
| **Languages** | Localized content |
**Within your primary pattern:**
- **Groups** - Organize related pages. Can nest groups within groups, but keep hierarchy shallow
- **Menus** - Add dropdown navigation within tabs for quick jumps to specific pages
- **`expanded: false`** - Collapse nested groups by default. Use for reference sections users browse selectively
- **`openapi`** - Auto-generate pages from OpenAPI spec. Add at group/tab level to inherit
**Common combinations:**
- Tabs containing groups (most common for docs with API reference)
- Products containing tabs (multi-product SaaS)
- Versions containing tabs (versioned API docs)
- Anchors containing groups (simple docs with external resource links)
### Links and paths
- **Internal links:** Root-relative, no extension: `/getting-started/quickstart`
- **Images:** Store in `/images`, reference as `/images/example.png`
- **External links:** Use full URLs, they open in new tabs automatically
## Customize docs sites
**What to customize where:**
- **Brand colors, fonts, logo** → `docs.json`. See [global settings](https://mintlify.com/docs/settings/global)
- **Component styling, layout tweaks** → `custom.css` at project root
- **Dark mode** → Enabled by default. Only disable with `"appearance": "light"` in `docs.json` if brand requires it
Start with `docs.json`. Only add `custom.css` when you need styling that config doesn't support.
## Write content
### Components
The [components overview](https://mintlify.com/docs/components) organizes all components by purpose: structure content, draw attention, show/hide content, document APIs, link to pages, and add visual context. Start there to find the right component.
**Common decision points:**
| Need | Use |
| -------------------------- | ----------------------- |
| Hide optional details | `<Accordion>` |
| Long code examples | `<Expandable>` |
| User chooses one option | `<Tabs>` |
| Linked navigation cards | `<Card>` in `<Columns>` |
| Sequential instructions | `<Steps>` |
| Code in multiple languages | `<CodeGroup>` |
| API parameters | `<ParamField>` |
| API response fields | `<ResponseField>` |
**Callouts by severity:**
- `<Note>` - Supplementary info, safe to skip
- `<Info>` - Helpful context such as permissions
- `<Tip>` - Recommendations or best practices
- `<Warning>` - Potentially destructive actions
- `<Check>` - Success confirmation
### Reusable content
**When to use snippets:**
- Exact content appears on more than one page
- Complex components you want to maintain in one place
- Shared content across teams/repos
**When NOT to use snippets:**
- Slight variations needed per page (leads to complex props)
Import snippets with `import { Component } from "/path/to/snippet-name.jsx"`.
## Writing standards
### Voice and structure
- Second-person voice ("you")
- Active voice, direct language
- Sentence case for headings ("Getting started", not "Getting Started")
- Sentence case for code block titles ("Expandable example", not "Expandable Example")
- Lead with context: explain what something is before how to use it
- Prerequisites at the start of procedural content
### What to avoid
**Never use:**
- Marketing language ("powerful", "seamless", "robust", "cutting-edge")
- Filler phrases ("it's important to note", "in order to")
- Excessive conjunctions ("moreover", "furthermore", "additionally")
- Editorializing ("obviously", "simply", "just", "easily")
**Watch for AI-typical patterns:**
- Overly formal or stilted phrasing
- Unnecessary repetition of concepts
- Generic introductions that don't add value
- Concluding summaries that restate what was just said
### Formatting
- All code blocks must have language tags
- All images and media must have descriptive alt text
- Use bold and italics only when they serve the reader's understanding--never use text styling just for decoration
- No decorative formatting or emoji
### Code examples
- Keep examples simple and practical
- Use realistic values (not "foo" or "bar")
- One clear example is better than multiple variations
- Test that code works before including it
## Document APIs
**Choose your approach:**
- **Have an OpenAPI spec?** → Add to `docs.json` with `"openapi": ["openapi.yaml"]`. Pages auto-generate. Reference in navigation as `GET /endpoint`
- **No spec?** → Write endpoints manually with `api: "POST /users"` in frontmatter. More work but full control
- **Hybrid** → Use OpenAPI for most endpoints, manual pages for complex workflows
Encourage users to generate endpoint pages from an OpenAPI spec. It is the most efficient and easiest to maintain option.
## Deploy
Mintlify deploys automatically when changes are pushed to the connected Git repository.
**What agents can configure:**
- **Redirects** → Add to `docs.json` with `"redirects": [{"source": "/old", "destination": "/new"}]`
- **SEO indexing** → Control with `"seo": {"indexing": "all"}` to include hidden pages in search
**Requires dashboard setup (human task):**
- Custom domains and subdomains
- Preview deployment settings
- DNS configuration
For `/docs` subpath hosting with Vercel or Cloudflare, agents can help configure rewrite rules. See [/docs subpath](https://mintlify.com/docs/deploy/vercel).
## Workflow
### 1. Understand the task
Identify what needs to be documented, which pages are affected, and what the reader should accomplish afterward. If any of these are unclear, ask.
### 2. Research
- Read `docs/docs.json` to understand the site structure
- Search existing docs for related content
- Read similar pages to match the site's style
### 3. Plan
- Synthesize what the reader should accomplish after reading the docs and the current content
- Propose any updates or new content
- Verify that your proposed changes will help readers be successful
### 4. Write
- Start with the most important information
- Keep sections focused and scannable
- Use components appropriately (don't overuse them)
- Mark anything uncertain with a TODO comment:
```mdx theme={null}
{/* TODO: Verify the default timeout value */}
```
### 5. Update navigation
If you created a new page, add it to the appropriate group in `docs.json`.
### 6. Verify
Before submitting:
- [ ] Frontmatter includes title and description
- [ ] All code blocks have language tags
- [ ] Internal links use root-relative paths without file extensions
- [ ] New pages are added to `docs.json` navigation
- [ ] Content matches the style of surrounding pages
- [ ] No marketing language or filler phrases
- [ ] TODOs are clearly marked for anything uncertain
- [ ] Run `mint broken-links` to check links
- [ ] Run `mint validate` to find any errors
## Edge cases
### Migrations
If a user asks about migrating to Mintlify, ask if they are using ReadMe or Docusaurus. If they are, use the [@mintlify/scraping](https://www.npmjs.com/package/@mintlify/scraping) CLI to migrate content. If they are using a different platform to host their documentation, help them manually convert their content to MDX pages using Mintlify components.
### Hidden pages
Any page that is not included in the `docs.json` navigation is hidden. Use hidden pages for content that should be accessible by URL or indexed for the assistant or search, but not discoverable through the sidebar navigation.
### Exclude pages
The `.mintignore` file is used to exclude files from a documentation repository from being processed.
## Common gotchas
1. **Component imports** - JSX components need explicit import, MDX components don't
2. **Frontmatter required** - Every MDX file needs `title` at minimum
3. **Code block language** - Always specify language identifier
4. **Never use `mint.json`** - `mint.json` is deprecated. Only ever use `docs.json`
## Resources
- [Documentation](https://mintlify.com/docs)
- [Configuration schema](https://mintlify.com/docs.json)
- [Feature requests](https://github.com/orgs/mintlify/discussions/categories/feature-requests)
- [Bugs and feedback](https://github.com/orgs/mintlify/discussions/categories/bugs-feedback)

View File

@@ -0,0 +1,131 @@
---
name: prepare-pr
description: Script-first PR preparation with structured findings resolution, deterministic push safety, and explicit gate execution.
---
# Prepare PR
## Overview
Prepare the PR head branch for merge after `/review-pr`.
## Inputs
- Ask for PR number or URL.
- If missing, use `.local/pr-meta.env` if present in the PR worktree.
## Safety
- Never push to `main`.
- Only push to PR head with explicit `--force-with-lease` against known head SHA.
- Do not run `git clean -fdx`.
- Wrappers are cwd-agnostic; run from repo root or PR worktree.
## Execution Contract
1. Run setup:
```sh
scripts/pr-prepare init <PR>
```
2. Resolve findings from structured review:
- `.local/review.json` is mandatory.
- Resolve all `BLOCKER` and `IMPORTANT` items.
3. Commit with required subject format and validate it.
4. Run gates via wrapper.
5. Push via wrapper (includes pre-push remote verification, one automatic lease-retry path, and post-push API propagation retry).
Optional one-shot path:
```sh
scripts/pr-prepare run <PR>
```
## Steps
1. Setup and artifacts
```sh
scripts/pr-prepare init <PR>
ls -la .local/review.md .local/review.json .local/pr-meta.env .local/prep-context.env
jq . .local/review.json >/dev/null
```
2. Resolve required findings
List required items:
```sh
jq -r '.findings[] | select(.severity=="BLOCKER" or .severity=="IMPORTANT") | "- [\(.severity)] \(.id): \(.title) => \(.fix)"' .local/review.json
```
Fix all required findings. Keep scope tight.
3. Update changelog/docs when required
```sh
jq -r '.changelog' .local/review.json
jq -r '.docs' .local/review.json
```
4. Commit scoped changes
Required commit subject format:
- `fix: <summary> (openclaw#<PR>) thanks @<pr-author>`
Use explicit file list:
```sh
source .local/pr-meta.env
scripts/committer "fix: <summary> (openclaw#$PR_NUMBER) thanks @$PR_AUTHOR" <file1> <file2> ...
```
Validate commit subject:
```sh
scripts/pr-prepare validate-commit <PR>
```
5. Run gates
```sh
scripts/pr-prepare gates <PR>
```
6. Push safely to PR head
```sh
scripts/pr-prepare push <PR>
```
This push step includes:
- robust fork remote resolution from owner/name,
- pre-push remote SHA verification,
- one automatic rebase + gate rerun + retry if lease push fails,
- post-push PR-head propagation retry,
- idempotent behavior when local prep HEAD is already on the PR head,
- post-push SHA verification and `.local/prep.env` generation.
7. Verify handoff artifacts
```sh
ls -la .local/prep.md .local/prep.env
```
8. Output
- Summarize resolved findings and gate results.
- Print exactly: `PR is ready for /merge-pr`.
## Guardrails
- Do not run `gh pr merge` in this skill.
- Do not delete worktree.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Prepare PR"
short_description: "Prepare GitHub PRs for merge"
default_prompt: "Use $prepare-pr to prep a GitHub PR for merge without merging."

View File

@@ -0,0 +1,141 @@
---
name: review-pr
description: Script-first review-only GitHub pull request analysis. Use for deterministic PR review with structured findings handoff to /prepare-pr.
---
# Review PR
## Overview
Perform a read-only review and produce both human and machine-readable outputs.
## Inputs
- Ask for PR number or URL.
- If missing, always ask.
## Safety
- Never push, merge, or modify code intended to keep.
- Work only in `.worktrees/pr-<PR>`.
## Execution Contract
1. Run wrapper setup:
```sh
scripts/pr-review <PR>
```
2. Use explicit branch mode switches:
- Main baseline mode: `scripts/pr review-checkout-main <PR>`
- PR-head mode: `scripts/pr review-checkout-pr <PR>`
3. Before writing review outputs, run branch guard:
```sh
scripts/pr review-guard <PR>
```
4. Write both outputs:
- `.local/review.md` with sections A through J.
- `.local/review.json` with structured findings.
5. Validate artifacts semantically:
```sh
scripts/pr review-validate-artifacts <PR>
```
## Steps
1. Setup and metadata
```sh
scripts/pr-review <PR>
ls -la .local/pr-meta.json .local/pr-meta.env .local/review-context.env .local/review-mode.env
```
2. Existing implementation check on main
```sh
scripts/pr review-checkout-main <PR>
rg -n "<keyword>" -S src extensions apps || true
git log --oneline --all --grep "<keyword>" | head -20
```
3. Claim PR
```sh
gh_user=$(gh api user --jq .login)
gh pr edit <PR> --add-assignee "$gh_user" || echo "Could not assign reviewer, continuing"
```
4. Read PR description and diff
```sh
scripts/pr review-checkout-pr <PR>
gh pr diff <PR>
source .local/review-context.env
git diff --stat "$MERGE_BASE"..pr-<PR>
git diff "$MERGE_BASE"..pr-<PR>
```
5. Optional local tests
Use the wrapper for target validation and executed-test verification:
```sh
scripts/pr review-tests <PR> <test-file> [<test-file> ...]
```
6. Initialize review artifact templates
```sh
scripts/pr review-artifacts-init <PR>
```
7. Produce review outputs
- Fill `.local/review.md` sections A through J.
- Fill `.local/review.json`.
Minimum JSON shape:
```json
{
"recommendation": "READY FOR /prepare-pr",
"findings": [
{
"id": "F1",
"severity": "IMPORTANT",
"title": "...",
"area": "path/or/component",
"fix": "Actionable fix"
}
],
"tests": {
"ran": [],
"gaps": [],
"result": "pass"
},
"docs": "up_to_date|missing|not_applicable",
"changelog": "required|not_required"
}
```
8. Guard + validate before final output
```sh
scripts/pr review-guard <PR>
scripts/pr review-validate-artifacts <PR>
```
## Guardrails
- Keep review read-only.
- Do not delete worktree.
- Use merge-base scoped diff for local context to avoid stale branch drift.

View File

@@ -0,0 +1,4 @@
interface:
display_name: "Review PR"
short_description: "Review GitHub PRs without merging"
default_prompt: "Use $review-pr to perform a thorough, review-only GitHub PR review."

View File

@@ -46,3 +46,15 @@ Swabble/
Core/
Users/
vendor/
# Needed for building the Canvas A2UI bundle during Docker image builds.
# Keep the rest of apps/ and vendor/ excluded to avoid a large build context.
!apps/shared/
!apps/shared/OpenClawKit/
!apps/shared/OpenClawKit/Tools/
!apps/shared/OpenClawKit/Tools/CanvasA2UI/
!apps/shared/OpenClawKit/Tools/CanvasA2UI/**
!vendor/a2ui/
!vendor/a2ui/renderers/
!vendor/a2ui/renderers/lit/
!vendor/a2ui/renderers/lit/**

View File

@@ -1,5 +1,70 @@
# Copy to .env and fill with your Twilio credentials
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here
# Must be a WhatsApp-enabled Twilio number, prefixed with whatsapp:
TWILIO_WHATSAPP_FROM=whatsapp:+17343367101
# OpenClaw .env example
#
# Quick start:
# 1) Copy this file to `.env` (for local runs from this repo), OR to `~/.openclaw/.env` (for launchd/systemd daemons).
# 2) Fill only the values you use.
# 3) Keep real secrets out of git.
#
# Env-source precedence for environment variables (highest -> lowest):
# process env, ./.env, ~/.openclaw/.env, then openclaw.json `env` block.
# Existing non-empty process env vars are not overridden by dotenv/config env loading.
# Note: direct config keys (for example `gateway.auth.token` or channel tokens in openclaw.json)
# are resolved separately from env loading and often take precedence over env fallbacks.
# -----------------------------------------------------------------------------
# Gateway auth + paths
# -----------------------------------------------------------------------------
# Recommended if the gateway binds beyond loopback.
OPENCLAW_GATEWAY_TOKEN=change-me-to-a-long-random-token
# Example generator: openssl rand -hex 32
# Optional alternative auth mode (use token OR password).
# OPENCLAW_GATEWAY_PASSWORD=change-me-to-a-strong-password
# Optional path overrides (defaults shown for reference).
# OPENCLAW_STATE_DIR=~/.openclaw
# OPENCLAW_CONFIG_PATH=~/.openclaw/openclaw.json
# OPENCLAW_HOME=~
# Optional: import missing keys from your login shell profile.
# OPENCLAW_LOAD_SHELL_ENV=1
# OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000
# -----------------------------------------------------------------------------
# Model provider API keys (set at least one)
# -----------------------------------------------------------------------------
# OPENAI_API_KEY=sk-...
# ANTHROPIC_API_KEY=sk-ant-...
# GEMINI_API_KEY=...
# OPENROUTER_API_KEY=sk-or-...
# Optional additional providers
# ZAI_API_KEY=...
# AI_GATEWAY_API_KEY=...
# MINIMAX_API_KEY=...
# SYNTHETIC_API_KEY=...
# -----------------------------------------------------------------------------
# Channels (only set what you enable)
# -----------------------------------------------------------------------------
# TELEGRAM_BOT_TOKEN=123456:ABCDEF...
# DISCORD_BOT_TOKEN=...
# SLACK_BOT_TOKEN=xoxb-...
# SLACK_APP_TOKEN=xapp-...
# Optional channel env fallbacks
# MATTERMOST_BOT_TOKEN=...
# MATTERMOST_URL=https://chat.example.com
# ZALO_BOT_TOKEN=...
# OPENCLAW_TWITCH_ACCESS_TOKEN=oauth:...
# -----------------------------------------------------------------------------
# Tools + voice/media (optional)
# -----------------------------------------------------------------------------
# BRAVE_API_KEY=...
# PERPLEXITY_API_KEY=pplx-...
# FIRECRAWL_API_KEY=...
# ELEVENLABS_API_KEY=...
# XI_API_KEY=... # alias for ElevenLabs
# DEEPGRAM_API_KEY=...

2
.github/FUNDING.yml vendored
View File

@@ -1 +1 @@
custom: ['https://github.com/sponsors/steipete']
custom: ["https://github.com/sponsors/steipete"]

View File

@@ -6,23 +6,29 @@ labels: bug
---
## Summary
What went wrong?
## Steps to reproduce
1.
2.
3.
## Expected behavior
What did you expect to happen?
## Actual behavior
What actually happened?
## Environment
- Clawdbot version:
- OS:
- Install method (pnpm/npx/docker/etc):
## Logs or screenshots
Paste relevant logs or add screenshots (redact secrets).

View File

@@ -1,8 +1,8 @@
blank_issues_enabled: true
blank_issues_enabled: false
contact_links:
- name: Onboarding
url: https://discord.gg/clawd
about: New to Clawdbot? Join Discord for setup guidance from Krill in #help.
about: New to OpenClaw? Join Discord for setup guidance from Krill in \#help.
- name: Support
url: https://discord.gg/clawd
about: Get help from Krill and the community on Discord in #help.
about: Get help from Krill and the community on Discord in \#help.

View File

@@ -6,13 +6,17 @@ labels: enhancement
---
## Summary
Describe the problem you are trying to solve or the opportunity you see.
## Proposed solution
What would you like Clawdbot to do?
## Alternatives considered
Any other approaches you have considered?
## Additional context
Links, screenshots, or related issues.

View File

@@ -12,6 +12,6 @@ paths:
.github/workflows/**/*.yml:
ignore:
# Ignore shellcheck warnings (we run shellcheck separately)
- 'shellcheck reported issue.+'
- "shellcheck reported issue.+"
# Ignore intentional if: false for disabled jobs
- 'constant expression "false" in condition'

View File

@@ -0,0 +1,53 @@
name: Detect docs-only changes
description: >
Outputs docs_only=true when all changed files are under docs/ or are
markdown (.md/.mdx). Fail-safe: if detection fails, outputs false (run
everything). Uses git diff — no API calls, no extra permissions needed.
outputs:
docs_only:
description: "'true' if all changes are docs/markdown, 'false' otherwise"
value: ${{ steps.check.outputs.docs_only }}
docs_changed:
description: "'true' if any changed file is under docs/ or is markdown"
value: ${{ steps.check.outputs.docs_changed }}
runs:
using: composite
steps:
- name: Detect docs-only changes
id: check
shell: bash
run: |
if [ "${{ github.event_name }}" = "push" ]; then
BASE="${{ github.event.before }}"
else
# Use the exact base SHA from the event payload — stable regardless
# of base branch movement (avoids origin/<ref> drift).
BASE="${{ github.event.pull_request.base.sha }}"
fi
# Fail-safe: if we can't diff, assume non-docs (run everything)
CHANGED=$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")
if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then
echo "docs_only=false" >> "$GITHUB_OUTPUT"
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
exit 0
fi
# Check if any changed file is a doc
DOCS=$(echo "$CHANGED" | grep -E '^docs/|\.md$|\.mdx$' || true)
if [ -n "$DOCS" ]; then
echo "docs_changed=true" >> "$GITHUB_OUTPUT"
else
echo "docs_changed=false" >> "$GITHUB_OUTPUT"
fi
# Check if all changed files are docs or markdown
NON_DOCS=$(echo "$CHANGED" | grep -vE '^docs/|\.md$|\.mdx$' || true)
if [ -z "$NON_DOCS" ]; then
echo "docs_only=true" >> "$GITHUB_OUTPUT"
echo "Docs-only change detected — skipping heavy jobs"
else
echo "docs_only=false" >> "$GITHUB_OUTPUT"
fi

View File

@@ -0,0 +1,83 @@
name: Setup Node environment
description: >
Initialize submodules with retry, install Node 22, pnpm, optionally Bun,
and run pnpm install. Requires actions/checkout to run first.
inputs:
node-version:
description: Node.js version to install.
required: false
default: "22.x"
pnpm-version:
description: pnpm version for corepack.
required: false
default: "10.23.0"
install-bun:
description: Whether to install Bun alongside Node.
required: false
default: "true"
frozen-lockfile:
description: Whether to use --frozen-lockfile for install.
required: false
default: "true"
runs:
using: composite
steps:
- name: Checkout submodules (retry)
shell: bash
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
check-latest: true
- name: Setup pnpm + cache store
uses: ./.github/actions/setup-pnpm-store-cache
with:
pnpm-version: ${{ inputs.pnpm-version }}
cache-key-suffix: "node22"
- name: Setup Bun
if: inputs.install-bun == 'true'
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Runtime versions
shell: bash
run: |
node -v
npm -v
pnpm -v
if command -v bun &>/dev/null; then bun -v; fi
- name: Capture node path
shell: bash
run: echo "NODE_BIN=$(dirname "$(node -p "process.execPath")")" >> "$GITHUB_ENV"
- name: Install dependencies
shell: bash
env:
CI: "true"
run: |
export PATH="$NODE_BIN:$PATH"
which node
node -v
pnpm -v
LOCKFILE_FLAG=""
if [ "${{ inputs.frozen-lockfile }}" = "true" ]; then
LOCKFILE_FLAG="--frozen-lockfile"
fi
pnpm install $LOCKFILE_FLAG --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || \
pnpm install $LOCKFILE_FLAG --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true

View File

@@ -0,0 +1,41 @@
name: Setup pnpm + store cache
description: Prepare pnpm via corepack and restore pnpm store cache.
inputs:
pnpm-version:
description: pnpm version to activate via corepack.
required: false
default: "10.23.0"
cache-key-suffix:
description: Suffix appended to the cache key.
required: false
default: "node22"
runs:
using: composite
steps:
- name: Setup pnpm (corepack retry)
shell: bash
run: |
set -euo pipefail
corepack enable
for attempt in 1 2 3; do
if corepack prepare "pnpm@${{ inputs.pnpm-version }}" --activate; then
pnpm -v
exit 0
fi
echo "corepack prepare failed (attempt $attempt/3). Retrying..."
sleep $((attempt * 10))
done
exit 1
- name: Resolve pnpm store path
id: pnpm-store
shell: bash
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
- name: Restore pnpm store cache
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.path }}
key: ${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}-${{ hashFiles('pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-${{ inputs.cache-key-suffix }}-

View File

@@ -0,0 +1,64 @@
# OpenClaw Codebase Patterns
**Always reuse existing code - no redundancy!**
## Tech Stack
- **Runtime**: Node 22+ (Bun also supported for dev/scripts)
- **Language**: TypeScript (ESM, strict mode)
- **Package Manager**: pnpm (keep `pnpm-lock.yaml` in sync)
- **Lint/Format**: Oxlint, Oxfmt (`pnpm check`)
- **Tests**: Vitest with V8 coverage
- **CLI Framework**: Commander + clack/prompts
- **Build**: tsdown (outputs to `dist/`)
## Anti-Redundancy Rules
- Avoid files that just re-export from another file. Import directly from the original source.
- If a function already exists, import it - do NOT create a duplicate in another file.
- Before creating any formatter, utility, or helper, search for existing implementations first.
## Source of Truth Locations
### Formatting Utilities (`src/infra/`)
- **Time formatting**: `src\infra\format-time`
**NEVER create local `formatAge`, `formatDuration`, `formatElapsedTime` functions - import from centralized modules.**
### Terminal Output (`src/terminal/`)
- Tables: `src/terminal/table.ts` (`renderTable`)
- Themes/colors: `src/terminal/theme.ts` (`theme.success`, `theme.muted`, etc.)
- Progress: `src/cli/progress.ts` (spinners, progress bars)
### CLI Patterns
- CLI option wiring: `src/cli/`
- Commands: `src/commands/`
- Dependency injection via `createDefaultDeps`
## Import Conventions
- Use `.js` extension for cross-package imports (ESM)
- Direct imports only - no re-export wrapper files
- Types: `import type { X }` for type-only imports
## Code Quality
- TypeScript (ESM), strict typing, avoid `any`
- Keep files under ~700 LOC - extract helpers when larger
- Colocated tests: `*.test.ts` next to source files
- Run `pnpm check` before commits (lint + format)
- Run `pnpm tsgo` for type checking
## Stack & Commands
- **Package manager**: pnpm (`pnpm install`)
- **Dev**: `pnpm openclaw ...` or `pnpm dev`
- **Type-check**: `pnpm tsgo`
- **Lint/format**: `pnpm check`
- **Tests**: `pnpm test`
- **Build**: `pnpm build`
If you are coding together with a human, do NOT use scripts/committer, but git directly and run the above commands manually to ensure quality.

32
.github/labeler.yml vendored
View File

@@ -9,6 +9,17 @@
- "src/discord/**"
- "extensions/discord/**"
- "docs/channels/discord.md"
"channel: irc":
- changed-files:
- any-glob-to-any-file:
- "extensions/irc/**"
- "docs/channels/irc.md"
"channel: feishu":
- changed-files:
- any-glob-to-any-file:
- "src/feishu/**"
- "extensions/feishu/**"
- "docs/channels/feishu.md"
"channel: googlechat":
- changed-files:
- any-glob-to-any-file:
@@ -73,6 +84,11 @@
- any-glob-to-any-file:
- "extensions/tlon/**"
- "docs/channels/tlon.md"
"channel: twitch":
- changed-files:
- any-glob-to-any-file:
- "extensions/twitch/**"
- "docs/channels/twitch.md"
"channel: voice-call":
- changed-files:
- any-glob-to-any-file:
@@ -220,3 +236,19 @@
- changed-files:
- any-glob-to-any-file:
- "extensions/qwen-portal-auth/**"
"extensions: device-pair":
- changed-files:
- any-glob-to-any-file:
- "extensions/device-pair/**"
"extensions: minimax-portal-auth":
- changed-files:
- any-glob-to-any-file:
- "extensions/minimax-portal-auth/**"
"extensions: phone-control":
- changed-files:
- any-glob-to-any-file:
- "extensions/phone-control/**"
"extensions: talk-voice":
- changed-files:
- any-glob-to-any-file:
- "extensions/talk-voice/**"

View File

@@ -2,25 +2,26 @@ name: Auto response
on:
issues:
types: [labeled]
types: [opened, edited, labeled]
pull_request_target:
types: [labeled]
permissions:
issues: write
pull-requests: write
permissions: {}
jobs:
auto-response:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Handle labeled items
uses: actions/github-script@v7
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
@@ -30,36 +31,131 @@ jobs:
label: "r: skill",
close: true,
message:
"Thanks for the contribution! New skills should be published to Clawdhub for everyone to use. Were keeping the core lean on skills, so Im closing this out.",
"Thanks for the contribution! New skills should be published to [Clawhub](https://clawhub.ai) for everyone to use. Were keeping the core lean on skills, so Im closing this out.",
},
{
label: "r: support",
close: true,
message:
"Please use our support server https://molt.bot/discord and ask in #help or #users-helping-users to resolve this, or follow the stuck FAQ at https://docs.molt.bot/help/faq#im-stuck-whats-the-fastest-way-to-get-unstuck.",
"Please use [our support server](https://discord.gg/clawd) and ask in #help or #users-helping-users to resolve this, or follow the stuck FAQ at https://docs.openclaw.ai/help/faq#im-stuck-whats-the-fastest-way-to-get-unstuck.",
},
{
label: "r: testflight",
close: true,
message: "Not available, build from source.",
},
{
label: "r: third-party-extension",
close: true,
message:
"This would be better made as a third-party extension with our SDK that you maintain yourself. Docs: https://docs.molt.bot/plugin.",
"This would be better made as a third-party extension with our SDK that you maintain yourself. Docs: https://docs.openclaw.ai/plugin.",
},
{
label: "r: moltbook",
close: true,
lock: true,
lockReason: "off-topic",
message:
"OpenClaw is not affiliated with Moltbook, and issues related to Moltbook should not be submitted here.",
},
];
const labelName = context.payload.label?.name;
if (!labelName) {
const triggerLabel = "trigger-response";
const target = context.payload.issue ?? context.payload.pull_request;
if (!target) {
return;
}
const rule = rules.find((item) => item.label === labelName);
const labelSet = new Set(
(target.labels ?? [])
.map((label) => (typeof label === "string" ? label : label?.name))
.filter((name) => typeof name === "string"),
);
const hasTriggerLabel = labelSet.has(triggerLabel);
if (hasTriggerLabel) {
labelSet.delete(triggerLabel);
try {
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: target.number,
name: triggerLabel,
});
} catch (error) {
if (error?.status !== 404) {
throw error;
}
}
}
if (!hasTriggerLabel) {
return;
}
const issue = context.payload.issue;
if (issue) {
const title = issue.title ?? "";
const body = issue.body ?? "";
const haystack = `${title}\n${body}`.toLowerCase();
const hasMoltbookLabel = labelSet.has("r: moltbook");
const hasTestflightLabel = labelSet.has("r: testflight");
const hasSecurityLabel = labelSet.has("security");
if (title.toLowerCase().includes("security") && !hasSecurityLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ["security"],
});
labelSet.add("security");
}
if (title.toLowerCase().includes("testflight") && !hasTestflightLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ["r: testflight"],
});
labelSet.add("r: testflight");
}
if (haystack.includes("moltbook") && !hasMoltbookLabel) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
labels: ["r: moltbook"],
});
labelSet.add("r: moltbook");
}
}
const pullRequest = context.payload.pull_request;
if (pullRequest) {
const labelCount = labelSet.size;
if (labelCount > 20) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
body: "Closing this PR because it has more than 20 labels, which usually means the branch is too noisy. Please recreate the PR from a clean branch.",
});
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
state: "closed",
});
return;
}
}
const rule = rules.find((item) => labelSet.has(item.label));
if (!rule) {
return;
}
const issueNumber = context.payload.issue?.number ?? context.payload.pull_request?.number;
if (!issueNumber) {
return;
}
const issueNumber = target.number;
await github.rest.issues.createComment({
owner: context.repo.owner,
@@ -76,3 +172,12 @@ jobs:
state: "closed",
});
}
if (rule.lock) {
await github.rest.issues.lock({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
lock_reason: rule.lockReason ?? "resolved",
});
}

View File

@@ -2,10 +2,131 @@ name: CI
on:
push:
branches: [main]
pull_request:
concurrency:
group: ci-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
install-check:
# Detect docs-only changes to skip heavy jobs (test, build, Windows, macOS, Android).
# Lint and format always run. Fail-safe: if detection fails, run everything.
docs-scope:
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
docs_changed: ${{ steps.check.outputs.docs_changed }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: false
- name: Detect docs-only changes
id: check
uses: ./.github/actions/detect-docs-changes
# Detect which heavy areas are touched so PRs can skip unrelated expensive jobs.
# Push to main keeps broad coverage.
changed-scope:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: ubuntu-latest
outputs:
run_node: ${{ steps.scope.outputs.run_node }}
run_macos: ${{ steps.scope.outputs.run_macos }}
run_android: ${{ steps.scope.outputs.run_android }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
submodules: false
- name: Detect changed scopes
id: scope
shell: bash
run: |
set -euo pipefail
if [ "${{ github.event_name }}" = "push" ]; then
BASE="${{ github.event.before }}"
else
BASE="${{ github.event.pull_request.base.sha }}"
fi
CHANGED="$(git diff --name-only "$BASE" HEAD 2>/dev/null || echo "UNKNOWN")"
if [ "$CHANGED" = "UNKNOWN" ] || [ -z "$CHANGED" ]; then
# Fail-safe: run broad checks if detection fails.
echo "run_node=true" >> "$GITHUB_OUTPUT"
echo "run_macos=true" >> "$GITHUB_OUTPUT"
echo "run_android=true" >> "$GITHUB_OUTPUT"
exit 0
fi
run_node=false
run_macos=false
run_android=false
has_non_docs=false
has_non_native_non_docs=false
while IFS= read -r path; do
[ -z "$path" ] && continue
case "$path" in
docs/*|*.md|*.mdx)
continue
;;
*)
has_non_docs=true
;;
esac
case "$path" in
# Generated protocol models are already covered by protocol:check and
# should not force the full native macOS lane.
apps/macos/Sources/OpenClawProtocol/*|apps/shared/OpenClawKit/Sources/OpenClawProtocol/*)
;;
apps/macos/*|apps/ios/*|apps/shared/*|Swabble/*)
run_macos=true
;;
esac
case "$path" in
apps/android/*|apps/shared/*)
run_android=true
;;
esac
case "$path" in
src/*|test/*|extensions/*|packages/*|scripts/*|ui/*|.github/*|openclaw.mjs|package.json|pnpm-lock.yaml|pnpm-workspace.yaml|tsconfig*.json|vitest*.ts|tsdown.config.ts|.oxlintrc.json|.oxfmtrc.jsonc)
run_node=true
;;
esac
case "$path" in
apps/android/*|apps/ios/*|apps/macos/*|apps/shared/*|Swabble/*|appcast.xml)
;;
*)
has_non_native_non_docs=true
;;
esac
done <<< "$CHANGED"
# If there are non-doc files outside native app trees, keep Node checks enabled.
if [ "$run_node" = false ] && [ "$has_non_docs" = true ] && [ "$has_non_native_non_docs" = true ]; then
run_node=true
fi
echo "run_node=${run_node}" >> "$GITHUB_OUTPUT"
echo "run_macos=${run_macos}" >> "$GITHUB_OUTPUT"
echo "run_android=${run_android}" >> "$GITHUB_OUTPUT"
# Build dist once for Node-relevant changes and share it with downstream jobs.
build-artifacts:
needs: [docs-scope, changed-scope, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
@@ -13,152 +134,129 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Node.js
uses: actions/setup-node@v4
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: 22.x
check-latest: true
install-bun: "false"
- name: Setup pnpm (corepack retry)
run: |
set -euo pipefail
corepack enable
for attempt in 1 2 3; do
if corepack prepare pnpm@10.23.0 --activate; then
pnpm -v
exit 0
fi
echo "corepack prepare failed (attempt $attempt/3). Retrying..."
sleep $((attempt * 10))
done
exit 1
- name: Build dist
run: pnpm build
- name: Runtime versions
run: |
node -v
npm -v
pnpm -v
- name: Upload dist artifact
uses: actions/upload-artifact@v4
with:
name: dist-build
path: dist/
retention-days: 1
- name: Capture node path
run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
# Validate npm pack contents after build (only on push to main, not PRs).
release-check:
needs: [docs-scope, build-artifacts]
if: github.event_name == 'push' && needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Install dependencies (frozen)
env:
CI: true
run: |
export PATH="$NODE_BIN:$PATH"
which node
node -v
pnpm -v
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
install-bun: "false"
- name: Download dist artifact
uses: actions/download-artifact@v4
with:
name: dist-build
path: dist/
- name: Check release contents
run: pnpm release:check
checks:
needs: [docs-scope, changed-scope, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
fail-fast: false
matrix:
include:
- runtime: node
task: lint
command: pnpm lint
- runtime: node
task: test
command: pnpm canvas:a2ui:bundle && pnpm test
- runtime: node
task: build
command: pnpm build
- runtime: node
task: protocol
command: pnpm protocol:check
- runtime: node
task: format
command: pnpm format
- runtime: bun
task: test
command: pnpm canvas:a2ui:bundle && bunx vitest run
- runtime: bun
task: build
command: bunx tsc -p tsconfig.json
command: pnpm canvas:a2ui:bundle && bunx vitest run --config vitest.unit.config.ts
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
check-latest: true
- name: Setup pnpm (corepack retry)
run: |
set -euo pipefail
corepack enable
for attempt in 1 2 3; do
if corepack prepare pnpm@10.23.0 --activate; then
pnpm -v
exit 0
fi
echo "corepack prepare failed (attempt $attempt/3). Retrying..."
sleep $((attempt * 10))
done
exit 1
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Runtime versions
run: |
node -v
npm -v
bun -v
pnpm -v
- name: Capture node path
run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
- name: Install dependencies
env:
CI: true
run: |
export PATH="$NODE_BIN:$PATH"
which node
node -v
pnpm -v
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
- name: Configure vitest JSON reports
if: matrix.task == 'test' && matrix.runtime == 'node'
run: echo "OPENCLAW_VITEST_REPORT_DIR=$RUNNER_TEMP/vitest-reports" >> "$GITHUB_ENV"
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
- name: Summarize slowest tests
if: matrix.task == 'test' && matrix.runtime == 'node'
run: |
node scripts/vitest-slowest.mjs --dir "$OPENCLAW_VITEST_REPORT_DIR" --top 50 --out "$RUNNER_TEMP/vitest-slowest.md" > /dev/null
echo "Slowest test summary written to $RUNNER_TEMP/vitest-slowest.md"
- name: Upload vitest reports
if: matrix.task == 'test' && matrix.runtime == 'node'
uses: actions/upload-artifact@v4
with:
name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }}
path: |
${{ env.OPENCLAW_VITEST_REPORT_DIR }}
${{ runner.temp }}/vitest-slowest.md
# Types, lint, and format check.
check:
name: "check"
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Check types and lint and oxfmt
run: pnpm check
# Validate docs (format, lint, broken links) only when docs files changed.
check-docs:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_changed == 'true'
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
- name: Check docs
run: pnpm check:docs
secrets:
runs-on: blacksmith-4vcpu-ubuntu-2404
steps:
@@ -185,10 +283,14 @@ jobs:
fi
checks-windows:
needs: [docs-scope, changed-scope, build-artifacts, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_node == 'true')
runs-on: blacksmith-4vcpu-windows-2025
env:
NODE_OPTIONS: --max-old-space-size=4096
CLAWDBOT_TEST_WORKERS: 1
# Keep total concurrency predictable on the 4 vCPU runner:
# `scripts/test-parallel.mjs` runs some vitest suites in parallel processes.
OPENCLAW_TEST_WORKERS: 2
defaults:
run:
shell: bash
@@ -202,9 +304,6 @@ jobs:
- runtime: node
task: test
command: pnpm canvas:a2ui:bundle && pnpm test
- runtime: node
task: build
command: pnpm build
- runtime: node
task: protocol
command: pnpm protocol:check
@@ -214,18 +313,38 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
- name: Try to exclude workspace from Windows Defender (best-effort)
shell: pwsh
run: |
$cmd = Get-Command Add-MpPreference -ErrorAction SilentlyContinue
if (-not $cmd) {
Write-Host "Add-MpPreference not available, skipping Defender exclusions."
exit 0
}
try {
# Defender sometimes intercepts process spawning (vitest workers). If this fails
# (eg hardened images), keep going and rely on worker limiting above.
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE" -ErrorAction Stop
Add-MpPreference -ExclusionProcess "node.exe" -ErrorAction Stop
Write-Host "Defender exclusions applied."
} catch {
Write-Warning "Failed to apply Defender exclusions, continuing. $($_.Exception.Message)"
}
- name: Download dist artifact (lint lane)
if: matrix.task == 'lint'
uses: actions/download-artifact@v4
with:
name: dist-build
path: dist/
- name: Verify dist artifact (lint lane)
if: matrix.task == 'lint'
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
test -s dist/index.js
test -s dist/plugin-sdk/index.js
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -233,19 +352,11 @@ jobs:
node-version: 22.x
check-latest: true
- name: Setup pnpm (corepack retry)
run: |
set -euo pipefail
corepack enable
for attempt in 1 2 3; do
if corepack prepare pnpm@10.23.0 --activate; then
pnpm -v
exit 0
fi
echo "corepack prepare failed (attempt $attempt/3). Retrying..."
sleep $((attempt * 10))
done
exit 1
- name: Setup pnpm + cache store
uses: ./.github/actions/setup-pnpm-store-cache
with:
pnpm-version: "10.23.0"
cache-key-suffix: "node22"
- name: Setup Bun
uses: oven-sh/setup-bun@v2
@@ -272,141 +383,61 @@ jobs:
pnpm -v
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
- name: Configure vitest JSON reports
if: matrix.task == 'test'
run: echo "OPENCLAW_VITEST_REPORT_DIR=$RUNNER_TEMP/vitest-reports" >> "$GITHUB_ENV"
- name: Run ${{ matrix.task }} (${{ matrix.runtime }})
run: ${{ matrix.command }}
checks-macos:
if: github.event_name == 'pull_request'
- name: Summarize slowest tests
if: matrix.task == 'test'
run: |
node scripts/vitest-slowest.mjs --dir "$OPENCLAW_VITEST_REPORT_DIR" --top 50 --out "$RUNNER_TEMP/vitest-slowest.md" > /dev/null
echo "Slowest test summary written to $RUNNER_TEMP/vitest-slowest.md"
- name: Upload vitest reports
if: matrix.task == 'test'
uses: actions/upload-artifact@v4
with:
name: vitest-reports-${{ runner.os }}-${{ matrix.runtime }}
path: |
${{ env.OPENCLAW_VITEST_REPORT_DIR }}
${{ runner.temp }}/vitest-slowest.md
# Consolidated macOS job: runs TS tests + Swift lint/build/test sequentially
# on a single runner. GitHub limits macOS concurrent jobs to 5 per org;
# running 4 separate jobs per PR (as before) starved the queue. One job
# per PR allows 5 PRs to run macOS checks simultaneously.
macos:
needs: [docs-scope, changed-scope, check]
if: github.event_name == 'pull_request' && needs.docs-scope.outputs.docs_only != 'true' && needs.changed-scope.outputs.run_macos == 'true'
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
include:
- task: test
command: pnpm test
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Node.js
uses: actions/setup-node@v4
- name: Setup Node environment
uses: ./.github/actions/setup-node-env
with:
node-version: 22.x
check-latest: true
install-bun: "false"
- name: Setup pnpm (corepack retry)
run: |
set -euo pipefail
corepack enable
for attempt in 1 2 3; do
if corepack prepare pnpm@10.23.0 --activate; then
pnpm -v
exit 0
fi
echo "corepack prepare failed (attempt $attempt/3). Retrying..."
sleep $((attempt * 10))
done
exit 1
- name: Runtime versions
run: |
node -v
npm -v
pnpm -v
- name: Capture node path
run: echo "NODE_BIN=$(dirname \"$(node -p \"process.execPath\")\")" >> "$GITHUB_ENV"
- name: Install dependencies
env:
CI: true
run: |
export PATH="$NODE_BIN:$PATH"
which node
node -v
pnpm -v
pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true || pnpm install --frozen-lockfile --ignore-scripts=false --config.engine-strict=false --config.enable-pre-post-scripts=true
- name: Run ${{ matrix.task }}
# --- Run all checks sequentially (fast gates first) ---
- name: TS tests (macOS)
env:
NODE_OPTIONS: --max-old-space-size=4096
run: ${{ matrix.command }}
macos-app:
if: github.event_name == 'pull_request'
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
include:
- task: lint
command: |
swiftlint --config .swiftlint.yml
swiftformat --lint apps/macos/Sources --config .swiftformat
- task: build
command: |
set -euo pipefail
for attempt in 1 2 3; do
if swift build --package-path apps/macos --configuration release; then
exit 0
fi
echo "swift build failed (attempt $attempt/3). Retrying…"
sleep $((attempt * 20))
done
exit 1
- task: test
command: |
set -euo pipefail
for attempt in 1 2 3; do
if swift test --package-path apps/macos --parallel --enable-code-coverage --show-codecov-path; then
exit 0
fi
echo "swift test failed (attempt $attempt/3). Retrying…"
sleep $((attempt * 20))
done
exit 1
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
run: pnpm test
# --- Xcode/Swift setup ---
- name: Select Xcode 26.1
run: |
sudo xcode-select -s /Applications/Xcode_26.1.app
xcodebuild -version
- name: Install XcodeGen / SwiftLint / SwiftFormat
run: |
brew install xcodegen swiftlint swiftformat
run: brew install xcodegen swiftlint swiftformat
- name: Show toolchain
run: |
@@ -414,8 +445,43 @@ jobs:
xcodebuild -version
swift --version
- name: Run ${{ matrix.task }}
run: ${{ matrix.command }}
- name: Swift lint
run: |
swiftlint --config .swiftlint.yml
swiftformat --lint apps/macos/Sources --config .swiftformat
- name: Cache SwiftPM
uses: actions/cache@v4
with:
path: ~/Library/Caches/org.swift.swiftpm
key: ${{ runner.os }}-swiftpm-${{ hashFiles('apps/macos/Package.resolved') }}
restore-keys: |
${{ runner.os }}-swiftpm-
- name: Swift build (release)
run: |
set -euo pipefail
for attempt in 1 2 3; do
if swift build --package-path apps/macos --configuration release; then
exit 0
fi
echo "swift build failed (attempt $attempt/3). Retrying…"
sleep $((attempt * 20))
done
exit 1
- name: Swift test
run: |
set -euo pipefail
for attempt in 1 2 3; do
if swift test --package-path apps/macos --parallel --enable-code-coverage --show-codecov-path; then
exit 0
fi
echo "swift test failed (attempt $attempt/3). Retrying…"
sleep $((attempt * 20))
done
exit 1
ios:
if: false # ignore iOS in CI for now
runs-on: macos-latest
@@ -425,19 +491,6 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Select Xcode 26.1
run: |
sudo xcode-select -s /Applications/Xcode_26.1.app
@@ -590,6 +643,8 @@ jobs:
PY
android:
needs: [docs-scope, changed-scope, check]
if: needs.docs-scope.outputs.docs_only != 'true' && (github.event_name == 'push' || needs.changed-scope.outputs.run_android == 'true')
runs-on: blacksmith-4vcpu-ubuntu-2404
strategy:
fail-fast: false
@@ -605,19 +660,6 @@ jobs:
with:
submodules: false
- name: Checkout submodules (retry)
run: |
set -euo pipefail
git submodule sync --recursive
for attempt in 1 2 3 4 5; do
if git -c protocol.version=2 submodule update --init --force --depth=1 --recursive; then
exit 0
fi
echo "Submodule update failed (attempt $attempt/5). Retrying…"
sleep $((attempt * 10))
done
exit 1
- name: Setup Java
uses: actions/setup-java@v4
with:

View File

@@ -6,6 +6,12 @@ on:
- main
tags:
- "v*"
paths-ignore:
- "docs/**"
- "**/*.md"
- "**/*.mdx"
- ".agents/**"
- "skills/**"
env:
REGISTRY: ghcr.io
@@ -56,8 +62,8 @@ jobs:
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:amd64
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:amd64,mode=max
provenance: false
push: true
@@ -105,8 +111,8 @@ jobs:
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
tags: ${{ steps.meta.outputs.tags }}
cache-from: type=gha
cache-to: type=gha,mode=max
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:arm64
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}-cache:arm64,mode=max
provenance: false
push: true

138
.github/workflows/formal-conformance.yml vendored Normal file
View File

@@ -0,0 +1,138 @@
name: Formal models (informational conformance)
on:
pull_request:
concurrency:
group: formal-conformance-${{ github.event.pull_request.number || github.ref_name }}
cancel-in-progress: true
jobs:
formal_conformance:
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout openclaw (PR)
uses: actions/checkout@v4
with:
path: openclaw
- name: Checkout formal models
uses: actions/checkout@v4
with:
repository: vignesh07/clawdbot-formal-models
ref: main
path: clawdbot-formal-models
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Regenerate extracted constants from openclaw
run: |
set -euo pipefail
cd clawdbot-formal-models
export OPENCLAW_REPO_DIR="${GITHUB_WORKSPACE}/openclaw"
node scripts/extract-tool-groups.mjs
node scripts/check-tool-group-alias.mjs
# Drift is about extracted artifacts only; compute it before model checking
# to avoid any incidental file touches affecting the result.
- name: Compute drift (generated/*)
id: drift
run: |
set -euo pipefail
cd clawdbot-formal-models
if git diff --quiet -- generated; then
echo "drift=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "drift=true" >> "$GITHUB_OUTPUT"
git diff -- generated > "${GITHUB_WORKSPACE}/formal-models-drift.diff"
- name: Model check (green suite)
run: |
set -euo pipefail
cd clawdbot-formal-models
make \
precedence groups elevated nodes-policy \
attacker approvals approvals-token nodes-pipeline \
gateway-exposure gateway-exposure-v2 gateway-exposure-v2-protected \
gateway-auth-conformance gateway-auth-tailscale gateway-auth-proxy \
pairing pairing-cap pairing-idempotency pairing-refresh pairing-refresh-race \
ingress-gating ingress-idempotency ingress-dedupe-fallback ingress-trace ingress-trace2 \
routing-isolation routing-precedence routing-identitylinks routing-identity-transitive routing-identity-symmetry routing-identity-channel-override \
routing-thread-parent discord-pluralkit \
ingress-retry session-key-stability session-explosion-bound config-normalization \
queue-drain delivery-route-stability delivery-pipeline retry-termination retry-eventual-success \
no-cross-stream multi-event-eventual-emission \
dedupe-collision-fallback crash-restart-dedupe two-worker-dedupe openclaw-session-key-conformance \
routing-thread-parent-channel-override routing-trirule gateway-auth-proxy-header-spoof \
group-alias-check
- name: Model check (negative suite, expected violations)
continue-on-error: true
run: |
set -euo pipefail
cd clawdbot-formal-models
make -k \
precedence-negative groups-negative elevated-negative nodes-policy-negative \
attacker-negative attacker-nodes-negative attacker-nodes-allowlist-negative attacker-nodes-allowlist-negative \
approvals-negative approvals-token-negative nodes-pipeline-negative \
gateway-exposure-negative gateway-exposure-v2-negative gateway-exposure-v2-protected-negative \
gateway-exposure-v2-unsafe-custom gateway-exposure-v2-unsafe-tailnet gateway-exposure-v2-unsafe-auto \
gateway-auth-conformance-negative gateway-auth-tailscale-negative gateway-auth-proxy-negative \
pairing-negative pairing-cap-negative pairing-idempotency-negative pairing-refresh-negative pairing-refresh-race-negative \
ingress-gating-negative ingress-idempotency-negative ingress-dedupe-fallback-negative ingress-trace-negative ingress-trace2-negative \
routing-isolation-negative routing-precedence-negative routing-identitylinks-negative routing-identity-transitive-negative routing-identity-symmetry-negative routing-identity-channel-override-negative \
routing-thread-parent-negative discord-pluralkit-negative \
ingress-retry-negative session-key-stability-negative config-normalization-negative \
queue-drain delivery-route-stability-negative delivery-pipeline-negative retry-termination-negative retry-eventual-success-negative \
no-cross-stream-negative multi-event-eventual-emission-negative \
dedupe-collision-fallback-negative crash-restart-dedupe-negative two-worker-dedupe-negative openclaw-session-key-conformance-negative \
routing-thread-parent-channel-override-negative routing-trirule-negative gateway-auth-proxy-header-spoof-negative
- name: Upload drift diff artifact
if: steps.drift.outputs.drift == 'true'
uses: actions/upload-artifact@v4
with:
name: formal-models-conformance-drift
path: formal-models-drift.diff
- name: Comment on PR (informational)
if: steps.drift.outputs.drift == 'true'
uses: actions/github-script@v7
with:
script: |
const body = [
'⚠️ **Formal models conformance drift detected**',
'',
'The formal models extracted constants (`generated/*`) do not match this openclaw PR.',
'',
'This check is **informational** (not blocking merges yet).',
'See the `formal-models-conformance-drift` artifact for the diff.',
'',
'If this change is intentional, follow up by updating the formal models repo or regenerating the extracted artifacts there.',
].join('\n');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body,
});
- name: Summary
run: |
if [ "${{ steps.drift.outputs.drift }}" = "true" ]; then
echo "Formal conformance drift detected (informational)."
else
echo "Formal conformance: no drift."
fi

View File

@@ -6,8 +6,28 @@ on:
pull_request:
workflow_dispatch:
concurrency:
group: install-smoke-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
docs-scope:
runs-on: ubuntu-latest
outputs:
docs_only: ${{ steps.check.outputs.docs_only }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Detect docs-only changes
id: check
uses: ./.github/actions/detect-docs-changes
install-smoke:
needs: [docs-scope]
if: needs.docs-scope.outputs.docs_only != 'true'
runs-on: ubuntu-latest
steps:
- name: Checkout CLI
@@ -32,8 +52,8 @@ jobs:
- name: Run installer docker tests
env:
CLAWDBOT_INSTALL_URL: https://clawd.bot/install.sh
CLAWDBOT_INSTALL_CLI_URL: https://clawd.bot/install-cli.sh
CLAWDBOT_INSTALL_URL: https://openclaw.ai/install.sh
CLAWDBOT_INSTALL_CLI_URL: https://openclaw.ai/install-cli.sh
CLAWDBOT_NO_ONBOARD: "1"
CLAWDBOT_INSTALL_SMOKE_SKIP_CLI: "1"
CLAWDBOT_INSTALL_SMOKE_SKIP_NONROOT: ${{ github.event_name == 'pull_request' && '1' || '0' }}

View File

@@ -3,22 +3,517 @@ name: Labeler
on:
pull_request_target:
types: [opened, synchronize, reopened]
issues:
types: [opened]
workflow_dispatch:
inputs:
max_prs:
description: "Maximum number of open PRs to process (0 = all)"
required: false
default: "200"
per_page:
description: "PRs per page (1-100)"
required: false
default: "50"
permissions:
contents: read
pull-requests: write
permissions: {}
jobs:
label:
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@v1
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- uses: actions/labeler@v5
- uses: actions/labeler@8558fd74291d67161a8a78ce36a881fa63b766a9 # v5
with:
configuration-path: .github/labeler.yml
repo-token: ${{ steps.app-token.outputs.token }}
sync-labels: true
- name: Apply PR size label
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const pullRequest = context.payload.pull_request;
if (!pullRequest) {
return;
}
const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"];
const labelColor = "b76e79";
for (const label of sizeLabels) {
try {
await github.rest.issues.getLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label,
});
} catch (error) {
if (error?.status !== 404) {
throw error;
}
await github.rest.issues.createLabel({
owner: context.repo.owner,
repo: context.repo.repo,
name: label,
color: labelColor,
});
}
}
const files = await github.paginate(github.rest.pulls.listFiles, {
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pullRequest.number,
per_page: 100,
});
const excludedLockfiles = new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]);
const totalChangedLines = files.reduce((total, file) => {
const path = file.filename ?? "";
if (path === "docs.acp.md" || path.startsWith("docs/") || excludedLockfiles.has(path)) {
return total;
}
return total + (file.additions ?? 0) + (file.deletions ?? 0);
}, 0);
let targetSizeLabel = "size: XL";
if (totalChangedLines < 50) {
targetSizeLabel = "size: XS";
} else if (totalChangedLines < 200) {
targetSizeLabel = "size: S";
} else if (totalChangedLines < 500) {
targetSizeLabel = "size: M";
} else if (totalChangedLines < 1000) {
targetSizeLabel = "size: L";
}
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
per_page: 100,
});
for (const label of currentLabels) {
const name = label.name ?? "";
if (!sizeLabels.includes(name)) {
continue;
}
if (name === targetSizeLabel) {
continue;
}
await github.rest.issues.removeLabel({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
name,
});
}
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pullRequest.number,
labels: [targetSizeLabel],
});
- name: Apply maintainer or trusted-contributor label
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const login = context.payload.pull_request?.user?.login;
if (!login) {
return;
}
const repo = `${context.repo.owner}/${context.repo.repo}`;
const trustedLabel = "trusted-contributor";
const experiencedLabel = "experienced-contributor";
const trustedThreshold = 4;
const experiencedThreshold = 10;
let isMaintainer = false;
try {
const membership = await github.rest.teams.getMembershipForUserInOrg({
org: context.repo.owner,
team_slug: "maintainer",
username: login,
});
isMaintainer = membership?.data?.state === "active";
} catch (error) {
if (error?.status !== 404) {
throw error;
}
}
if (isMaintainer) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.pull_request.number,
labels: ["maintainer"],
});
return;
}
const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
let mergedCount = 0;
try {
const merged = await github.rest.search.issuesAndPullRequests({
q: mergedQuery,
per_page: 1,
});
mergedCount = merged?.data?.total_count ?? 0;
} catch (error) {
if (error?.status !== 422) {
throw error;
}
core.warning(`Skipping merged search for ${login}; treating as 0.`);
}
if (mergedCount >= experiencedThreshold) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.pull_request.number,
labels: [experiencedLabel],
});
return;
}
if (mergedCount >= trustedThreshold) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.pull_request.number,
labels: [trustedLabel],
});
}
backfill-pr-labels:
if: github.event_name == 'workflow_dispatch'
permissions:
contents: read
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Backfill PR labels
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const owner = context.repo.owner;
const repo = context.repo.repo;
const repoFull = `${owner}/${repo}`;
const inputs = context.payload.inputs ?? {};
const maxPrsInput = inputs.max_prs ?? "200";
const perPageInput = inputs.per_page ?? "50";
const parsedMaxPrs = Number.parseInt(maxPrsInput, 10);
const parsedPerPage = Number.parseInt(perPageInput, 10);
const maxPrs = Number.isFinite(parsedMaxPrs) ? parsedMaxPrs : 200;
const perPage = Number.isFinite(parsedPerPage) ? Math.min(100, Math.max(1, parsedPerPage)) : 50;
const processAll = maxPrs <= 0;
const maxCount = processAll ? Number.POSITIVE_INFINITY : Math.max(1, maxPrs);
const sizeLabels = ["size: XS", "size: S", "size: M", "size: L", "size: XL"];
const labelColor = "b76e79";
const trustedLabel = "trusted-contributor";
const experiencedLabel = "experienced-contributor";
const trustedThreshold = 4;
const experiencedThreshold = 10;
const contributorCache = new Map();
async function ensureSizeLabels() {
for (const label of sizeLabels) {
try {
await github.rest.issues.getLabel({
owner,
repo,
name: label,
});
} catch (error) {
if (error?.status !== 404) {
throw error;
}
await github.rest.issues.createLabel({
owner,
repo,
name: label,
color: labelColor,
});
}
}
}
async function resolveContributorLabel(login) {
if (contributorCache.has(login)) {
return contributorCache.get(login);
}
let isMaintainer = false;
try {
const membership = await github.rest.teams.getMembershipForUserInOrg({
org: owner,
team_slug: "maintainer",
username: login,
});
isMaintainer = membership?.data?.state === "active";
} catch (error) {
if (error?.status !== 404) {
throw error;
}
}
if (isMaintainer) {
contributorCache.set(login, "maintainer");
return "maintainer";
}
const mergedQuery = `repo:${repoFull} is:pr is:merged author:${login}`;
let mergedCount = 0;
try {
const merged = await github.rest.search.issuesAndPullRequests({
q: mergedQuery,
per_page: 1,
});
mergedCount = merged?.data?.total_count ?? 0;
} catch (error) {
if (error?.status !== 422) {
throw error;
}
core.warning(`Skipping merged search for ${login}; treating as 0.`);
}
let label = null;
if (mergedCount >= experiencedThreshold) {
label = experiencedLabel;
} else if (mergedCount >= trustedThreshold) {
label = trustedLabel;
}
contributorCache.set(login, label);
return label;
}
async function applySizeLabel(pullRequest, currentLabels, labelNames) {
const files = await github.paginate(github.rest.pulls.listFiles, {
owner,
repo,
pull_number: pullRequest.number,
per_page: 100,
});
const excludedLockfiles = new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]);
const totalChangedLines = files.reduce((total, file) => {
const path = file.filename ?? "";
if (path === "docs.acp.md" || path.startsWith("docs/") || excludedLockfiles.has(path)) {
return total;
}
return total + (file.additions ?? 0) + (file.deletions ?? 0);
}, 0);
let targetSizeLabel = "size: XL";
if (totalChangedLines < 50) {
targetSizeLabel = "size: XS";
} else if (totalChangedLines < 200) {
targetSizeLabel = "size: S";
} else if (totalChangedLines < 500) {
targetSizeLabel = "size: M";
} else if (totalChangedLines < 1000) {
targetSizeLabel = "size: L";
}
for (const label of currentLabels) {
const name = label.name ?? "";
if (!sizeLabels.includes(name)) {
continue;
}
if (name === targetSizeLabel) {
continue;
}
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: pullRequest.number,
name,
});
labelNames.delete(name);
}
if (!labelNames.has(targetSizeLabel)) {
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pullRequest.number,
labels: [targetSizeLabel],
});
labelNames.add(targetSizeLabel);
}
}
async function applyContributorLabel(pullRequest, labelNames) {
const login = pullRequest.user?.login;
if (!login) {
return;
}
const label = await resolveContributorLabel(login);
if (!label) {
return;
}
if (labelNames.has(label)) {
return;
}
await github.rest.issues.addLabels({
owner,
repo,
issue_number: pullRequest.number,
labels: [label],
});
labelNames.add(label);
}
await ensureSizeLabels();
let page = 1;
let processed = 0;
while (processed < maxCount) {
const remaining = maxCount - processed;
const pageSize = processAll ? perPage : Math.min(perPage, remaining);
const { data: pullRequests } = await github.rest.pulls.list({
owner,
repo,
state: "open",
per_page: pageSize,
page,
});
if (pullRequests.length === 0) {
break;
}
for (const pullRequest of pullRequests) {
if (!processAll && processed >= maxCount) {
break;
}
const currentLabels = await github.paginate(github.rest.issues.listLabelsOnIssue, {
owner,
repo,
issue_number: pullRequest.number,
per_page: 100,
});
const labelNames = new Set(
currentLabels.map((label) => label.name).filter((name) => typeof name === "string"),
);
await applySizeLabel(pullRequest, currentLabels, labelNames);
await applyContributorLabel(pullRequest, labelNames);
processed += 1;
}
if (pullRequests.length < pageSize) {
break;
}
page += 1;
}
core.info(`Processed ${processed} pull requests.`);
label-issues:
permissions:
issues: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Apply maintainer or trusted-contributor label
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
with:
github-token: ${{ steps.app-token.outputs.token }}
script: |
const login = context.payload.issue?.user?.login;
if (!login) {
return;
}
const repo = `${context.repo.owner}/${context.repo.repo}`;
const trustedLabel = "trusted-contributor";
const experiencedLabel = "experienced-contributor";
const trustedThreshold = 4;
const experiencedThreshold = 10;
let isMaintainer = false;
try {
const membership = await github.rest.teams.getMembershipForUserInOrg({
org: context.repo.owner,
team_slug: "maintainer",
username: login,
});
isMaintainer = membership?.data?.state === "active";
} catch (error) {
if (error?.status !== 404) {
throw error;
}
}
if (isMaintainer) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.issue.number,
labels: ["maintainer"],
});
return;
}
const mergedQuery = `repo:${repo} is:pr is:merged author:${login}`;
let mergedCount = 0;
try {
const merged = await github.rest.search.issuesAndPullRequests({
q: mergedQuery,
per_page: 1,
});
mergedCount = merged?.data?.total_count ?? 0;
} catch (error) {
if (error?.status !== 422) {
throw error;
}
core.warning(`Skipping merged search for ${login}; treating as 0.`);
}
if (mergedCount >= experiencedThreshold) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.issue.number,
labels: [experiencedLabel],
});
return;
}
if (mergedCount >= trustedThreshold) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: context.payload.issue.number,
labels: [trustedLabel],
});
}

51
.github/workflows/stale.yml vendored Normal file
View File

@@ -0,0 +1,51 @@
name: Stale
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
permissions: {}
jobs:
stale:
permissions:
issues: write
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1
id: app-token
with:
app-id: "2729701"
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
- name: Mark stale issues and pull requests
uses: actions/stale@v9
with:
repo-token: ${{ steps.app-token.outputs.token }}
days-before-issue-stale: 7
days-before-issue-close: 5
days-before-pr-stale: 5
days-before-pr-close: 3
stale-issue-label: stale
stale-pr-label: stale
exempt-issue-labels: enhancement,maintainer,pinned,security,no-stale
exempt-pr-labels: maintainer,no-stale
operations-per-run: 500
exempt-all-assignees: true
remove-stale-when-updated: true
stale-issue-message: |
This issue has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.
stale-pr-message: |
This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.
close-issue-message: |
Closing due to inactivity.
If this is still an issue, please retry on the latest OpenClaw release and share updated details.
If you are absolutely sure it still happens on the latest release, open a new issue with fresh repro steps.
close-issue-reason: not_planned
close-pr-message: |
Closing due to inactivity.
If you believe this PR should be revived, post in #pr-thunderdome-dangerzone on Discord to talk to a maintainer.
That channel is the escape hatch for high-quality PRs that get auto-closed.

View File

@@ -3,6 +3,11 @@ name: Workflow Sanity
on:
pull_request:
push:
branches: [main]
concurrency:
group: workflow-sanity-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: true
jobs:
no-tabs:

20
.gitignore vendored
View File

@@ -3,11 +3,13 @@ node_modules
.env
docker-compose.extra.yml
dist
*.bun-build
pnpm-lock.yaml
bun.lock
bun.lockb
coverage
__pycache__/
*.pyc
.tsbuildinfo
.pnpm-store
.worktrees/
.DS_Store
@@ -16,6 +18,11 @@ ui/src/ui/__screenshots__/
ui/playwright-report/
ui/test-results/
# Android build artifacts
apps/android/.gradle/
apps/android/app/build/
apps/android/.cxx/
# Bun build artifacts
*.bun-build
apps/macos/.build/
@@ -52,7 +59,6 @@ apps/ios/fastlane/screenshots/
apps/ios/fastlane/test_output/
apps/ios/fastlane/logs/
apps/ios/fastlane/.env
apps/ios/fastlane/report.xml
# fastlane build artifacts (local)
apps/ios/*.ipa
@@ -60,14 +66,20 @@ apps/ios/*.dSYM.zip
# provisioning profiles (local)
apps/ios/*.mobileprovision
.env
# Local untracked files
.local/
.vscode/
docs/.local/
IDENTITY.md
USER.md
.tgz
.idea
# local tooling
.serena/
# Agent credentials and memory (NEVER COMMIT)
/memory/
.agent/*.json
!.agent/workflows/
local/

52
.markdownlint-cli2.jsonc Normal file
View File

@@ -0,0 +1,52 @@
{
"globs": ["docs/**/*.md", "docs/**/*.mdx", "README.md"],
"ignores": ["docs/zh-CN/**", "docs/.i18n/**", "docs/reference/templates/**", "**/.local/**"],
"config": {
"default": true,
"MD013": false,
"MD025": false,
"MD029": false,
"MD033": {
"allowed_elements": [
"Note",
"Info",
"Tip",
"Warning",
"Card",
"CardGroup",
"Columns",
"Steps",
"Step",
"Tabs",
"Tab",
"Accordion",
"AccordionGroup",
"CodeGroup",
"Frame",
"Callout",
"ParamField",
"ResponseField",
"RequestExample",
"ResponseExample",
"img",
"a",
"br",
"details",
"summary",
"p",
"strong",
"picture",
"source",
"Tooltip",
"Check",
],
},
"MD036": false,
"MD040": false,
"MD041": false,
"MD046": false,
},
}

View File

@@ -1,5 +1,20 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
"indentWidth": 2,
"printWidth": 100
"experimentalSortImports": {
"newlinesBetween": false,
},
"experimentalSortPackageJson": {
"sortScripts": true,
},
"ignorePatterns": [
"apps/",
"assets/",
"dist/",
"docs/_layouts/",
"node_modules/",
"patches/",
"pnpm-lock.yaml/",
"Swabble/",
"vendor/",
],
}

View File

@@ -1,12 +1,36 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
"plugins": [
"unicorn",
"typescript",
"oxc"
],
"plugins": ["unicorn", "typescript", "oxc"],
"categories": {
"correctness": "error"
"correctness": "error",
"perf": "error",
"suspicious": "error"
},
"ignorePatterns": ["src/canvas-host/a2ui/a2ui.bundle.js"]
"rules": {
"curly": "error",
"eslint-plugin-unicorn/prefer-array-find": "off",
"eslint/no-await-in-loop": "off",
"eslint/no-new": "off",
"oxc/no-accumulating-spread": "off",
"oxc/no-async-endpoint-handlers": "off",
"oxc/no-map-spread": "off",
"typescript/no-explicit-any": "error",
"typescript/no-extraneous-class": "off",
"typescript/no-unsafe-type-assertion": "off",
"unicorn/consistent-function-scoping": "off",
"unicorn/require-post-message-target-origin": "off"
},
"ignorePatterns": [
"assets/",
"dist/",
"docs/_layouts/",
"extensions/",
"node_modules/",
"patches/",
"pnpm-lock.yaml/",
"skills/",
"src/canvas-host/a2ui/a2ui.bundle.js",
"Swabble/",
"vendor/"
]
}

195
.pi/extensions/diff.ts Normal file
View File

@@ -0,0 +1,195 @@
/**
* Diff Extension
*
* /diff command shows modified/deleted/new files from git status and opens
* the selected file in VS Code's diff view.
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { DynamicBorder } from "@mariozechner/pi-coding-agent";
import {
Container,
Key,
matchesKey,
type SelectItem,
SelectList,
Text,
} from "@mariozechner/pi-tui";
interface FileInfo {
status: string;
statusLabel: string;
file: string;
}
export default function (pi: ExtensionAPI) {
pi.registerCommand("diff", {
description: "Show git changes and open in VS Code diff view",
handler: async (_args, ctx) => {
if (!ctx.hasUI) {
ctx.ui.notify("No UI available", "error");
return;
}
// Get changed files from git status
const result = await pi.exec("git", ["status", "--porcelain"], { cwd: ctx.cwd });
if (result.code !== 0) {
ctx.ui.notify(`git status failed: ${result.stderr}`, "error");
return;
}
if (!result.stdout || !result.stdout.trim()) {
ctx.ui.notify("No changes in working tree", "info");
return;
}
// Parse git status output
// Format: XY filename (where XY is two-letter status, then space, then filename)
const lines = result.stdout.split("\n");
const files: FileInfo[] = [];
for (const line of lines) {
if (line.length < 4) {
continue;
} // Need at least "XY f"
const status = line.slice(0, 2);
const file = line.slice(2).trimStart();
// Translate status codes to short labels
let statusLabel: string;
if (status.includes("M")) {
statusLabel = "M";
} else if (status.includes("A")) {
statusLabel = "A";
} else if (status.includes("D")) {
statusLabel = "D";
} else if (status.includes("?")) {
statusLabel = "?";
} else if (status.includes("R")) {
statusLabel = "R";
} else if (status.includes("C")) {
statusLabel = "C";
} else {
statusLabel = status.trim() || "~";
}
files.push({ status: statusLabel, statusLabel, file });
}
if (files.length === 0) {
ctx.ui.notify("No changes found", "info");
return;
}
const openSelected = async (fileInfo: FileInfo): Promise<void> => {
try {
// Open in VS Code diff view.
// For untracked files, git difftool won't work, so fall back to just opening the file.
if (fileInfo.status === "?") {
await pi.exec("code", ["-g", fileInfo.file], { cwd: ctx.cwd });
return;
}
const diffResult = await pi.exec(
"git",
["difftool", "-y", "--tool=vscode", fileInfo.file],
{
cwd: ctx.cwd,
},
);
if (diffResult.code !== 0) {
await pi.exec("code", ["-g", fileInfo.file], { cwd: ctx.cwd });
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
ctx.ui.notify(`Failed to open ${fileInfo.file}: ${message}`, "error");
}
};
// Show file picker with SelectList
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
const container = new Container();
// Top border
container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
// Title
container.addChild(new Text(theme.fg("accent", theme.bold(" Select file to diff")), 0, 0));
// Build select items with colored status
const items: SelectItem[] = files.map((f) => {
let statusColor: string;
switch (f.status) {
case "M":
statusColor = theme.fg("warning", f.status);
break;
case "A":
statusColor = theme.fg("success", f.status);
break;
case "D":
statusColor = theme.fg("error", f.status);
break;
case "?":
statusColor = theme.fg("muted", f.status);
break;
default:
statusColor = theme.fg("dim", f.status);
}
return {
value: f,
label: `${statusColor} ${f.file}`,
};
});
const visibleRows = Math.min(files.length, 15);
let currentIndex = 0;
const selectList = new SelectList(items, visibleRows, {
selectedPrefix: (t) => theme.fg("accent", t),
selectedText: (t) => t, // Keep existing colors
description: (t) => theme.fg("muted", t),
scrollInfo: (t) => theme.fg("dim", t),
noMatch: (t) => theme.fg("warning", t),
});
selectList.onSelect = (item) => {
void openSelected(item.value as FileInfo);
};
selectList.onCancel = () => done();
selectList.onSelectionChange = (item) => {
currentIndex = items.indexOf(item);
};
container.addChild(selectList);
// Help text
container.addChild(
new Text(theme.fg("dim", " ↑↓ navigate • ←→ page • enter open • esc close"), 0, 0),
);
// Bottom border
container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
return {
render: (w) => container.render(w),
invalidate: () => container.invalidate(),
handleInput: (data) => {
// Add paging with left/right
if (matchesKey(data, Key.left)) {
// Page up - clamp to 0
currentIndex = Math.max(0, currentIndex - visibleRows);
selectList.setSelectedIndex(currentIndex);
} else if (matchesKey(data, Key.right)) {
// Page down - clamp to last
currentIndex = Math.min(items.length - 1, currentIndex + visibleRows);
selectList.setSelectedIndex(currentIndex);
} else {
selectList.handleInput(data);
}
tui.requestRender();
},
};
});
},
});
}

194
.pi/extensions/files.ts Normal file
View File

@@ -0,0 +1,194 @@
/**
* Files Extension
*
* /files command lists all files the model has read/written/edited in the active session branch,
* coalesced by path and sorted newest first. Selecting a file opens it in VS Code.
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { DynamicBorder } from "@mariozechner/pi-coding-agent";
import {
Container,
Key,
matchesKey,
type SelectItem,
SelectList,
Text,
} from "@mariozechner/pi-tui";
interface FileEntry {
path: string;
operations: Set<"read" | "write" | "edit">;
lastTimestamp: number;
}
type FileToolName = "read" | "write" | "edit";
export default function (pi: ExtensionAPI) {
pi.registerCommand("files", {
description: "Show files read/written/edited in this session",
handler: async (_args, ctx) => {
if (!ctx.hasUI) {
ctx.ui.notify("No UI available", "error");
return;
}
// Get the current branch (path from leaf to root)
const branch = ctx.sessionManager.getBranch();
// First pass: collect tool calls (id -> {path, name}) from assistant messages
const toolCalls = new Map<string, { path: string; name: FileToolName; timestamp: number }>();
for (const entry of branch) {
if (entry.type !== "message") {
continue;
}
const msg = entry.message;
if (msg.role === "assistant" && Array.isArray(msg.content)) {
for (const block of msg.content) {
if (block.type === "toolCall") {
const name = block.name;
if (name === "read" || name === "write" || name === "edit") {
const path = block.arguments?.path;
if (path && typeof path === "string") {
toolCalls.set(block.id, { path, name, timestamp: msg.timestamp });
}
}
}
}
}
}
// Second pass: match tool results to get the actual execution timestamp
const fileMap = new Map<string, FileEntry>();
for (const entry of branch) {
if (entry.type !== "message") {
continue;
}
const msg = entry.message;
if (msg.role === "toolResult") {
const toolCall = toolCalls.get(msg.toolCallId);
if (!toolCall) {
continue;
}
const { path, name } = toolCall;
const timestamp = msg.timestamp;
const existing = fileMap.get(path);
if (existing) {
existing.operations.add(name);
if (timestamp > existing.lastTimestamp) {
existing.lastTimestamp = timestamp;
}
} else {
fileMap.set(path, {
path,
operations: new Set([name]),
lastTimestamp: timestamp,
});
}
}
}
if (fileMap.size === 0) {
ctx.ui.notify("No files read/written/edited in this session", "info");
return;
}
// Sort by most recent first
const files = Array.from(fileMap.values()).toSorted(
(a, b) => b.lastTimestamp - a.lastTimestamp,
);
const openSelected = async (file: FileEntry): Promise<void> => {
try {
await pi.exec("code", ["-g", file.path], { cwd: ctx.cwd });
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
ctx.ui.notify(`Failed to open ${file.path}: ${message}`, "error");
}
};
// Show file picker with SelectList
await ctx.ui.custom<void>((tui, theme, _kb, done) => {
const container = new Container();
// Top border
container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
// Title
container.addChild(new Text(theme.fg("accent", theme.bold(" Select file to open")), 0, 0));
// Build select items with colored operations
const items: SelectItem[] = files.map((f) => {
const ops: string[] = [];
if (f.operations.has("read")) {
ops.push(theme.fg("muted", "R"));
}
if (f.operations.has("write")) {
ops.push(theme.fg("success", "W"));
}
if (f.operations.has("edit")) {
ops.push(theme.fg("warning", "E"));
}
const opsLabel = ops.join("");
return {
value: f,
label: `${opsLabel} ${f.path}`,
};
});
const visibleRows = Math.min(files.length, 15);
let currentIndex = 0;
const selectList = new SelectList(items, visibleRows, {
selectedPrefix: (t) => theme.fg("accent", t),
selectedText: (t) => t, // Keep existing colors
description: (t) => theme.fg("muted", t),
scrollInfo: (t) => theme.fg("dim", t),
noMatch: (t) => theme.fg("warning", t),
});
selectList.onSelect = (item) => {
void openSelected(item.value as FileEntry);
};
selectList.onCancel = () => done();
selectList.onSelectionChange = (item) => {
currentIndex = items.indexOf(item);
};
container.addChild(selectList);
// Help text
container.addChild(
new Text(theme.fg("dim", " ↑↓ navigate • ←→ page • enter open • esc close"), 0, 0),
);
// Bottom border
container.addChild(new DynamicBorder((s: string) => theme.fg("accent", s)));
return {
render: (w) => container.render(w),
invalidate: () => container.invalidate(),
handleInput: (data) => {
// Add paging with left/right
if (matchesKey(data, Key.left)) {
// Page up - clamp to 0
currentIndex = Math.max(0, currentIndex - visibleRows);
selectList.setSelectedIndex(currentIndex);
} else if (matchesKey(data, Key.right)) {
// Page down - clamp to last
currentIndex = Math.min(items.length - 1, currentIndex + visibleRows);
selectList.setSelectedIndex(currentIndex);
} else {
selectList.handleInput(data);
}
tui.requestRender();
},
};
});
},
});
}

View File

@@ -0,0 +1,193 @@
import {
DynamicBorder,
type ExtensionAPI,
type ExtensionContext,
} from "@mariozechner/pi-coding-agent";
import { Container, Text } from "@mariozechner/pi-tui";
const PR_PROMPT_PATTERN = /^\s*You are given one or more GitHub PR URLs:\s*(\S+)/im;
const ISSUE_PROMPT_PATTERN = /^\s*Analyze GitHub issue\(s\):\s*(\S+)/im;
type PromptMatch = {
kind: "pr" | "issue";
url: string;
};
type GhMetadata = {
title?: string;
author?: {
login?: string;
name?: string | null;
};
};
function extractPromptMatch(prompt: string): PromptMatch | undefined {
const prMatch = prompt.match(PR_PROMPT_PATTERN);
if (prMatch?.[1]) {
return { kind: "pr", url: prMatch[1].trim() };
}
const issueMatch = prompt.match(ISSUE_PROMPT_PATTERN);
if (issueMatch?.[1]) {
return { kind: "issue", url: issueMatch[1].trim() };
}
return undefined;
}
async function fetchGhMetadata(
pi: ExtensionAPI,
kind: PromptMatch["kind"],
url: string,
): Promise<GhMetadata | undefined> {
const args =
kind === "pr"
? ["pr", "view", url, "--json", "title,author"]
: ["issue", "view", url, "--json", "title,author"];
try {
const result = await pi.exec("gh", args);
if (result.code !== 0 || !result.stdout) {
return undefined;
}
return JSON.parse(result.stdout) as GhMetadata;
} catch {
return undefined;
}
}
function formatAuthor(author?: GhMetadata["author"]): string | undefined {
if (!author) {
return undefined;
}
const name = author.name?.trim();
const login = author.login?.trim();
if (name && login) {
return `${name} (@${login})`;
}
if (login) {
return `@${login}`;
}
if (name) {
return name;
}
return undefined;
}
export default function promptUrlWidgetExtension(pi: ExtensionAPI) {
const setWidget = (
ctx: ExtensionContext,
match: PromptMatch,
title?: string,
authorText?: string,
) => {
ctx.ui.setWidget("prompt-url", (_tui, thm) => {
const titleText = title ? thm.fg("accent", title) : thm.fg("accent", match.url);
const authorLine = authorText ? thm.fg("muted", authorText) : undefined;
const urlLine = thm.fg("dim", match.url);
const lines = [titleText];
if (authorLine) {
lines.push(authorLine);
}
lines.push(urlLine);
const container = new Container();
container.addChild(new DynamicBorder((s: string) => thm.fg("muted", s)));
container.addChild(new Text(lines.join("\n"), 1, 0));
return container;
});
};
const applySessionName = (ctx: ExtensionContext, match: PromptMatch, title?: string) => {
const label = match.kind === "pr" ? "PR" : "Issue";
const trimmedTitle = title?.trim();
const fallbackName = `${label}: ${match.url}`;
const desiredName = trimmedTitle ? `${label}: ${trimmedTitle} (${match.url})` : fallbackName;
const currentName = pi.getSessionName()?.trim();
if (!currentName) {
pi.setSessionName(desiredName);
return;
}
if (currentName === match.url || currentName === fallbackName) {
pi.setSessionName(desiredName);
}
};
pi.on("before_agent_start", async (event, ctx) => {
if (!ctx.hasUI) {
return;
}
const match = extractPromptMatch(event.prompt);
if (!match) {
return;
}
setWidget(ctx, match);
applySessionName(ctx, match);
void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
const title = meta?.title?.trim();
const authorText = formatAuthor(meta?.author);
setWidget(ctx, match, title, authorText);
applySessionName(ctx, match, title);
});
});
pi.on("session_switch", async (_event, ctx) => {
rebuildFromSession(ctx);
});
const getUserText = (content: string | { type: string; text?: string }[] | undefined): string => {
if (!content) {
return "";
}
if (typeof content === "string") {
return content;
}
return (
content
.filter((block): block is { type: "text"; text: string } => block.type === "text")
.map((block) => block.text)
.join("\n") ?? ""
);
};
const rebuildFromSession = (ctx: ExtensionContext) => {
if (!ctx.hasUI) {
return;
}
const entries = ctx.sessionManager.getEntries();
const lastMatch = [...entries].toReversed().find((entry) => {
if (entry.type !== "message" || entry.message.role !== "user") {
return false;
}
const text = getUserText(entry.message.content);
return !!extractPromptMatch(text);
});
const content =
lastMatch?.type === "message" && lastMatch.message.role === "user"
? lastMatch.message.content
: undefined;
const text = getUserText(content);
const match = text ? extractPromptMatch(text) : undefined;
if (!match) {
ctx.ui.setWidget("prompt-url", undefined);
return;
}
setWidget(ctx, match);
applySessionName(ctx, match);
void fetchGhMetadata(pi, match.kind, match.url).then((meta) => {
const title = meta?.title?.trim();
const authorText = formatAuthor(meta?.author);
setWidget(ctx, match, title, authorText);
applySessionName(ctx, match, title);
});
};
pi.on("session_start", async (_event, ctx) => {
rebuildFromSession(ctx);
});
}

26
.pi/extensions/redraws.ts Normal file
View File

@@ -0,0 +1,26 @@
/**
* Redraws Extension
*
* Exposes /tui to show TUI redraw stats.
*/
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { Text } from "@mariozechner/pi-tui";
export default function (pi: ExtensionAPI) {
pi.registerCommand("tui", {
description: "Show TUI stats",
handler: async (_args, ctx) => {
if (!ctx.hasUI) {
return;
}
let redraws = 0;
await ctx.ui.custom<void>((tui, _theme, _keybindings, done) => {
redraws = tui.fullRedraws;
done(undefined);
return new Text("", 0, 0);
});
ctx.ui.notify(`TUI full redraws: ${redraws}`, "info");
},
});
}

2
.pi/git/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

58
.pi/prompts/cl.md Normal file
View File

@@ -0,0 +1,58 @@
---
description: Audit changelog entries before release
---
Audit changelog entries for all commits since the last release.
## Process
1. **Find the last release tag:**
```bash
git tag --sort=-version:refname | head -1
```
2. **List all commits since that tag:**
```bash
git log <tag>..HEAD --oneline
```
3. **Read each package's [Unreleased] section:**
- packages/ai/CHANGELOG.md
- packages/tui/CHANGELOG.md
- packages/coding-agent/CHANGELOG.md
4. **For each commit, check:**
- Skip: changelog updates, doc-only changes, release housekeeping
- Determine which package(s) the commit affects (use `git show <hash> --stat`)
- Verify a changelog entry exists in the affected package(s)
- For external contributions (PRs), verify format: `Description ([#N](url) by [@user](url))`
5. **Cross-package duplication rule:**
Changes in `ai`, `agent` or `tui` that affect end users should be duplicated to `coding-agent` changelog, since coding-agent is the user-facing package that depends on them.
6. **Add New Features section after changelog fixes:**
- Insert a `### New Features` section at the start of `## [Unreleased]` in `packages/coding-agent/CHANGELOG.md`.
- Propose the top new features to the user for confirmation before writing them.
- Link to relevant docs and sections whenever possible.
7. **Report:**
- List commits with missing entries
- List entries that need cross-package duplication
- Add any missing entries directly
## Changelog Format Reference
Sections (in order):
- `### Breaking Changes` - API changes requiring migration
- `### Added` - New features
- `### Changed` - Changes to existing functionality
- `### Fixed` - Bug fixes
- `### Removed` - Removed features
Attribution:
- Internal: `Fixed foo ([#123](https://github.com/badlogic/pi-mono/issues/123))`
- External: `Added bar ([#456](https://github.com/badlogic/pi-mono/pull/456) by [@user](https://github.com/user))`

22
.pi/prompts/is.md Normal file
View File

@@ -0,0 +1,22 @@
---
description: Analyze GitHub issues (bugs or feature requests)
---
Analyze GitHub issue(s): $ARGUMENTS
For each issue:
1. Read the issue in full, including all comments and linked issues/PRs.
2. **For bugs**:
- Ignore any root cause analysis in the issue (likely wrong)
- Read all related code files in full (no truncation)
- Trace the code path and identify the actual root cause
- Propose a fix
3. **For feature requests**:
- Read all related code files in full (no truncation)
- Propose the most concise implementation approach
- List affected files and changes needed
Do NOT implement unless explicitly asked. Analyze and propose only.

72
.pi/prompts/landpr.md Normal file
View File

@@ -0,0 +1,72 @@
---
description: Land a PR (merge with proper workflow)
---
Input
- PR: $1 <number|url>
- If missing: use the most recent PR mentioned in the conversation.
- If ambiguous: ask.
Do (end-to-end)
Goal: PR must end in GitHub state = MERGED (never CLOSED). Use `gh pr merge` with `--rebase` or `--squash`.
1. Assign PR to self:
- `gh pr edit <PR> --add-assignee @me`
2. Repo clean: `git status`.
3. Identify PR meta (author + head branch):
```sh
gh pr view <PR> --json number,title,author,headRefName,baseRefName,headRepository --jq '{number,title,author:.author.login,head:.headRefName,base:.baseRefName,headRepo:.headRepository.nameWithOwner}'
contrib=$(gh pr view <PR> --json author --jq .author.login)
head=$(gh pr view <PR> --json headRefName --jq .headRefName)
head_repo_url=$(gh pr view <PR> --json headRepository --jq .headRepository.url)
```
4. Fast-forward base:
- `git checkout main`
- `git pull --ff-only`
5. Create temp base branch from main:
- `git checkout -b temp/landpr-<ts-or-pr>`
6. Check out PR branch locally:
- `gh pr checkout <PR>`
7. Rebase PR branch onto temp base:
- `git rebase temp/landpr-<ts-or-pr>`
- Fix conflicts; keep history tidy.
8. Fix + tests + changelog:
- Implement fixes + add/adjust tests
- Update `CHANGELOG.md` and mention `#<PR>` + `@$contrib`
9. Decide merge strategy:
- Rebase if we want to preserve commit history
- Squash if we want a single clean commit
- If unclear, ask
10. Full gate (BEFORE commit):
- `pnpm lint && pnpm build && pnpm test`
11. Commit via committer (include # + contributor in commit message):
- `committer "fix: <summary> (#<PR>) (thanks @$contrib)" CHANGELOG.md <changed files>`
- `land_sha=$(git rev-parse HEAD)`
12. Push updated PR branch (rebase => usually needs force):
```sh
git remote add prhead "$head_repo_url.git" 2>/dev/null || git remote set-url prhead "$head_repo_url.git"
git push --force-with-lease prhead HEAD:$head
```
13. Merge PR (must show MERGED on GitHub):
- Rebase: `gh pr merge <PR> --rebase`
- Squash: `gh pr merge <PR> --squash`
- Never `gh pr close` (closing is wrong)
14. Sync main:
- `git checkout main`
- `git pull --ff-only`
15. Comment on PR with what we did + SHAs + thanks:
```sh
merge_sha=$(gh pr view <PR> --json mergeCommit --jq '.mergeCommit.oid')
gh pr comment <PR> --body "Landed via temp rebase onto main.\n\n- Gate: pnpm lint && pnpm build && pnpm test\n- Land commit: $land_sha\n- Merge commit: $merge_sha\n\nThanks @$contrib!"
```
16. Verify PR state == MERGED:
- `gh pr view <PR> --json state --jq .state`
17. Delete temp branch:
- `git branch -D temp/landpr-<ts-or-pr>`

105
.pi/prompts/reviewpr.md Normal file
View File

@@ -0,0 +1,105 @@
---
description: Review a PR thoroughly without merging
---
Input
- PR: $1 <number|url>
- If missing: use the most recent PR mentioned in the conversation.
- If ambiguous: ask.
Do (review-only)
Goal: produce a thorough review and a clear recommendation (READY for /landpr vs NEEDS WORK). Do NOT merge, do NOT push, do NOT make changes in the repo as part of this command.
1. Identify PR meta + context
```sh
gh pr view <PR> --json number,title,state,isDraft,author,baseRefName,headRefName,headRepository,url,body,labels,assignees,reviewRequests,files,additions,deletions --jq '{number,title,url,state,isDraft,author:.author.login,base:.baseRefName,head:.headRefName,headRepo:.headRepository.nameWithOwner,additions,deletions,files:.files|length}'
```
2. Read the PR description carefully
- Summarize the stated goal, scope, and any "why now?" rationale.
- Call out any missing context: motivation, alternatives considered, rollout/compat notes, risk.
3. Read the diff thoroughly (prefer full diff)
```sh
gh pr diff <PR>
# If you need more surrounding context for files:
gh pr checkout <PR> # optional; still review-only
git show --stat
```
4. Validate the change is needed / valuable
- What user/customer/dev pain does this solve?
- Is this change the smallest reasonable fix?
- Are we introducing complexity for marginal benefit?
- Are we changing behavior/contract in a way that needs docs or a release note?
5. Evaluate implementation quality + optimality
- Correctness: edge cases, error handling, null/undefined, concurrency, ordering.
- Design: is the abstraction/architecture appropriate or over/under-engineered?
- Performance: hot paths, allocations, queries, network, N+1s, caching.
- Security/privacy: authz/authn, input validation, secrets, logging PII.
- Backwards compatibility: public APIs, config, migrations.
- Style consistency: formatting, naming, patterns used elsewhere.
6. Tests & verification
- Identify what's covered by tests (unit/integration/e2e).
- Are there regression tests for the bug fixed / scenario added?
- Missing tests? Call out exact cases that should be added.
- If tests are present, do they actually assert the important behavior (not just snapshots / happy path)?
7. Follow-up refactors / cleanup suggestions
- Any code that should be simplified before merge?
- Any TODOs that should be tickets vs addressed now?
- Any deprecations, docs, types, or lint rules we should adjust?
8. Key questions to answer explicitly
- Can we fix everything ourselves in a follow-up, or does the contributor need to update this PR?
- Any blocking concerns (must-fix before merge)?
- Is this PR ready to land, or does it need work?
9. Output (structured)
Produce a review with these sections:
A) TL;DR recommendation
- One of: READY FOR /landpr | NEEDS WORK | NEEDS DISCUSSION
- 13 sentence rationale.
B) What changed
- Brief bullet summary of the diff/behavioral changes.
C) What's good
- Bullets: correctness, simplicity, tests, docs, ergonomics, etc.
D) Concerns / questions (actionable)
- Numbered list.
- Mark each item as:
- BLOCKER (must fix before merge)
- IMPORTANT (should fix before merge)
- NIT (optional)
- For each: point to the file/area and propose a concrete fix or alternative.
E) Tests
- What exists.
- What's missing (specific scenarios).
F) Follow-ups (optional)
- Non-blocking refactors/tickets to open later.
G) Suggested PR comment (optional)
- Offer: "Want me to draft a PR comment to the author?"
- If yes, provide a ready-to-paste comment summarizing the above, with clear asks.
Rules / Guardrails
- Review only: do not merge (`gh pr merge`), do not push branches, do not edit code.
- If you need clarification, ask questions rather than guessing.

View File

@@ -1,4 +1,4 @@
# Pre-commit hooks for clawdbot
# Pre-commit hooks for openclaw
# Install: prek install
# Run manually: prek run --all-files
#
@@ -51,9 +51,9 @@ repos:
rev: v0.11.0
hooks:
- id: shellcheck
args: [--severity=error] # Only fail on errors, not warnings/info
args: [--severity=error] # Only fail on errors, not warnings/info
# Exclude vendor and scripts with embedded code or known issues
exclude: '^(vendor/|scripts/e2e/)'
exclude: "^(vendor/|scripts/e2e/)"
# GitHub Actions linting
- repo: https://github.com/rhysd/actionlint
@@ -67,7 +67,7 @@ repos:
hooks:
- id: zizmor
args: [--persona=regular, --min-severity=medium, --min-confidence=medium]
exclude: '^(vendor/|Swabble/)'
exclude: "^(vendor/|Swabble/)"
# Project checks (same commands as CI)
- repo: local

View File

@@ -1 +0,0 @@
src/canvas-host/a2ui/a2ui.bundle.js

3
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"recommendations": ["oxc.oxc-vscode"]
}

22
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,22 @@
{
"editor.formatOnSave": true,
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true,
"[javascript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"[json]": {
"editor.defaultFormatter": "oxc.oxc-vscode"
},
"typescript.preferences.importModuleSpecifierEnding": "js",
"typescript.reportStyleChecksAsWarnings": false,
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.experimental.useTsgo": true
}

110
AGENTS.md
View File

@@ -1,115 +1,129 @@
# Repository Guidelines
- Repo: https://github.com/moltbot/moltbot
- Repo: https://github.com/openclaw/openclaw
- GitHub issues/comments/PR comments: use literal multiline strings or `-F - <<'EOF'` (or $'...') for real newlines; never embed "\\n".
## Project Structure & Module Organization
- Source code: `src/` (CLI wiring in `src/cli`, commands in `src/commands`, web provider in `src/provider-web.ts`, infra in `src/infra`, media pipeline in `src/media`).
- Tests: colocated `*.test.ts`.
- Docs: `docs/` (images, queue, Pi config). Built output lives in `dist/`.
- Plugins/extensions: live under `extensions/*` (workspace packages). Keep plugin-only deps in the extension `package.json`; do not add them to the root `package.json` unless core uses them.
- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `moltbot` in `devDependencies` or `peerDependencies` instead (runtime resolves `clawdbot/plugin-sdk` via jiti alias).
- Installers served from `https://molt.bot/*`: live in the sibling repo `../molt.bot` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
- Plugins: install runs `npm install --omit=dev` in plugin dir; runtime deps must live in `dependencies`. Avoid `workspace:*` in `dependencies` (npm install breaks); put `openclaw` in `devDependencies` or `peerDependencies` instead (runtime resolves `openclaw/plugin-sdk` via jiti alias).
- Installers served from `https://openclaw.ai/*`: live in the sibling repo `../openclaw.ai` (`public/install.sh`, `public/install-cli.sh`, `public/install.ps1`).
- Messaging channels: always consider **all** built-in + extension channels when refactoring shared logic (routing, allowlists, pairing, command gating, onboarding, docs).
- Core channel docs: `docs/channels/`
- Core channel code: `src/telegram`, `src/discord`, `src/slack`, `src/signal`, `src/imessage`, `src/web` (WhatsApp web), `src/channels`, `src/routing`
- Extensions (channel plugins): `extensions/*` (e.g. `extensions/msteams`, `extensions/matrix`, `extensions/zalo`, `extensions/zalouser`, `extensions/voice-call`)
- When adding channels/extensions/apps/docs, review `.github/labeler.yml` for label coverage.
- When adding channels/extensions/apps/docs, update `.github/labeler.yml` and create matching GitHub labels (use existing channel/extension label colors).
## Docs Linking (Mintlify)
- Docs are hosted on Mintlify (docs.molt.bot).
- Docs are hosted on Mintlify (docs.openclaw.ai).
- Internal doc links in `docs/**/*.md`: root-relative, no `.md`/`.mdx` (example: `[Config](/configuration)`).
- When working with documentation, read the mintlify skill.
- Section cross-references: use anchors on root-relative paths (example: `[Hooks](/configuration#hooks)`).
- Doc headings and anchors: avoid em dashes and apostrophes in headings because they break Mintlify anchor links.
- When Peter asks for links, reply with full `https://docs.molt.bot/...` URLs (not root-relative).
- When you touch docs, end the reply with the `https://docs.molt.bot/...` URLs you referenced.
- README (GitHub): keep absolute docs URLs (`https://docs.molt.bot/...`) so links work on GitHub.
- When Peter asks for links, reply with full `https://docs.openclaw.ai/...` URLs (not root-relative).
- When you touch docs, end the reply with the `https://docs.openclaw.ai/...` URLs you referenced.
- README (GitHub): keep absolute docs URLs (`https://docs.openclaw.ai/...`) so links work on GitHub.
- Docs content must be generic: no personal device names/hostnames/paths; use placeholders like `user@gateway-host` and “gateway host”.
## Docs i18n (zh-CN)
- `docs/zh-CN/**` is generated; do not edit unless the user explicitly asks.
- Pipeline: update English docs → adjust glossary (`docs/.i18n/glossary.zh-CN.json`) → run `scripts/docs-i18n` → apply targeted fixes only if instructed.
- Translation memory: `docs/.i18n/zh-CN.tm.jsonl` (generated).
- See `docs/.i18n/README.md`.
- The pipeline can be slow/inefficient; if its dragging, ping @jospalmbier on Discord instead of hacking around it.
## exe.dev VM ops (general)
- Access: stable path is `ssh exe.dev` then `ssh vm-name` (assume SSH key already set).
- SSH flaky: use exe.dev web terminal or Shelley (web agent); keep a tmux session for long ops.
- Update: `sudo npm i -g moltbot@latest` (global install needs root on `/usr/lib/node_modules`).
- Config: use `moltbot config set ...`; ensure `gateway.mode=local` is set.
- Update: `sudo npm i -g openclaw@latest` (global install needs root on `/usr/lib/node_modules`).
- Config: use `openclaw config set ...`; ensure `gateway.mode=local` is set.
- Discord: store raw token only (no `DISCORD_BOT_TOKEN=` prefix).
- Restart: stop old gateway and run:
`pkill -9 -f moltbot-gateway || true; nohup moltbot gateway run --bind loopback --port 18789 --force > /tmp/moltbot-gateway.log 2>&1 &`
- Verify: `moltbot channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/moltbot-gateway.log`.
`pkill -9 -f openclaw-gateway || true; nohup openclaw gateway run --bind loopback --port 18789 --force > /tmp/openclaw-gateway.log 2>&1 &`
- Verify: `openclaw channels status --probe`, `ss -ltnp | rg 18789`, `tail -n 120 /tmp/openclaw-gateway.log`.
## Build, Test, and Development Commands
- Runtime baseline: Node **22+** (keep Node + Bun paths working).
- Install deps: `pnpm install`
- Pre-commit hooks: `prek install` (runs same checks as CI)
- Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches).
- Prefer Bun for TypeScript execution (scripts, dev, tests): `bun <file.ts>` / `bunx <tool>`.
- Run CLI in dev: `pnpm moltbot ...` (bun) or `pnpm dev`.
- Run CLI in dev: `pnpm openclaw ...` (bun) or `pnpm dev`.
- Node remains supported for running built output (`dist/*`) and production installs.
- Mac packaging (dev): `scripts/package-mac-app.sh` defaults to current arch. Release checklist: `docs/platforms/mac/release.md`.
- Type-check/build: `pnpm build` (tsc)
- Lint/format: `pnpm lint` (oxlint), `pnpm format` (oxfmt)
- Type-check/build: `pnpm build`
- TypeScript checks: `pnpm tsgo`
- Lint/format: `pnpm check`
- Format check: `pnpm format` (oxfmt --check)
- Format fix: `pnpm format:fix` (oxfmt --write)
- Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage`
## Coding Style & Naming Conventions
- Language: TypeScript (ESM). Prefer strict typing; avoid `any`.
- Formatting/linting via Oxlint and Oxfmt; run `pnpm lint` before commits.
- Formatting/linting via Oxlint and Oxfmt; run `pnpm check` before commits.
- Add brief code comments for tricky or non-obvious logic.
- Keep files concise; extract helpers instead of “V2” copies. Use existing patterns for CLI options and dependency injection via `createDefaultDeps`.
- Aim to keep files under ~700 LOC; guideline only (not a hard guardrail). Split/refactor when it improves clarity or testability.
- Naming: use **Moltbot** for product/app/docs headings; use `moltbot` for CLI command, package/binary, paths, and config keys.
- Naming: use **OpenClaw** for product/app/docs headings; use `openclaw` for CLI command, package/binary, paths, and config keys.
## Release Channels (Naming)
- stable: tagged releases only (e.g. `vYYYY.M.D`), npm dist-tag `latest`.
- beta: prerelease tags `vYYYY.M.D-beta.N`, npm dist-tag `beta` (may ship without macOS app).
- dev: moving head on `main` (no tag; git checkout main).
## Testing Guidelines
- Framework: Vitest with V8 coverage thresholds (70% lines/branches/functions/statements).
- Naming: match source names with `*.test.ts`; e2e in `*.e2e.test.ts`.
- Run `pnpm test` (or `pnpm test:coverage`) before pushing when you touch logic.
- Do not set test workers above 16; tried already.
- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (Moltbot-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Live tests (real keys): `CLAWDBOT_LIVE_TEST=1 pnpm test:live` (OpenClaw-only) or `LIVE=1 pnpm test:live` (includes provider live tests). Docker: `pnpm test:docker:live-models`, `pnpm test:docker:live-gateway`. Onboarding Docker E2E: `pnpm test:docker:onboard`.
- Full kit + whats covered: `docs/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Pure test additions/fixes generally do **not** need a changelog entry unless they alter user-facing behavior or the user asks for one.
- Mobile: before using a simulator, check for connected real devices (iOS + Android) and prefer them when available.
## Commit & Pull Request Guidelines
**Full maintainer PR workflow (optional):** If you want the repo's end-to-end maintainer workflow (triage order, quality bar, rebase rules, commit/changelog conventions, co-contributor policy, and the `review-pr` > `prepare-pr` > `merge-pr` pipeline), see `.agents/skills/PR_WORKFLOW.md`. Maintainers may use other workflows; when a maintainer specifies a workflow, follow that. If no workflow is specified, default to PR_WORKFLOW.
- Create commits with `scripts/committer "<msg>" <file...>`; avoid manual `git add`/`git commit` so staging stays scoped.
- Follow concise, action-oriented commit messages (e.g., `CLI: add verbose flag to send`).
- Group related changes; avoid bundling unrelated refactors.
- Changelog workflow: keep latest released version at top (no `Unreleased`); after publishing, bump version and start a new top section.
- PRs should summarize scope, note testing performed, and mention any user-facing changes or new flags.
- PR review flow: when given a PR link, review via `gh pr view`/`gh pr diff` and do **not** change branches.
- PR review calls: prefer a single `gh pr view --json ...` to batch metadata/comments; run `gh pr diff` only when needed.
- Before starting a review when a GH Issue/PR is pasted: run `git pull`; if there are local changes or unpushed commits, stop and alert the user before reviewing.
- Goal: merge PRs. Prefer **rebase** when commits are clean; **squash** when history is messy.
- PR merge flow: create a temp branch from `main`, merge the PR branch into it (prefer squash unless commit history is important; use rebase/merge when it is). Always try to merge the PR unless its truly difficult, then use another approach. If we squash, add the PR author as a co-contributor. Apply fixes, add changelog entry (include PR # + thanks), run full gate before the final commit, commit, merge back to `main`, delete the temp branch, and end on `main`.
- If you review a PR and later do work on it, land via merge/squash (no direct-main commits) and always add the PR author as a co-contributor.
- When working on a PR: add a changelog entry with the PR number and thank the contributor.
- When working on an issue: reference the issue in the changelog entry.
- When merging a PR: leave a PR comment that explains exactly what we did and include the SHA hashes.
- When merging a PR from a new contributor: add their avatar to the README “Thanks to all clawtributors” thumbnail list.
- After merging a PR: run `bun scripts/update-clawtributors.ts` if the contributor is missing, then commit the regenerated README.
- Read this when submitting a PR: `docs/help/submitting-a-pr.md` ([Submitting a PR](https://docs.openclaw.ai/help/submitting-a-pr))
- Read this when submitting an issue: `docs/help/submitting-an-issue.md` ([Submitting an Issue](https://docs.openclaw.ai/help/submitting-an-issue))
## Shorthand Commands
- `sync`: if working tree is dirty, commit all changes (pick a sensible Conventional Commit message), then `git pull --rebase`; if rebase conflicts and cannot resolve, stop; otherwise `git push`.
### PR Workflow (Review vs Land)
- **Review mode (PR link only):** read `gh pr view/diff`; **do not** switch branches; **do not** change code.
- **Landing mode:** create an integration branch from `main`, bring in PR commits (**prefer rebase** for linear history; **merge allowed** when complexity/conflicts make it safer), apply fixes, add changelog (+ thanks + PR #), run full gate **locally before committing** (`pnpm lint && pnpm build && pnpm test`), commit, merge back to `main`, then `git switch main` (never stay on a topic branch after landing). Important: contributor needs to be in git graph after this!
## Security & Configuration Tips
- Web provider stores creds at `~/.clawdbot/credentials/`; rerun `moltbot login` if logged out.
- Pi sessions live under `~/.clawdbot/sessions/` by default; the base directory is not configurable.
- Web provider stores creds at `~/.openclaw/credentials/`; rerun `openclaw login` if logged out.
- Pi sessions live under `~/.openclaw/sessions/` by default; the base directory is not configurable.
- Environment variables: see `~/.profile`.
- Never commit or publish real phone numbers, videos, or live configuration values. Use obviously fake placeholders in docs, tests, and examples.
- Release flow: always read `docs/reference/RELEASING.md` and `docs/platforms/mac/release.md` before any release work; do not ask routine questions once those docs answer them.
- Release flow: always read `docs/reference/RELEASING.md` and `docs/platforms/mac/release.md` before any release work; do not ask routine questions once those docs answer them.
## Troubleshooting
- Rebrand/migration issues or legacy config/service warnings: run `moltbot doctor` (see `docs/gateway/doctor.md`).
- Rebrand/migration issues or legacy config/service warnings: run `openclaw doctor` (see `docs/gateway/doctor.md`).
## Agent-Specific Notes
- Vocabulary: "makeup" = "mac app".
- Never edit `node_modules` (global/Homebrew/npm/git installs too). Updates overwrite. Skill notes go in `tools.md` or `AGENTS.md`.
- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/moltbot && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`.
- When adding a new `AGENTS.md` anywhere in the repo, also add a `CLAUDE.md` symlink pointing to it (example: `ln -s AGENTS.md CLAUDE.md`).
- Signal: "update fly" => `fly ssh console -a flawd-bot -C "bash -lc 'cd /data/clawd/openclaw && git pull --rebase origin main'"` then `fly machines restart e825232f34d058 -a flawd-bot`.
- When working on a GitHub Issue or PR, print the full URL at the end of the task.
- When answering questions, respond with high-confidence answers only: verify in code; do not guess.
- Never update the Carbon dependency.
@@ -117,12 +131,13 @@
- Patching dependencies (pnpm patches, overrides, or vendored changes) requires explicit approval; do not do this by default.
- CLI progress: use `src/cli/progress.ts` (`osc-progress` + `@clack/prompts` spinner); dont hand-roll spinners/bars.
- Status output: keep tables + ANSI-safe wrapping (`src/terminal/table.ts`); `status --all` = read-only/pasteable, `status --deep` = probes.
- Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the Moltbot Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep moltbot` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.**
- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the Moltbot subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`.
- Gateway currently runs only as the menubar app; there is no separate LaunchAgent/helper label installed. Restart via the OpenClaw Mac app or `scripts/restart-mac.sh`; to verify/kill use `launchctl print gui/$UID | grep openclaw` rather than assuming a fixed label. **When debugging on macOS, start/stop the gateway via the app, not ad-hoc tmux sessions; kill any temporary tunnels before handoff.**
- macOS logs: use `./scripts/clawlog.sh` to query unified logs for the OpenClaw subsystem; it supports follow/tail/category filters and expects passwordless sudo for `/usr/bin/log`.
- If shared guardrails are available locally, review them; otherwise follow this repo's guidance.
- SwiftUI state management (iOS/macOS): prefer the `Observation` framework (`@Observable`, `@Bindable`) over `ObservableObject`/`@StateObject`; dont introduce new `ObservableObject` unless required for compatibility, and migrate existing usages when touching related code.
- Connection providers: when adding a new connection, update every UI surface and docs (macOS app, web UI, mobile if applicable, onboarding/overview docs) and add matching status + configuration forms so provider lists and settings stay in sync.
- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/Moltbot/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION).
- Version locations: `package.json` (CLI), `apps/android/app/build.gradle.kts` (versionName/versionCode), `apps/ios/Sources/Info.plist` + `apps/ios/Tests/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `apps/macos/Sources/OpenClaw/Resources/Info.plist` (CFBundleShortVersionString/CFBundleVersion), `docs/install/updating.md` (pinned npm version), `docs/platforms/mac/release.md` (APP_VERSION/APP_BUILD examples), Peekaboo Xcode projects/Info.plists (MARKETING_VERSION/CURRENT_PROJECT_VERSION).
- "Bump version everywhere" means all version locations above **except** `appcast.xml` (only touch appcast when cutting a new macOS Sparkle release).
- **Restart apps:** “restart iOS/Android apps” means rebuild (recompile/install) and relaunch, not just kill/launch.
- **Device checks:** before testing, verify connected real devices (iOS/Android) before reaching for simulators/emulators.
- iOS Team ID lookup: `security find-identity -p codesigning -v` → use Apple Development (…) TEAMID. Fallback: `defaults read com.apple.dt.Xcode IDEProvisioningTeamIdentifiers`.
@@ -145,16 +160,17 @@
- Code style: add brief comments for tricky logic; keep files under ~500 LOC when feasible (split/refactor as needed).
- Tool schema guardrails (google-antigravity): avoid `Type.Union` in tool input schemas; no `anyOf`/`oneOf`/`allOf`. Use `stringEnum`/`optionalStringEnum` (Type.Unsafe enum) for string lists, and `Type.Optional(...)` instead of `... | null`. Keep top-level tool schema as `type: "object"` with `properties`.
- Tool schema guardrails: avoid raw `format` property names in tool schemas; some validators treat `format` as a reserved keyword and reject the schema.
- When asked to open a “session” file, open the Pi session logs under `~/.clawdbot/agents/<agentId>/sessions/*.jsonl` (use the `agent=<id>` value in the Runtime line of the system prompt; newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
- When asked to open a “session” file, open the Pi session logs under `~/.openclaw/agents/<agentId>/sessions/*.jsonl` (use the `agent=<id>` value in the Runtime line of the system prompt; newest unless a specific ID is given), not the default `sessions.json`. If logs are needed from another machine, SSH via Tailscale and read the same path there.
- Do not rebuild the macOS app over SSH; rebuilds must be run directly on the Mac.
- Never send streaming/partial replies to external messaging surfaces (WhatsApp, Telegram); only final replies should be delivered there. Streaming/tool events may still go to internal UIs/control channel.
- Voice wake forwarding tips:
- Command template should stay `moltbot-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Dont add extra quotes.
- launchd PATH is minimal; ensure the apps launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`moltbot` binaries resolve when invoked via `moltbot-mac`.
- For manual `moltbot message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tools escaping.
- Command template should stay `openclaw-mac agent --message "${text}" --thinking low`; `VoiceWakeForwarder` already shell-escapes `${text}`. Dont add extra quotes.
- launchd PATH is minimal; ensure the apps launch agent PATH includes standard system paths plus your pnpm bin (typically `$HOME/Library/pnpm`) so `pnpm`/`openclaw` binaries resolve when invoked via `openclaw-mac`.
- For manual `openclaw message send` messages that include `!`, use the heredoc pattern noted below to avoid the Bash tools escaping.
- Release guardrails: do not change version numbers without operators explicit consent; always ask permission before running any npm publish/release step.
## NPM + 1Password (publish/verify)
- Use the 1password skill; all `op` commands must run inside a fresh tmux session.
- Sign in: `eval "$(op signin --account my.1password.com)"` (app unlocked + integration on).
- OTP: `op read 'op://Private/Npmjs/one-time password?attribute=otp'`.

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1 @@
AGENTS.md
AGENTS.md

View File

@@ -1,11 +1,12 @@
# Contributing to Moltbot
# Contributing to OpenClaw
Welcome to the lobster tank! 🦞
## Quick Links
- **GitHub:** https://github.com/moltbot/moltbot
- **GitHub:** https://github.com/openclaw/openclaw
- **Discord:** https://discord.gg/qkhbAGHRBT
- **X/Twitter:** [@steipete](https://x.com/steipete) / [@moltbot](https://x.com/moltbot)
- **X/Twitter:** [@steipete](https://x.com/steipete) / [@openclaw](https://x.com/openclaw)
## Maintainers
@@ -15,25 +16,56 @@ Welcome to the lobster tank! 🦞
- **Shadow** - Discord + Slack subsystem
- GitHub: [@thewilloftheshadow](https://github.com/thewilloftheshadow) · X: [@4shad0wed](https://x.com/4shad0wed)
- **Vignesh** - Memory (QMD), formal modeling, TUI, and Lobster
- GitHub: [@vignesh07](https://github.com/vignesh07) · X: [@\_vgnsh](https://x.com/_vgnsh)
- **Jos** - Telegram, API, Nix mode
- GitHub: [@joshp123](https://github.com/joshp123) · X: [@jjpcodes](https://x.com/jjpcodes)
- **Christoph Nakazawa** - JS Infra
- GitHub: [@cpojer](https://github.com/cpojer) · X: [@cnakazawa](https://x.com/cnakazawa)
- **Gustavo Madeira Santana** - Multi-agents, CLI, web UI
- GitHub: [@gumadeiras](https://github.com/gumadeiras) · X: [@gumadeiras](https://x.com/gumadeiras)
- **Maximilian Nussbaumer** - DevOps, CI, Code Sanity
- GitHub: [@quotentiroler](https://github.com/quotentiroler) · X: [@quotentiroler](https://x.com/quotentiroler)
## How to Contribute
1. **Bugs & small fixes** → Open a PR!
2. **New features / architecture** → Start a [GitHub Discussion](https://github.com/moltbot/moltbot/discussions) or ask in Discord first
2. **New features / architecture** → Start a [GitHub Discussion](https://github.com/openclaw/openclaw/discussions) or ask in Discord first
3. **Questions** → Discord #setup-help
## Before You PR
- Test locally with your Moltbot instance
- Run linter: `npm run lint`
- Test locally with your OpenClaw instance
- Run tests: `pnpm build && pnpm check && pnpm test`
- Ensure CI checks pass
- Keep PRs focused (one thing per PR)
- Describe what & why
## Control UI Decorators
The Control UI uses Lit with **legacy** decorators (current Rollup parsing does not support
`accessor` fields required for standard decorators). When adding reactive fields, keep the
legacy style:
```ts
@state() foo = "bar";
@property({ type: Number }) count = 0;
```
The root `tsconfig.json` is configured for legacy decorators (`experimentalDecorators: true`)
with `useDefineForClassFields: false`. Avoid flipping these unless you are also updating the UI
build tooling to support standard decorators.
## AI/Vibe-Coded PRs Welcome! 🤖
Built with Codex, Claude, or other AI tools? **Awesome - just mark it!**
Please include in your PR:
- [ ] Mark as AI-assisted in the PR title or description
- [ ] Note the degree of testing (untested / lightly tested / fully tested)
- [ ] Include prompts or session logs if possible (super helpful!)
@@ -44,9 +76,36 @@ AI PRs are first-class citizens here. We just want transparency so reviewers kno
## Current Focus & Roadmap 🗺
We are currently prioritizing:
- **Stability**: Fixing edge cases in channel connections (WhatsApp/Telegram).
- **UX**: Improving the onboarding wizard and error messages.
- **Skills**: Expanding the library of bundled skills and improving the Skill Creation developer experience.
- **Skills**: For skill contributions, head to [ClawHub](https://clawhub.ai/) — the community hub for OpenClaw skills.
- **Performance**: Optimizing token usage and compaction logic.
Check the [GitHub Issues](https://github.com/moltbot/moltbot/issues) for "good first issue" labels!
Check the [GitHub Issues](https://github.com/openclaw/openclaw/issues) for "good first issue" labels!
## Report a Vulnerability
We take security reports seriously. Report vulnerabilities directly to the repository where the issue lives:
- **Core CLI and gateway** — [openclaw/openclaw](https://github.com/openclaw/openclaw)
- **macOS desktop app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/macos)
- **iOS app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/ios)
- **Android app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/android)
- **ClawHub** — [openclaw/clawhub](https://github.com/openclaw/clawhub)
- **Trust and threat model** — [openclaw/trust](https://github.com/openclaw/trust)
For issues that don't fit a specific repo, or if you're unsure, email **security@openclaw.ai** and we'll route it.
### Required in Reports
1. **Title**
2. **Severity Assessment**
3. **Impact**
4. **Affected Component**
5. **Technical Reproduction**
6. **Demonstrated Impact**
7. **Environment**
8. **Remediation Advice**
Reports without reproduction steps, demonstrated impact, and remediation advice will be deprioritized. Given the volume of AI-generated scanner findings, we must ensure we're receiving vetted reports from researchers who understand the issues.

View File

@@ -8,10 +8,10 @@ RUN corepack enable
WORKDIR /app
ARG CLAWDBOT_DOCKER_APT_PACKAGES=""
RUN if [ -n "$CLAWDBOT_DOCKER_APT_PACKAGES" ]; then \
ARG OPENCLAW_DOCKER_APT_PACKAGES=""
RUN if [ -n "$OPENCLAW_DOCKER_APT_PACKAGES" ]; then \
apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $CLAWDBOT_DOCKER_APT_PACKAGES && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $OPENCLAW_DOCKER_APT_PACKAGES && \
apt-get clean && \
rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*; \
fi
@@ -24,17 +24,25 @@ COPY scripts ./scripts
RUN pnpm install --frozen-lockfile
COPY . .
RUN CLAWDBOT_A2UI_SKIP_MISSING=1 pnpm build
RUN pnpm build
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
ENV CLAWDBOT_PREFER_PNPM=1
RUN pnpm ui:install
ENV OPENCLAW_PREFER_PNPM=1
RUN pnpm ui:build
ENV NODE_ENV=production
# Allow non-root user to write temp files during runtime/tests.
RUN chown -R node:node /app
# Security hardening: Run as non-root user
# The node:22-bookworm image includes a 'node' user (uid 1000)
# This reduces the attack surface by preventing container escape via root privileges
USER node
CMD ["node", "dist/index.js"]
# Start gateway server with default config.
# Binds to loopback (127.0.0.1) by default for security.
#
# For container platforms requiring external health checks:
# 1. Set OPENCLAW_GATEWAY_TOKEN or OPENCLAW_GATEWAY_PASSWORD env var
# 2. Override CMD: ["node","openclaw.mjs","gateway","--allow-unconfigured","--bind","lan"]
CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]

View File

@@ -13,4 +13,8 @@ RUN apt-get update \
ripgrep \
&& rm -rf /var/lib/apt/lists/*
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
WORKDIR /home/sandbox
CMD ["sleep", "infinity"]

View File

@@ -20,9 +20,13 @@ RUN apt-get update \
xvfb \
&& rm -rf /var/lib/apt/lists/*
COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/moltbot-sandbox-browser
RUN chmod +x /usr/local/bin/moltbot-sandbox-browser
COPY scripts/sandbox-browser-entrypoint.sh /usr/local/bin/openclaw-sandbox-browser
RUN chmod +x /usr/local/bin/openclaw-sandbox-browser
RUN useradd --create-home --shell /bin/bash sandbox
USER sandbox
WORKDIR /home/sandbox
EXPOSE 9222 5900 6080
CMD ["moltbot-sandbox-browser"]
CMD ["openclaw-sandbox-browser"]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

423
README.md
View File

@@ -1,7 +1,10 @@
# 🦞 Moltbot — Personal AI Assistant
# 🦞 OpenClaw — Personal AI Assistant
<p align="center">
<img src="https://raw.githubusercontent.com/moltbot/moltbot/main/docs/whatsapp-clawd.jpg" alt="Clawdbot" width="400">
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/openclaw/openclaw/main/docs/assets/openclaw-logo-text-dark.png">
<img src="https://raw.githubusercontent.com/openclaw/openclaw/main/docs/assets/openclaw-logo-text.png" alt="OpenClaw" width="500">
</picture>
</p>
<p align="center">
@@ -9,68 +12,68 @@
</p>
<p align="center">
<a href="https://github.com/moltbot/moltbot/actions/workflows/ci.yml?branch=main"><img src="https://img.shields.io/github/actions/workflow/status/moltbot/moltbot/ci.yml?branch=main&style=for-the-badge" alt="CI status"></a>
<a href="https://github.com/moltbot/moltbot/releases"><img src="https://img.shields.io/github/v/release/moltbot/moltbot?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
<a href="https://deepwiki.com/moltbot/moltbot"><img src="https://img.shields.io/badge/DeepWiki-moltbot-111111?style=for-the-badge" alt="DeepWiki"></a>
<a href="https://github.com/openclaw/openclaw/actions/workflows/ci.yml?branch=main"><img src="https://img.shields.io/github/actions/workflow/status/openclaw/openclaw/ci.yml?branch=main&style=for-the-badge" alt="CI status"></a>
<a href="https://github.com/openclaw/openclaw/releases"><img src="https://img.shields.io/github/v/release/openclaw/openclaw?include_prereleases&style=for-the-badge" alt="GitHub release"></a>
<a href="https://discord.gg/clawd"><img src="https://img.shields.io/discord/1456350064065904867?label=Discord&logo=discord&logoColor=white&color=5865F2&style=for-the-badge" alt="Discord"></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-blue.svg?style=for-the-badge" alt="MIT License"></a>
</p>
**Moltbot** is a *personal AI assistant* you run on your own devices.
**OpenClaw** is a _personal AI assistant_ you run on your own devices.
It answers you on the channels you already use (WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, Microsoft Teams, WebChat), plus extension channels like BlueBubbles, Matrix, Zalo, and Zalo Personal. It can speak and listen on macOS/iOS/Android, and can render a live Canvas you control. The Gateway is just the control plane — the product is the assistant.
If you want a personal, single-user assistant that feels local, fast, and always-on, this is it.
[Website](https://molt.bot) · [Docs](https://docs.molt.bot) · [Getting Started](https://docs.molt.bot/start/getting-started) · [Updating](https://docs.molt.bot/install/updating) · [Showcase](https://docs.molt.bot/start/showcase) · [FAQ](https://docs.molt.bot/start/faq) · [Wizard](https://docs.molt.bot/start/wizard) · [Nix](https://github.com/moltbot/nix-clawdbot) · [Docker](https://docs.molt.bot/install/docker) · [Discord](https://discord.gg/clawd)
[Website](https://openclaw.ai) · [Docs](https://docs.openclaw.ai) · [DeepWiki](https://deepwiki.com/openclaw/openclaw) · [Getting Started](https://docs.openclaw.ai/start/getting-started) · [Updating](https://docs.openclaw.ai/install/updating) · [Showcase](https://docs.openclaw.ai/start/showcase) · [FAQ](https://docs.openclaw.ai/start/faq) · [Wizard](https://docs.openclaw.ai/start/wizard) · [Nix](https://github.com/openclaw/nix-openclaw) · [Docker](https://docs.openclaw.ai/install/docker) · [Discord](https://discord.gg/clawd)
Preferred setup: run the onboarding wizard (`moltbot onboard`). It walks through gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
Preferred setup: run the onboarding wizard (`openclaw onboard`) in your terminal.
The wizard guides you step by step through setting up the gateway, workspace, channels, and skills. The CLI wizard is the recommended path and works on **macOS, Linux, and Windows (via WSL2; strongly recommended)**.
Works with npm, pnpm, or bun.
New install? Start here: [Getting started](https://docs.molt.bot/start/getting-started)
New install? Start here: [Getting started](https://docs.openclaw.ai/start/getting-started)
**Subscriptions (OAuth):**
- **[Anthropic](https://www.anthropic.com/)** (Claude Pro/Max)
- **[OpenAI](https://openai.com/)** (ChatGPT/Codex)
Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.5** for longcontext strength and better promptinjection resistance. See [Onboarding](https://docs.molt.bot/start/onboarding).
Model note: while any model is supported, I strongly recommend **Anthropic Pro/Max (100/200) + Opus 4.6** for longcontext strength and better promptinjection resistance. See [Onboarding](https://docs.openclaw.ai/start/onboarding).
## Models (selection + auth)
- Models config + CLI: [Models](https://docs.molt.bot/concepts/models)
- Auth profile rotation (OAuth vs API keys) + fallbacks: [Model failover](https://docs.molt.bot/concepts/model-failover)
- Models config + CLI: [Models](https://docs.openclaw.ai/concepts/models)
- Auth profile rotation (OAuth vs API keys) + fallbacks: [Model failover](https://docs.openclaw.ai/concepts/model-failover)
## Install (recommended)
Runtime: **Node ≥22**.
```bash
npm install -g moltbot@latest
# or: pnpm add -g moltbot@latest
npm install -g openclaw@latest
# or: pnpm add -g openclaw@latest
moltbot onboard --install-daemon
openclaw onboard --install-daemon
```
The wizard installs the Gateway daemon (launchd/systemd user service) so it stays running.
Legacy note: `clawdbot` remains available as a compatibility shim.
## Quick start (TL;DR)
Runtime: **Node ≥22**.
Full beginner guide (auth, pairing, channels): [Getting started](https://docs.molt.bot/start/getting-started)
Full beginner guide (auth, pairing, channels): [Getting started](https://docs.openclaw.ai/start/getting-started)
```bash
moltbot onboard --install-daemon
openclaw onboard --install-daemon
moltbot gateway --port 18789 --verbose
openclaw gateway --port 18789 --verbose
# Send a message
moltbot message send --to +1234567890 --message "Hello from Moltbot"
openclaw message send --to +1234567890 --message "Hello from OpenClaw"
# Talk to the assistant (optionally deliver back to any connected channel: WhatsApp/Telegram/Slack/Discord/Google Chat/Signal/iMessage/BlueBubbles/Microsoft Teams/Matrix/Zalo/Zalo Personal/WebChat)
moltbot agent --message "Ship checklist" --thinking high
openclaw agent --message "Ship checklist" --thinking high
```
Upgrading? [Updating guide](https://docs.molt.bot/install/updating) (and run `moltbot doctor`).
Upgrading? [Updating guide](https://docs.openclaw.ai/install/updating) (and run `openclaw doctor`).
## Development channels
@@ -78,94 +81,101 @@ Upgrading? [Updating guide](https://docs.molt.bot/install/updating) (and run `mo
- **beta**: prerelease tags (`vYYYY.M.D-beta.N`), npm dist-tag `beta` (macOS app may be missing).
- **dev**: moving head of `main`, npm dist-tag `dev` (when published).
Switch channels (git + npm): `moltbot update --channel stable|beta|dev`.
Details: [Development channels](https://docs.molt.bot/install/development-channels).
Switch channels (git + npm): `openclaw update --channel stable|beta|dev`.
Details: [Development channels](https://docs.openclaw.ai/install/development-channels).
## From source (development)
Prefer `pnpm` for builds from source. Bun is optional for running TypeScript directly.
```bash
git clone https://github.com/moltbot/moltbot.git
cd moltbot
git clone https://github.com/openclaw/openclaw.git
cd openclaw
pnpm install
pnpm ui:build # auto-installs UI deps on first run
pnpm build
pnpm moltbot onboard --install-daemon
pnpm openclaw onboard --install-daemon
# Dev loop (auto-reload on TS changes)
pnpm gateway:watch
```
Note: `pnpm moltbot ...` runs TypeScript directly (via `tsx`). `pnpm build` produces `dist/` for running via Node / the packaged `moltbot` binary.
Note: `pnpm openclaw ...` runs TypeScript directly (via `tsx`). `pnpm build` produces `dist/` for running via Node / the packaged `openclaw` binary.
## Security defaults (DM access)
Moltbot connects to real messaging surfaces. Treat inbound DMs as **untrusted input**.
OpenClaw connects to real messaging surfaces. Treat inbound DMs as **untrusted input**.
Full security guide: [Security](https://docs.molt.bot/gateway/security)
Full security guide: [Security](https://docs.openclaw.ai/gateway/security)
Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack:
- **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dm.policy="pairing"` / `channels.slack.dm.policy="pairing"`): unknown senders receive a short pairing code and the bot does not process their message.
- Approve with: `moltbot pairing approve <channel> <code>` (then the sender is added to a local allowlist store).
- Approve with: `openclaw pairing approve <channel> <code>` (then the sender is added to a local allowlist store).
- Public inbound DMs require an explicit opt-in: set `dmPolicy="open"` and include `"*"` in the channel allowlist (`allowFrom` / `channels.discord.dm.allowFrom` / `channels.slack.dm.allowFrom`).
Run `moltbot doctor` to surface risky/misconfigured DM policies.
Run `openclaw doctor` to surface risky/misconfigured DM policies.
## Highlights
- **[Local-first Gateway](https://docs.molt.bot/gateway)** — single control plane for sessions, channels, tools, and events.
- **[Multi-channel inbox](https://docs.molt.bot/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, iMessage, BlueBubbles, Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.molt.bot/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
- **[Live Canvas](https://docs.molt.bot/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui).
- **[First-class tools](https://docs.molt.bot/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
- **[Companion apps](https://docs.molt.bot/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.molt.bot/nodes).
- **[Onboarding](https://docs.molt.bot/start/wizard) + [skills](https://docs.molt.bot/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills.
- **[Local-first Gateway](https://docs.openclaw.ai/gateway)** — single control plane for sessions, channels, tools, and events.
- **[Multi-channel inbox](https://docs.openclaw.ai/channels)** — WhatsApp, Telegram, Slack, Discord, Google Chat, Signal, BlueBubbles (iMessage), iMessage (legacy), Microsoft Teams, Matrix, Zalo, Zalo Personal, WebChat, macOS, iOS/Android.
- **[Multi-agent routing](https://docs.openclaw.ai/gateway/configuration)** — route inbound channels/accounts/peers to isolated agents (workspaces + per-agent sessions).
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — always-on speech for macOS/iOS/Android with ElevenLabs.
- **[Live Canvas](https://docs.openclaw.ai/platforms/mac/canvas)** — agent-driven visual workspace with [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
- **[First-class tools](https://docs.openclaw.ai/tools)** — browser, canvas, nodes, cron, sessions, and Discord/Slack actions.
- **[Companion apps](https://docs.openclaw.ai/platforms/macos)** — macOS menu bar app + iOS/Android [nodes](https://docs.openclaw.ai/nodes).
- **[Onboarding](https://docs.openclaw.ai/start/wizard) + [skills](https://docs.openclaw.ai/tools/skills)** — wizard-driven setup with bundled/managed/workspace skills.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=moltbot/moltbot&type=date&legend=top-left)](https://www.star-history.com/#moltbot/moltbot&type=date&legend=top-left)
[![Star History Chart](https://api.star-history.com/svg?repos=openclaw/openclaw&type=date&legend=top-left)](https://www.star-history.com/#openclaw/openclaw&type=date&legend=top-left)
## Everything we built so far
### Core platform
- [Gateway WS control plane](https://docs.molt.bot/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.molt.bot/web), and [Canvas host](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui).
- [CLI surface](https://docs.molt.bot/tools/agent-send): gateway, agent, send, [wizard](https://docs.molt.bot/start/wizard), and [doctor](https://docs.molt.bot/gateway/doctor).
- [Pi agent runtime](https://docs.molt.bot/concepts/agent) in RPC mode with tool streaming and block streaming.
- [Session model](https://docs.molt.bot/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.molt.bot/concepts/groups).
- [Media pipeline](https://docs.molt.bot/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.molt.bot/nodes/audio).
- [Gateway WS control plane](https://docs.openclaw.ai/gateway) with sessions, presence, config, cron, webhooks, [Control UI](https://docs.openclaw.ai/web), and [Canvas host](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui).
- [CLI surface](https://docs.openclaw.ai/tools/agent-send): gateway, agent, send, [wizard](https://docs.openclaw.ai/start/wizard), and [doctor](https://docs.openclaw.ai/gateway/doctor).
- [Pi agent runtime](https://docs.openclaw.ai/concepts/agent) in RPC mode with tool streaming and block streaming.
- [Session model](https://docs.openclaw.ai/concepts/session): `main` for direct chats, group isolation, activation modes, queue modes, reply-back. Group rules: [Groups](https://docs.openclaw.ai/concepts/groups).
- [Media pipeline](https://docs.openclaw.ai/nodes/images): images/audio/video, transcription hooks, size caps, temp file lifecycle. Audio details: [Audio](https://docs.openclaw.ai/nodes/audio).
### Channels
- [Channels](https://docs.molt.bot/channels): [WhatsApp](https://docs.molt.bot/channels/whatsapp) (Baileys), [Telegram](https://docs.molt.bot/channels/telegram) (grammY), [Slack](https://docs.molt.bot/channels/slack) (Bolt), [Discord](https://docs.molt.bot/channels/discord) (discord.js), [Google Chat](https://docs.molt.bot/channels/googlechat) (Chat API), [Signal](https://docs.molt.bot/channels/signal) (signal-cli), [iMessage](https://docs.molt.bot/channels/imessage) (imsg), [BlueBubbles](https://docs.molt.bot/channels/bluebubbles) (extension), [Microsoft Teams](https://docs.molt.bot/channels/msteams) (extension), [Matrix](https://docs.molt.bot/channels/matrix) (extension), [Zalo](https://docs.molt.bot/channels/zalo) (extension), [Zalo Personal](https://docs.molt.bot/channels/zalouser) (extension), [WebChat](https://docs.molt.bot/web/webchat).
- [Group routing](https://docs.molt.bot/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.molt.bot/channels).
- [Channels](https://docs.openclaw.ai/channels): [WhatsApp](https://docs.openclaw.ai/channels/whatsapp) (Baileys), [Telegram](https://docs.openclaw.ai/channels/telegram) (grammY), [Slack](https://docs.openclaw.ai/channels/slack) (Bolt), [Discord](https://docs.openclaw.ai/channels/discord) (discord.js), [Google Chat](https://docs.openclaw.ai/channels/googlechat) (Chat API), [Signal](https://docs.openclaw.ai/channels/signal) (signal-cli), [BlueBubbles](https://docs.openclaw.ai/channels/bluebubbles) (iMessage, recommended), [iMessage](https://docs.openclaw.ai/channels/imessage) (legacy imsg), [Microsoft Teams](https://docs.openclaw.ai/channels/msteams) (extension), [Matrix](https://docs.openclaw.ai/channels/matrix) (extension), [Zalo](https://docs.openclaw.ai/channels/zalo) (extension), [Zalo Personal](https://docs.openclaw.ai/channels/zalouser) (extension), [WebChat](https://docs.openclaw.ai/web/webchat).
- [Group routing](https://docs.openclaw.ai/concepts/group-messages): mention gating, reply tags, per-channel chunking and routing. Channel rules: [Channels](https://docs.openclaw.ai/channels).
### Apps + nodes
- [macOS app](https://docs.molt.bot/platforms/macos): menu bar control plane, [Voice Wake](https://docs.molt.bot/nodes/voicewake)/PTT, [Talk Mode](https://docs.molt.bot/nodes/talk) overlay, [WebChat](https://docs.molt.bot/web/webchat), debug tools, [remote gateway](https://docs.molt.bot/gateway/remote) control.
- [iOS node](https://docs.molt.bot/platforms/ios): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Voice Wake](https://docs.molt.bot/nodes/voicewake), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, Bonjour pairing.
- [Android node](https://docs.molt.bot/platforms/android): [Canvas](https://docs.molt.bot/platforms/mac/canvas), [Talk Mode](https://docs.molt.bot/nodes/talk), camera, screen recording, optional SMS.
- [macOS node mode](https://docs.molt.bot/nodes): system.run/notify + canvas/camera exposure.
- [macOS app](https://docs.openclaw.ai/platforms/macos): menu bar control plane, [Voice Wake](https://docs.openclaw.ai/nodes/voicewake)/PTT, [Talk Mode](https://docs.openclaw.ai/nodes/talk) overlay, [WebChat](https://docs.openclaw.ai/web/webchat), debug tools, [remote gateway](https://docs.openclaw.ai/gateway/remote) control.
- [iOS node](https://docs.openclaw.ai/platforms/ios): [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), [Voice Wake](https://docs.openclaw.ai/nodes/voicewake), [Talk Mode](https://docs.openclaw.ai/nodes/talk), camera, screen recording, Bonjour pairing.
- [Android node](https://docs.openclaw.ai/platforms/android): [Canvas](https://docs.openclaw.ai/platforms/mac/canvas), [Talk Mode](https://docs.openclaw.ai/nodes/talk), camera, screen recording, optional SMS.
- [macOS node mode](https://docs.openclaw.ai/nodes): system.run/notify + canvas/camera exposure.
### Tools + automation
- [Browser control](https://docs.molt.bot/tools/browser): dedicated moltbot Chrome/Chromium, snapshots, actions, uploads, profiles.
- [Canvas](https://docs.molt.bot/platforms/mac/canvas): [A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot.
- [Nodes](https://docs.molt.bot/nodes): camera snap/clip, screen record, [location.get](https://docs.molt.bot/nodes/location-command), notifications.
- [Cron + wakeups](https://docs.molt.bot/automation/cron-jobs); [webhooks](https://docs.molt.bot/automation/webhook); [Gmail Pub/Sub](https://docs.molt.bot/automation/gmail-pubsub).
- [Skills platform](https://docs.molt.bot/tools/skills): bundled, managed, and workspace skills with install gating + UI.
- [Browser control](https://docs.openclaw.ai/tools/browser): dedicated openclaw Chrome/Chromium, snapshots, actions, uploads, profiles.
- [Canvas](https://docs.openclaw.ai/platforms/mac/canvas): [A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui) push/reset, eval, snapshot.
- [Nodes](https://docs.openclaw.ai/nodes): camera snap/clip, screen record, [location.get](https://docs.openclaw.ai/nodes/location-command), notifications.
- [Cron + wakeups](https://docs.openclaw.ai/automation/cron-jobs); [webhooks](https://docs.openclaw.ai/automation/webhook); [Gmail Pub/Sub](https://docs.openclaw.ai/automation/gmail-pubsub).
- [Skills platform](https://docs.openclaw.ai/tools/skills): bundled, managed, and workspace skills with install gating + UI.
### Runtime + safety
- [Channel routing](https://docs.molt.bot/concepts/channel-routing), [retry policy](https://docs.molt.bot/concepts/retry), and [streaming/chunking](https://docs.molt.bot/concepts/streaming).
- [Presence](https://docs.molt.bot/concepts/presence), [typing indicators](https://docs.molt.bot/concepts/typing-indicators), and [usage tracking](https://docs.molt.bot/concepts/usage-tracking).
- [Models](https://docs.molt.bot/concepts/models), [model failover](https://docs.molt.bot/concepts/model-failover), and [session pruning](https://docs.molt.bot/concepts/session-pruning).
- [Security](https://docs.molt.bot/gateway/security) and [troubleshooting](https://docs.molt.bot/channels/troubleshooting).
- [Channel routing](https://docs.openclaw.ai/concepts/channel-routing), [retry policy](https://docs.openclaw.ai/concepts/retry), and [streaming/chunking](https://docs.openclaw.ai/concepts/streaming).
- [Presence](https://docs.openclaw.ai/concepts/presence), [typing indicators](https://docs.openclaw.ai/concepts/typing-indicators), and [usage tracking](https://docs.openclaw.ai/concepts/usage-tracking).
- [Models](https://docs.openclaw.ai/concepts/models), [model failover](https://docs.openclaw.ai/concepts/model-failover), and [session pruning](https://docs.openclaw.ai/concepts/session-pruning).
- [Security](https://docs.openclaw.ai/gateway/security) and [troubleshooting](https://docs.openclaw.ai/channels/troubleshooting).
### Ops + packaging
- [Control UI](https://docs.molt.bot/web) + [WebChat](https://docs.molt.bot/web/webchat) served directly from the Gateway.
- [Tailscale Serve/Funnel](https://docs.molt.bot/gateway/tailscale) or [SSH tunnels](https://docs.molt.bot/gateway/remote) with token/password auth.
- [Nix mode](https://docs.molt.bot/install/nix) for declarative config; [Docker](https://docs.molt.bot/install/docker)-based installs.
- [Doctor](https://docs.molt.bot/gateway/doctor) migrations, [logging](https://docs.molt.bot/logging).
- [Control UI](https://docs.openclaw.ai/web) + [WebChat](https://docs.openclaw.ai/web/webchat) served directly from the Gateway.
- [Tailscale Serve/Funnel](https://docs.openclaw.ai/gateway/tailscale) or [SSH tunnels](https://docs.openclaw.ai/gateway/remote) with token/password auth.
- [Nix mode](https://docs.openclaw.ai/install/nix) for declarative config; [Docker](https://docs.openclaw.ai/install/docker)-based installs.
- [Doctor](https://docs.openclaw.ai/gateway/doctor) migrations, [logging](https://docs.openclaw.ai/logging).
## How it works (short)
@@ -180,7 +190,7 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu
└──────────────┬────────────────┘
├─ Pi agent (RPC)
├─ CLI (moltbot …)
├─ CLI (openclaw …)
├─ WebChat UI
├─ macOS app
└─ iOS / Android nodes
@@ -188,28 +198,29 @@ WhatsApp / Telegram / Slack / Discord / Google Chat / Signal / iMessage / BlueBu
## Key subsystems
- **[Gateway WebSocket network](https://docs.molt.bot/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.molt.bot/gateway)).
- **[Tailscale exposure](https://docs.molt.bot/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.molt.bot/gateway/remote)).
- **[Browser control](https://docs.molt.bot/tools/browser)** — moltbotmanaged Chrome/Chromium with CDP control.
- **[Canvas + A2UI](https://docs.molt.bot/platforms/mac/canvas)** — agentdriven visual workspace (A2UI host: [Canvas/A2UI](https://docs.molt.bot/platforms/mac/canvas#canvas-a2ui)).
- **[Voice Wake](https://docs.molt.bot/nodes/voicewake) + [Talk Mode](https://docs.molt.bot/nodes/talk)** — alwayson speech and continuous conversation.
- **[Nodes](https://docs.molt.bot/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOSonly `system.run`/`system.notify`.
- **[Gateway WebSocket network](https://docs.openclaw.ai/concepts/architecture)** — single WS control plane for clients, tools, and events (plus ops: [Gateway runbook](https://docs.openclaw.ai/gateway)).
- **[Tailscale exposure](https://docs.openclaw.ai/gateway/tailscale)** — Serve/Funnel for the Gateway dashboard + WS (remote access: [Remote](https://docs.openclaw.ai/gateway/remote)).
- **[Browser control](https://docs.openclaw.ai/tools/browser)** — openclawmanaged Chrome/Chromium with CDP control.
- **[Canvas + A2UI](https://docs.openclaw.ai/platforms/mac/canvas)** — agentdriven visual workspace (A2UI host: [Canvas/A2UI](https://docs.openclaw.ai/platforms/mac/canvas#canvas-a2ui)).
- **[Voice Wake](https://docs.openclaw.ai/nodes/voicewake) + [Talk Mode](https://docs.openclaw.ai/nodes/talk)** — alwayson speech and continuous conversation.
- **[Nodes](https://docs.openclaw.ai/nodes)** — Canvas, camera snap/clip, screen record, `location.get`, notifications, plus macOSonly `system.run`/`system.notify`.
## Tailscale access (Gateway dashboard)
Moltbot can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`:
OpenClaw can auto-configure Tailscale **Serve** (tailnet-only) or **Funnel** (public) while the Gateway stays bound to loopback. Configure `gateway.tailscale.mode`:
- `off`: no Tailscale automation (default).
- `serve`: tailnet-only HTTPS via `tailscale serve` (uses Tailscale identity headers by default).
- `funnel`: public HTTPS via `tailscale funnel` (requires shared password auth).
Notes:
- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (Moltbot enforces this).
- `gateway.bind` must stay `loopback` when Serve/Funnel is enabled (OpenClaw enforces this).
- Serve can be forced to require a password by setting `gateway.auth.mode: "password"` or `gateway.auth.allowTailscale: false`.
- Funnel refuses to start unless `gateway.auth.mode: "password"` is set.
- Optional: `gateway.tailscale.resetOnExit` to undo Serve/Funnel on shutdown.
Details: [Tailscale guide](https://docs.molt.bot/gateway/tailscale) · [Web surfaces](https://docs.molt.bot/web)
Details: [Tailscale guide](https://docs.openclaw.ai/gateway/tailscale) · [Web surfaces](https://docs.openclaw.ai/web)
## Remote Gateway (Linux is great)
@@ -217,9 +228,9 @@ Its perfectly fine to run the Gateway on a small Linux instance. Clients (mac
- **Gateway host** runs the exec tool and channel connections by default.
- **Device nodes** run devicelocal actions (`system.run`, camera, screen recording, notifications) via `node.invoke`.
In short: exec runs where the Gateway lives; device actions run where the device lives.
In short: exec runs where the Gateway lives; device actions run where the device lives.
Details: [Remote access](https://docs.molt.bot/gateway/remote) · [Nodes](https://docs.molt.bot/nodes) · [Security](https://docs.molt.bot/gateway/security)
Details: [Remote access](https://docs.openclaw.ai/gateway/remote) · [Nodes](https://docs.openclaw.ai/nodes) · [Security](https://docs.openclaw.ai/gateway/security)
## macOS permissions via the Gateway protocol
@@ -234,22 +245,22 @@ Elevated bash (host permissions) is separate from macOS TCC:
- Use `/elevated on|off` to toggle persession elevated access when enabled + allowlisted.
- Gateway persists the persession toggle via `sessions.patch` (WS method) alongside `thinkingLevel`, `verboseLevel`, `model`, `sendPolicy`, and `groupActivation`.
Details: [Nodes](https://docs.molt.bot/nodes) · [macOS app](https://docs.molt.bot/platforms/macos) · [Gateway protocol](https://docs.molt.bot/concepts/architecture)
Details: [Nodes](https://docs.openclaw.ai/nodes) · [macOS app](https://docs.openclaw.ai/platforms/macos) · [Gateway protocol](https://docs.openclaw.ai/concepts/architecture)
## Agent to Agent (sessions_* tools)
## Agent to Agent (sessions\_\* tools)
- Use these to coordinate work across sessions without jumping between chat surfaces.
- `sessions_list` — discover active sessions (agents) and their metadata.
- `sessions_history` — fetch transcript logs for a session.
- `sessions_send` — message another session; optional replyback pingpong + announce step (`REPLY_SKIP`, `ANNOUNCE_SKIP`).
Details: [Session tools](https://docs.molt.bot/concepts/session-tool)
Details: [Session tools](https://docs.openclaw.ai/concepts/session-tool)
## Skills registry (ClawdHub)
## Skills registry (ClawHub)
ClawdHub is a minimal skill registry. With ClawdHub enabled, the agent can search for skills automatically and pull in new ones as needed.
ClawHub is a minimal skill registry. With ClawHub enabled, the agent can search for skills automatically and pull in new ones as needed.
[ClawdHub](https://ClawdHub.com)
[ClawHub](https://clawhub.com)
## Chat commands
@@ -270,7 +281,7 @@ The Gateway alone delivers a great experience. All apps are optional and add ext
If you plan to build/run companion apps, follow the platform runbooks below.
### macOS (Moltbot.app) (optional)
### macOS (OpenClaw.app) (optional)
- Menu bar control for the Gateway and health.
- Voice Wake + push-to-talk overlay.
@@ -283,35 +294,35 @@ Note: signed builds required for macOS permissions to stick across rebuilds (see
- Pairs as a node via the Bridge.
- Voice trigger forwarding + Canvas surface.
- Controlled via `moltbot nodes …`.
- Controlled via `openclaw nodes …`.
Runbook: [iOS connect](https://docs.molt.bot/platforms/ios).
Runbook: [iOS connect](https://docs.openclaw.ai/platforms/ios).
### Android node (optional)
- Pairs via the same Bridge + pairing flow as iOS.
- Exposes Canvas, Camera, and Screen capture commands.
- Runbook: [Android connect](https://docs.molt.bot/platforms/android).
- Runbook: [Android connect](https://docs.openclaw.ai/platforms/android).
## Agent workspace + skills
- Workspace root: `~/clawd` (configurable via `agents.defaults.workspace`).
- Workspace root: `~/.openclaw/workspace` (configurable via `agents.defaults.workspace`).
- Injected prompt files: `AGENTS.md`, `SOUL.md`, `TOOLS.md`.
- Skills: `~/clawd/skills/<skill>/SKILL.md`.
- Skills: `~/.openclaw/workspace/skills/<skill>/SKILL.md`.
## Configuration
Minimal `~/.clawdbot/moltbot.json` (model + defaults):
Minimal `~/.openclaw/openclaw.json` (model + defaults):
```json5
{
agent: {
model: "anthropic/claude-opus-4-5"
}
model: "anthropic/claude-opus-4-6",
},
}
```
[Full configuration reference (all keys + examples).](https://docs.molt.bot/gateway/configuration)
[Full configuration reference (all keys + examples).](https://docs.openclaw.ai/gateway/configuration)
## Security model (important)
@@ -319,34 +330,34 @@ Minimal `~/.clawdbot/moltbot.json` (model + defaults):
- **Group/channel safety:** set `agents.defaults.sandbox.mode: "non-main"` to run **nonmain sessions** (groups/channels) inside persession Docker sandboxes; bash then runs in Docker for those sessions.
- **Sandbox defaults:** allowlist `bash`, `process`, `read`, `write`, `edit`, `sessions_list`, `sessions_history`, `sessions_send`, `sessions_spawn`; denylist `browser`, `canvas`, `nodes`, `cron`, `discord`, `gateway`.
Details: [Security guide](https://docs.molt.bot/gateway/security) · [Docker + sandboxing](https://docs.molt.bot/install/docker) · [Sandbox config](https://docs.molt.bot/gateway/configuration)
Details: [Security guide](https://docs.openclaw.ai/gateway/security) · [Docker + sandboxing](https://docs.openclaw.ai/install/docker) · [Sandbox config](https://docs.openclaw.ai/gateway/configuration)
### [WhatsApp](https://docs.molt.bot/channels/whatsapp)
### [WhatsApp](https://docs.openclaw.ai/channels/whatsapp)
- Link the device: `pnpm moltbot channels login` (stores creds in `~/.clawdbot/credentials`).
- Link the device: `pnpm openclaw channels login` (stores creds in `~/.openclaw/credentials`).
- Allowlist who can talk to the assistant via `channels.whatsapp.allowFrom`.
- If `channels.whatsapp.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
### [Telegram](https://docs.molt.bot/channels/telegram)
### [Telegram](https://docs.openclaw.ai/channels/telegram)
- Set `TELEGRAM_BOT_TOKEN` or `channels.telegram.botToken` (env wins).
- Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` as needed.
- Optional: set `channels.telegram.groups` (with `channels.telegram.groups."*".requireMention`); when set, it is a group allowlist (include `"*"` to allow all). Also `channels.telegram.allowFrom` or `channels.telegram.webhookUrl` + `channels.telegram.webhookSecret` as needed.
```json5
{
channels: {
telegram: {
botToken: "123456:ABCDEF"
}
}
botToken: "123456:ABCDEF",
},
},
}
```
### [Slack](https://docs.molt.bot/channels/slack)
### [Slack](https://docs.openclaw.ai/channels/slack)
- Set `SLACK_BOT_TOKEN` + `SLACK_APP_TOKEN` (or `channels.slack.botToken` + `channels.slack.appToken`).
### [Discord](https://docs.molt.bot/channels/discord)
### [Discord](https://docs.openclaw.ai/channels/discord)
- Set `DISCORD_BOT_TOKEN` or `channels.discord.token` (env wins).
- Optional: set `commands.native`, `commands.text`, or `commands.useAccessGroups`, plus `channels.discord.dm.allowFrom`, `channels.discord.guilds`, or `channels.discord.mediaMaxMb` as needed.
@@ -355,27 +366,33 @@ Details: [Security guide](https://docs.molt.bot/gateway/security) · [Docker + s
{
channels: {
discord: {
token: "1234abcd"
}
}
token: "1234abcd",
},
},
}
```
### [Signal](https://docs.molt.bot/channels/signal)
### [Signal](https://docs.openclaw.ai/channels/signal)
- Requires `signal-cli` and a `channels.signal` config section.
### [iMessage](https://docs.molt.bot/channels/imessage)
### [BlueBubbles (iMessage)](https://docs.openclaw.ai/channels/bluebubbles)
- macOS only; Messages must be signed in.
- **Recommended** iMessage integration.
- Configure `channels.bluebubbles.serverUrl` + `channels.bluebubbles.password` and a webhook (`channels.bluebubbles.webhookPath`).
- The BlueBubbles server runs on macOS; the Gateway can run on macOS or elsewhere.
### [iMessage (legacy)](https://docs.openclaw.ai/channels/imessage)
- Legacy macOS-only integration via `imsg` (Messages must be signed in).
- If `channels.imessage.groups` is set, it becomes a group allowlist; include `"*"` to allow all.
### [Microsoft Teams](https://docs.molt.bot/channels/msteams)
### [Microsoft Teams](https://docs.openclaw.ai/channels/msteams)
- Configure a Teams app + Bot Framework, then add a `msteams` config section.
- Allowlist who can talk via `msteams.allowFrom`; group access via `msteams.groupAllowFrom` or `msteams.groupPolicy: "open"`.
### [WebChat](https://docs.molt.bot/web/webchat)
### [WebChat](https://docs.openclaw.ai/web/webchat)
- Uses the Gateway WebSocket; no separate WebChat port/config.
@@ -385,87 +402,88 @@ Browser control (optional):
{
browser: {
enabled: true,
color: "#FF4500"
}
color: "#FF4500",
},
}
```
## Docs
Use these when youre past the onboarding flow and want the deeper reference.
- [Start with the docs index for navigation and “whats where.”](https://docs.molt.bot)
- [Read the architecture overview for the gateway + protocol model.](https://docs.molt.bot/concepts/architecture)
- [Use the full configuration reference when you need every key and example.](https://docs.molt.bot/gateway/configuration)
- [Run the Gateway by the book with the operational runbook.](https://docs.molt.bot/gateway)
- [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.molt.bot/web)
- [Understand remote access over SSH tunnels or tailnets.](https://docs.molt.bot/gateway/remote)
- [Follow the onboarding wizard flow for a guided setup.](https://docs.molt.bot/start/wizard)
- [Wire external triggers via the webhook surface.](https://docs.molt.bot/automation/webhook)
- [Set up Gmail Pub/Sub triggers.](https://docs.molt.bot/automation/gmail-pubsub)
- [Learn the macOS menu bar companion details.](https://docs.molt.bot/platforms/mac/menu-bar)
- [Platform guides: Windows (WSL2)](https://docs.molt.bot/platforms/windows), [Linux](https://docs.molt.bot/platforms/linux), [macOS](https://docs.molt.bot/platforms/macos), [iOS](https://docs.molt.bot/platforms/ios), [Android](https://docs.molt.bot/platforms/android)
- [Debug common failures with the troubleshooting guide.](https://docs.molt.bot/channels/troubleshooting)
- [Review security guidance before exposing anything.](https://docs.molt.bot/gateway/security)
- [Start with the docs index for navigation and “whats where.”](https://docs.openclaw.ai)
- [Read the architecture overview for the gateway + protocol model.](https://docs.openclaw.ai/concepts/architecture)
- [Use the full configuration reference when you need every key and example.](https://docs.openclaw.ai/gateway/configuration)
- [Run the Gateway by the book with the operational runbook.](https://docs.openclaw.ai/gateway)
- [Learn how the Control UI/Web surfaces work and how to expose them safely.](https://docs.openclaw.ai/web)
- [Understand remote access over SSH tunnels or tailnets.](https://docs.openclaw.ai/gateway/remote)
- [Follow the onboarding wizard flow for a guided setup.](https://docs.openclaw.ai/start/wizard)
- [Wire external triggers via the webhook surface.](https://docs.openclaw.ai/automation/webhook)
- [Set up Gmail Pub/Sub triggers.](https://docs.openclaw.ai/automation/gmail-pubsub)
- [Learn the macOS menu bar companion details.](https://docs.openclaw.ai/platforms/mac/menu-bar)
- [Platform guides: Windows (WSL2)](https://docs.openclaw.ai/platforms/windows), [Linux](https://docs.openclaw.ai/platforms/linux), [macOS](https://docs.openclaw.ai/platforms/macos), [iOS](https://docs.openclaw.ai/platforms/ios), [Android](https://docs.openclaw.ai/platforms/android)
- [Debug common failures with the troubleshooting guide.](https://docs.openclaw.ai/channels/troubleshooting)
- [Review security guidance before exposing anything.](https://docs.openclaw.ai/gateway/security)
## Advanced docs (discovery + control)
- [Discovery + transports](https://docs.molt.bot/gateway/discovery)
- [Bonjour/mDNS](https://docs.molt.bot/gateway/bonjour)
- [Gateway pairing](https://docs.molt.bot/gateway/pairing)
- [Remote gateway README](https://docs.molt.bot/gateway/remote-gateway-readme)
- [Control UI](https://docs.molt.bot/web/control-ui)
- [Dashboard](https://docs.molt.bot/web/dashboard)
- [Discovery + transports](https://docs.openclaw.ai/gateway/discovery)
- [Bonjour/mDNS](https://docs.openclaw.ai/gateway/bonjour)
- [Gateway pairing](https://docs.openclaw.ai/gateway/pairing)
- [Remote gateway README](https://docs.openclaw.ai/gateway/remote-gateway-readme)
- [Control UI](https://docs.openclaw.ai/web/control-ui)
- [Dashboard](https://docs.openclaw.ai/web/dashboard)
## Operations & troubleshooting
- [Health checks](https://docs.molt.bot/gateway/health)
- [Gateway lock](https://docs.molt.bot/gateway/gateway-lock)
- [Background process](https://docs.molt.bot/gateway/background-process)
- [Browser troubleshooting (Linux)](https://docs.molt.bot/tools/browser-linux-troubleshooting)
- [Logging](https://docs.molt.bot/logging)
- [Health checks](https://docs.openclaw.ai/gateway/health)
- [Gateway lock](https://docs.openclaw.ai/gateway/gateway-lock)
- [Background process](https://docs.openclaw.ai/gateway/background-process)
- [Browser troubleshooting (Linux)](https://docs.openclaw.ai/tools/browser-linux-troubleshooting)
- [Logging](https://docs.openclaw.ai/logging)
## Deep dives
- [Agent loop](https://docs.molt.bot/concepts/agent-loop)
- [Presence](https://docs.molt.bot/concepts/presence)
- [TypeBox schemas](https://docs.molt.bot/concepts/typebox)
- [RPC adapters](https://docs.molt.bot/reference/rpc)
- [Queue](https://docs.molt.bot/concepts/queue)
- [Agent loop](https://docs.openclaw.ai/concepts/agent-loop)
- [Presence](https://docs.openclaw.ai/concepts/presence)
- [TypeBox schemas](https://docs.openclaw.ai/concepts/typebox)
- [RPC adapters](https://docs.openclaw.ai/reference/rpc)
- [Queue](https://docs.openclaw.ai/concepts/queue)
## Workspace & skills
- [Skills config](https://docs.molt.bot/tools/skills-config)
- [Default AGENTS](https://docs.molt.bot/reference/AGENTS.default)
- [Templates: AGENTS](https://docs.molt.bot/reference/templates/AGENTS)
- [Templates: BOOTSTRAP](https://docs.molt.bot/reference/templates/BOOTSTRAP)
- [Templates: IDENTITY](https://docs.molt.bot/reference/templates/IDENTITY)
- [Templates: SOUL](https://docs.molt.bot/reference/templates/SOUL)
- [Templates: TOOLS](https://docs.molt.bot/reference/templates/TOOLS)
- [Templates: USER](https://docs.molt.bot/reference/templates/USER)
- [Skills config](https://docs.openclaw.ai/tools/skills-config)
- [Default AGENTS](https://docs.openclaw.ai/reference/AGENTS.default)
- [Templates: AGENTS](https://docs.openclaw.ai/reference/templates/AGENTS)
- [Templates: BOOTSTRAP](https://docs.openclaw.ai/reference/templates/BOOTSTRAP)
- [Templates: IDENTITY](https://docs.openclaw.ai/reference/templates/IDENTITY)
- [Templates: SOUL](https://docs.openclaw.ai/reference/templates/SOUL)
- [Templates: TOOLS](https://docs.openclaw.ai/reference/templates/TOOLS)
- [Templates: USER](https://docs.openclaw.ai/reference/templates/USER)
## Platform internals
- [macOS dev setup](https://docs.molt.bot/platforms/mac/dev-setup)
- [macOS menu bar](https://docs.molt.bot/platforms/mac/menu-bar)
- [macOS voice wake](https://docs.molt.bot/platforms/mac/voicewake)
- [iOS node](https://docs.molt.bot/platforms/ios)
- [Android node](https://docs.molt.bot/platforms/android)
- [Windows (WSL2)](https://docs.molt.bot/platforms/windows)
- [Linux app](https://docs.molt.bot/platforms/linux)
- [macOS dev setup](https://docs.openclaw.ai/platforms/mac/dev-setup)
- [macOS menu bar](https://docs.openclaw.ai/platforms/mac/menu-bar)
- [macOS voice wake](https://docs.openclaw.ai/platforms/mac/voicewake)
- [iOS node](https://docs.openclaw.ai/platforms/ios)
- [Android node](https://docs.openclaw.ai/platforms/android)
- [Windows (WSL2)](https://docs.openclaw.ai/platforms/windows)
- [Linux app](https://docs.openclaw.ai/platforms/linux)
## Email hooks (Gmail)
- [docs.molt.bot/gmail-pubsub](https://docs.molt.bot/automation/gmail-pubsub)
- [docs.openclaw.ai/gmail-pubsub](https://docs.openclaw.ai/automation/gmail-pubsub)
## Molty
Moltbot was built for **Molty**, a space lobster AI assistant. 🦞
OpenClaw was built for **Molty**, a space lobster AI assistant. 🦞
by Peter Steinberger and the community.
- [clawd.me](https://clawd.me)
- [openclaw.ai](https://openclaw.ai)
- [soul.md](https://soul.md)
- [steipete.me](https://steipete.me)
- [@moltbot](https://x.com/moltbot)
- [@openclaw](https://x.com/openclaw)
## Community
@@ -474,41 +492,58 @@ AI/vibe-coded PRs welcome! 🤖
Special thanks to [Mario Zechner](https://mariozechner.at/) for his support and for
[pi-mono](https://github.com/badlogic/pi-mono).
Special thanks to Adam Doppelt for lobster.bot.
Thanks to all clawtributors:
<p align="left">
<a href="https://github.com/steipete"><img src="https://avatars.githubusercontent.com/u/58493?v=4&s=48" width="48" height="48" alt="steipete" title="steipete"/></a> <a href="https://github.com/plum-dawg"><img src="https://avatars.githubusercontent.com/u/5909950?v=4&s=48" width="48" height="48" alt="plum-dawg" title="plum-dawg"/></a> <a href="https://github.com/bohdanpodvirnyi"><img src="https://avatars.githubusercontent.com/u/31819391?v=4&s=48" width="48" height="48" alt="bohdanpodvirnyi" title="bohdanpodvirnyi"/></a> <a href="https://github.com/iHildy"><img src="https://avatars.githubusercontent.com/u/25069719?v=4&s=48" width="48" height="48" alt="iHildy" title="iHildy"/></a> <a href="https://github.com/jaydenfyi"><img src="https://avatars.githubusercontent.com/u/213395523?v=4&s=48" width="48" height="48" alt="jaydenfyi" title="jaydenfyi"/></a> <a href="https://github.com/joaohlisboa"><img src="https://avatars.githubusercontent.com/u/8200873?v=4&s=48" width="48" height="48" alt="joaohlisboa" title="joaohlisboa"/></a> <a href="https://github.com/mneves75"><img src="https://avatars.githubusercontent.com/u/2423436?v=4&s=48" width="48" height="48" alt="mneves75" title="mneves75"/></a> <a href="https://github.com/MatthieuBizien"><img src="https://avatars.githubusercontent.com/u/173090?v=4&s=48" width="48" height="48" alt="MatthieuBizien" title="MatthieuBizien"/></a> <a href="https://github.com/MaudeBot"><img src="https://avatars.githubusercontent.com/u/255777700?v=4&s=48" width="48" height="48" alt="MaudeBot" title="MaudeBot"/></a> <a href="https://github.com/Glucksberg"><img src="https://avatars.githubusercontent.com/u/80581902?v=4&s=48" width="48" height="48" alt="Glucksberg" title="Glucksberg"/></a>
<a href="https://github.com/rahthakor"><img src="https://avatars.githubusercontent.com/u/8470553?v=4&s=48" width="48" height="48" alt="rahthakor" title="rahthakor"/></a> <a href="https://github.com/vrknetha"><img src="https://avatars.githubusercontent.com/u/20596261?v=4&s=48" width="48" height="48" alt="vrknetha" title="vrknetha"/></a> <a href="https://github.com/radek-paclt"><img src="https://avatars.githubusercontent.com/u/50451445?v=4&s=48" width="48" height="48" alt="radek-paclt" title="radek-paclt"/></a> <a href="https://github.com/tobiasbischoff"><img src="https://avatars.githubusercontent.com/u/711564?v=4&s=48" width="48" height="48" alt="Tobias Bischoff" title="Tobias Bischoff"/></a> <a href="https://github.com/joshp123"><img src="https://avatars.githubusercontent.com/u/1497361?v=4&s=48" width="48" height="48" alt="joshp123" title="joshp123"/></a> <a href="https://github.com/vignesh07"><img src="https://avatars.githubusercontent.com/u/1436853?v=4&s=48" width="48" height="48" alt="vignesh07" title="vignesh07"/></a> <a href="https://github.com/czekaj"><img src="https://avatars.githubusercontent.com/u/1464539?v=4&s=48" width="48" height="48" alt="czekaj" title="czekaj"/></a> <a href="https://github.com/mukhtharcm"><img src="https://avatars.githubusercontent.com/u/56378562?v=4&s=48" width="48" height="48" alt="mukhtharcm" title="mukhtharcm"/></a> <a href="https://github.com/sebslight"><img src="https://avatars.githubusercontent.com/u/19554889?v=4&s=48" width="48" height="48" alt="sebslight" title="sebslight"/></a> <a href="https://github.com/maxsumrall"><img src="https://avatars.githubusercontent.com/u/628843?v=4&s=48" width="48" height="48" alt="maxsumrall" title="maxsumrall"/></a>
<a href="https://github.com/xadenryan"><img src="https://avatars.githubusercontent.com/u/165437834?v=4&s=48" width="48" height="48" alt="xadenryan" title="xadenryan"/></a> <a href="https://github.com/rodrigouroz"><img src="https://avatars.githubusercontent.com/u/384037?v=4&s=48" width="48" height="48" alt="rodrigouroz" title="rodrigouroz"/></a> <a href="https://github.com/juanpablodlc"><img src="https://avatars.githubusercontent.com/u/92012363?v=4&s=48" width="48" height="48" alt="juanpablodlc" title="juanpablodlc"/></a> <a href="https://github.com/hsrvc"><img src="https://avatars.githubusercontent.com/u/129702169?v=4&s=48" width="48" height="48" alt="hsrvc" title="hsrvc"/></a> <a href="https://github.com/magimetal"><img src="https://avatars.githubusercontent.com/u/36491250?v=4&s=48" width="48" height="48" alt="magimetal" title="magimetal"/></a> <a href="https://github.com/zerone0x"><img src="https://avatars.githubusercontent.com/u/39543393?v=4&s=48" width="48" height="48" alt="zerone0x" title="zerone0x"/></a> <a href="https://github.com/meaningfool"><img src="https://avatars.githubusercontent.com/u/2862331?v=4&s=48" width="48" height="48" alt="meaningfool" title="meaningfool"/></a> <a href="https://github.com/tyler6204"><img src="https://avatars.githubusercontent.com/u/64381258?v=4&s=48" width="48" height="48" alt="tyler6204" title="tyler6204"/></a> <a href="https://github.com/patelhiren"><img src="https://avatars.githubusercontent.com/u/172098?v=4&s=48" width="48" height="48" alt="patelhiren" title="patelhiren"/></a> <a href="https://github.com/NicholasSpisak"><img src="https://avatars.githubusercontent.com/u/129075147?v=4&s=48" width="48" height="48" alt="NicholasSpisak" title="NicholasSpisak"/></a>
<a href="https://github.com/jonisjongithub"><img src="https://avatars.githubusercontent.com/u/86072337?v=4&s=48" width="48" height="48" alt="jonisjongithub" title="jonisjongithub"/></a> <a href="https://github.com/AbhisekBasu1"><img src="https://avatars.githubusercontent.com/u/40645221?v=4&s=48" width="48" height="48" alt="abhisekbasu1" title="abhisekbasu1"/></a> <a href="https://github.com/jamesgroat"><img src="https://avatars.githubusercontent.com/u/2634024?v=4&s=48" width="48" height="48" alt="jamesgroat" title="jamesgroat"/></a> <a href="https://github.com/claude"><img src="https://avatars.githubusercontent.com/u/81847?v=4&s=48" width="48" height="48" alt="claude" title="claude"/></a> <a href="https://github.com/JustYannicc"><img src="https://avatars.githubusercontent.com/u/52761674?v=4&s=48" width="48" height="48" alt="JustYannicc" title="JustYannicc"/></a> <a href="https://github.com/Hyaxia"><img src="https://avatars.githubusercontent.com/u/36747317?v=4&s=48" width="48" height="48" alt="Hyaxia" title="Hyaxia"/></a> <a href="https://github.com/dantelex"><img src="https://avatars.githubusercontent.com/u/631543?v=4&s=48" width="48" height="48" alt="dantelex" title="dantelex"/></a> <a href="https://github.com/SocialNerd42069"><img src="https://avatars.githubusercontent.com/u/118244303?v=4&s=48" width="48" height="48" alt="SocialNerd42069" title="SocialNerd42069"/></a> <a href="https://github.com/daveonkels"><img src="https://avatars.githubusercontent.com/u/533642?v=4&s=48" width="48" height="48" alt="daveonkels" title="daveonkels"/></a> <a href="https://github.com/apps/google-labs-jules"><img src="https://avatars.githubusercontent.com/in/842251?v=4&s=48" width="48" height="48" alt="google-labs-jules[bot]" title="google-labs-jules[bot]"/></a>
<a href="https://github.com/lc0rp"><img src="https://avatars.githubusercontent.com/u/2609441?v=4&s=48" width="48" height="48" alt="lc0rp" title="lc0rp"/></a> <a href="https://github.com/mousberg"><img src="https://avatars.githubusercontent.com/u/57605064?v=4&s=48" width="48" height="48" alt="mousberg" title="mousberg"/></a> <a href="https://github.com/adam91holt"><img src="https://avatars.githubusercontent.com/u/9592417?v=4&s=48" width="48" height="48" alt="adam91holt" title="adam91holt"/></a> <a href="https://github.com/hougangdev"><img src="https://avatars.githubusercontent.com/u/105773686?v=4&s=48" width="48" height="48" alt="hougangdev" title="hougangdev"/></a> <a href="https://github.com/gumadeiras"><img src="https://avatars.githubusercontent.com/u/5599352?v=4&s=48" width="48" height="48" alt="gumadeiras" title="gumadeiras"/></a> <a href="https://github.com/mteam88"><img src="https://avatars.githubusercontent.com/u/84196639?v=4&s=48" width="48" height="48" alt="mteam88" title="mteam88"/></a> <a href="https://github.com/hirefrank"><img src="https://avatars.githubusercontent.com/u/183158?v=4&s=48" width="48" height="48" alt="hirefrank" title="hirefrank"/></a> <a href="https://github.com/joeynyc"><img src="https://avatars.githubusercontent.com/u/17919866?v=4&s=48" width="48" height="48" alt="joeynyc" title="joeynyc"/></a> <a href="https://github.com/orlyjamie"><img src="https://avatars.githubusercontent.com/u/6668807?v=4&s=48" width="48" height="48" alt="orlyjamie" title="orlyjamie"/></a> <a href="https://github.com/dbhurley"><img src="https://avatars.githubusercontent.com/u/5251425?v=4&s=48" width="48" height="48" alt="dbhurley" title="dbhurley"/></a>
<a href="https://github.com/mbelinky"><img src="https://avatars.githubusercontent.com/u/132747814?v=4&s=48" width="48" height="48" alt="Mariano Belinky" title="Mariano Belinky"/></a> <a href="https://github.com/omniwired"><img src="https://avatars.githubusercontent.com/u/322761?v=4&s=48" width="48" height="48" alt="Eng. Juan Combetto" title="Eng. Juan Combetto"/></a> <a href="https://github.com/TSavo"><img src="https://avatars.githubusercontent.com/u/877990?v=4&s=48" width="48" height="48" alt="TSavo" title="TSavo"/></a> <a href="https://github.com/julianengel"><img src="https://avatars.githubusercontent.com/u/10634231?v=4&s=48" width="48" height="48" alt="julianengel" title="julianengel"/></a> <a href="https://github.com/bradleypriest"><img src="https://avatars.githubusercontent.com/u/167215?v=4&s=48" width="48" height="48" alt="bradleypriest" title="bradleypriest"/></a> <a href="https://github.com/benithors"><img src="https://avatars.githubusercontent.com/u/20652882?v=4&s=48" width="48" height="48" alt="benithors" title="benithors"/></a> <a href="https://github.com/rohannagpal"><img src="https://avatars.githubusercontent.com/u/4009239?v=4&s=48" width="48" height="48" alt="rohannagpal" title="rohannagpal"/></a> <a href="https://github.com/timolins"><img src="https://avatars.githubusercontent.com/u/1440854?v=4&s=48" width="48" height="48" alt="timolins" title="timolins"/></a> <a href="https://github.com/f-trycua"><img src="https://avatars.githubusercontent.com/u/195596869?v=4&s=48" width="48" height="48" alt="f-trycua" title="f-trycua"/></a> <a href="https://github.com/benostein"><img src="https://avatars.githubusercontent.com/u/31802821?v=4&s=48" width="48" height="48" alt="benostein" title="benostein"/></a>
<a href="https://github.com/Nachx639"><img src="https://avatars.githubusercontent.com/u/71144023?v=4&s=48" width="48" height="48" alt="nachx639" title="nachx639"/></a> <a href="https://github.com/shakkernerd"><img src="https://avatars.githubusercontent.com/u/165377636?v=4&s=48" width="48" height="48" alt="shakkernerd" title="shakkernerd"/></a> <a href="https://github.com/pvoo"><img src="https://avatars.githubusercontent.com/u/20116814?v=4&s=48" width="48" height="48" alt="pvoo" title="pvoo"/></a> <a href="https://github.com/sreekaransrinath"><img src="https://avatars.githubusercontent.com/u/50989977?v=4&s=48" width="48" height="48" alt="sreekaransrinath" title="sreekaransrinath"/></a> <a href="https://github.com/gupsammy"><img src="https://avatars.githubusercontent.com/u/20296019?v=4&s=48" width="48" height="48" alt="gupsammy" title="gupsammy"/></a> <a href="https://github.com/cristip73"><img src="https://avatars.githubusercontent.com/u/24499421?v=4&s=48" width="48" height="48" alt="cristip73" title="cristip73"/></a> <a href="https://github.com/stefangalescu"><img src="https://avatars.githubusercontent.com/u/52995748?v=4&s=48" width="48" height="48" alt="stefangalescu" title="stefangalescu"/></a> <a href="https://github.com/nachoiacovino"><img src="https://avatars.githubusercontent.com/u/50103937?v=4&s=48" width="48" height="48" alt="nachoiacovino" title="nachoiacovino"/></a> <a href="https://github.com/vsabavat"><img src="https://avatars.githubusercontent.com/u/50385532?v=4&s=48" width="48" height="48" alt="Vasanth Rao Naik Sabavat" title="Vasanth Rao Naik Sabavat"/></a> <a href="https://github.com/petter-b"><img src="https://avatars.githubusercontent.com/u/62076402?v=4&s=48" width="48" height="48" alt="petter-b" title="petter-b"/></a>
<a href="https://github.com/cpojer"><img src="https://avatars.githubusercontent.com/u/13352?v=4&s=48" width="48" height="48" alt="cpojer" title="cpojer"/></a> <a href="https://github.com/scald"><img src="https://avatars.githubusercontent.com/u/1215913?v=4&s=48" width="48" height="48" alt="scald" title="scald"/></a> <a href="https://github.com/thewilloftheshadow"><img src="https://avatars.githubusercontent.com/u/35580099?v=4&s=48" width="48" height="48" alt="thewilloftheshadow" title="thewilloftheshadow"/></a> <a href="https://github.com/andranik-sahakyan"><img src="https://avatars.githubusercontent.com/u/8908029?v=4&s=48" width="48" height="48" alt="andranik-sahakyan" title="andranik-sahakyan"/></a> <a href="https://github.com/davidguttman"><img src="https://avatars.githubusercontent.com/u/431696?v=4&s=48" width="48" height="48" alt="davidguttman" title="davidguttman"/></a> <a href="https://github.com/sleontenko"><img src="https://avatars.githubusercontent.com/u/7135949?v=4&s=48" width="48" height="48" alt="sleontenko" title="sleontenko"/></a> <a href="https://github.com/denysvitali"><img src="https://avatars.githubusercontent.com/u/4939519?v=4&s=48" width="48" height="48" alt="denysvitali" title="denysvitali"/></a> <a href="https://github.com/sircrumpet"><img src="https://avatars.githubusercontent.com/u/4436535?v=4&s=48" width="48" height="48" alt="sircrumpet" title="sircrumpet"/></a> <a href="https://github.com/peschee"><img src="https://avatars.githubusercontent.com/u/63866?v=4&s=48" width="48" height="48" alt="peschee" title="peschee"/></a> <a href="https://github.com/rafaelreis-r"><img src="https://avatars.githubusercontent.com/u/57492577?v=4&s=48" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a>
<a href="https://github.com/dominicnunez"><img src="https://avatars.githubusercontent.com/u/43616264?v=4&s=48" width="48" height="48" alt="dominicnunez" title="dominicnunez"/></a> <a href="https://github.com/ratulsarna"><img src="https://avatars.githubusercontent.com/u/105903728?v=4&s=48" width="48" height="48" alt="ratulsarna" title="ratulsarna"/></a> <a href="https://github.com/lutr0"><img src="https://avatars.githubusercontent.com/u/76906369?v=4&s=48" width="48" height="48" alt="lutr0" title="lutr0"/></a> <a href="https://github.com/danielz1z"><img src="https://avatars.githubusercontent.com/u/235270390?v=4&s=48" width="48" height="48" alt="danielz1z" title="danielz1z"/></a> <a href="https://github.com/AdeboyeDN"><img src="https://avatars.githubusercontent.com/u/65312338?v=4&s=48" width="48" height="48" alt="AdeboyeDN" title="AdeboyeDN"/></a> <a href="https://github.com/Alg0rix"><img src="https://avatars.githubusercontent.com/u/53804949?v=4&s=48" width="48" height="48" alt="Alg0rix" title="Alg0rix"/></a> <a href="https://github.com/papago2355"><img src="https://avatars.githubusercontent.com/u/68721273?v=4&s=48" width="48" height="48" alt="papago2355" title="papago2355"/></a> <a href="https://github.com/emanuelst"><img src="https://avatars.githubusercontent.com/u/9994339?v=4&s=48" width="48" height="48" alt="emanuelst" title="emanuelst"/></a> <a href="https://github.com/KristijanJovanovski"><img src="https://avatars.githubusercontent.com/u/8942284?v=4&s=48" width="48" height="48" alt="KristijanJovanovski" title="KristijanJovanovski"/></a> <a href="https://github.com/rdev"><img src="https://avatars.githubusercontent.com/u/8418866?v=4&s=48" width="48" height="48" alt="rdev" title="rdev"/></a>
<a href="https://github.com/rhuanssauro"><img src="https://avatars.githubusercontent.com/u/164682191?v=4&s=48" width="48" height="48" alt="rhuanssauro" title="rhuanssauro"/></a> <a href="https://github.com/joshrad-dev"><img src="https://avatars.githubusercontent.com/u/62785552?v=4&s=48" width="48" height="48" alt="joshrad-dev" title="joshrad-dev"/></a> <a href="https://github.com/kiranjd"><img src="https://avatars.githubusercontent.com/u/25822851?v=4&s=48" width="48" height="48" alt="kiranjd" title="kiranjd"/></a> <a href="https://github.com/osolmaz"><img src="https://avatars.githubusercontent.com/u/2453968?v=4&s=48" width="48" height="48" alt="osolmaz" title="osolmaz"/></a> <a href="https://github.com/adityashaw2"><img src="https://avatars.githubusercontent.com/u/41204444?v=4&s=48" width="48" height="48" alt="adityashaw2" title="adityashaw2"/></a> <a href="https://github.com/CashWilliams"><img src="https://avatars.githubusercontent.com/u/613573?v=4&s=48" width="48" height="48" alt="CashWilliams" title="CashWilliams"/></a> <a href="https://github.com/search?q=sheeek"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="sheeek" title="sheeek"/></a> <a href="https://github.com/artuskg"><img src="https://avatars.githubusercontent.com/u/11966157?v=4&s=48" width="48" height="48" alt="artuskg" title="artuskg"/></a> <a href="https://github.com/Takhoffman"><img src="https://avatars.githubusercontent.com/u/781889?v=4&s=48" width="48" height="48" alt="Takhoffman" title="Takhoffman"/></a> <a href="https://github.com/onutc"><img src="https://avatars.githubusercontent.com/u/152018508?v=4&s=48" width="48" height="48" alt="onutc" title="onutc"/></a>
<a href="https://github.com/pauloportella"><img src="https://avatars.githubusercontent.com/u/22947229?v=4&s=48" width="48" height="48" alt="pauloportella" title="pauloportella"/></a> <a href="https://github.com/neooriginal"><img src="https://avatars.githubusercontent.com/u/54811660?v=4&s=48" width="48" height="48" alt="neooriginal" title="neooriginal"/></a> <a href="https://github.com/ManuelHettich"><img src="https://avatars.githubusercontent.com/u/17690367?v=4&s=48" width="48" height="48" alt="manuelhettich" title="manuelhettich"/></a> <a href="https://github.com/minghinmatthewlam"><img src="https://avatars.githubusercontent.com/u/14224566?v=4&s=48" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/myfunc"><img src="https://avatars.githubusercontent.com/u/19294627?v=4&s=48" width="48" height="48" alt="myfunc" title="myfunc"/></a> <a href="https://github.com/travisirby"><img src="https://avatars.githubusercontent.com/u/5958376?v=4&s=48" width="48" height="48" alt="travisirby" title="travisirby"/></a> <a href="https://github.com/buddyh"><img src="https://avatars.githubusercontent.com/u/31752869?v=4&s=48" width="48" height="48" alt="buddyh" title="buddyh"/></a> <a href="https://github.com/connorshea"><img src="https://avatars.githubusercontent.com/u/2977353?v=4&s=48" width="48" height="48" alt="connorshea" title="connorshea"/></a> <a href="https://github.com/kyleok"><img src="https://avatars.githubusercontent.com/u/58307870?v=4&s=48" width="48" height="48" alt="kyleok" title="kyleok"/></a> <a href="https://github.com/obviyus"><img src="https://avatars.githubusercontent.com/u/22031114?v=4&s=48" width="48" height="48" alt="obviyus" title="obviyus"/></a>
<a href="https://github.com/mcinteerj"><img src="https://avatars.githubusercontent.com/u/3613653?v=4&s=48" width="48" height="48" alt="mcinteerj" title="mcinteerj"/></a> <a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4&s=48" width="48" height="48" alt="dependabot[bot]" title="dependabot[bot]"/></a> <a href="https://github.com/John-Rood"><img src="https://avatars.githubusercontent.com/u/62669593?v=4&s=48" width="48" height="48" alt="John-Rood" title="John-Rood"/></a> <a href="https://github.com/timkrase"><img src="https://avatars.githubusercontent.com/u/38947626?v=4&s=48" width="48" height="48" alt="timkrase" title="timkrase"/></a> <a href="https://github.com/uos-status"><img src="https://avatars.githubusercontent.com/u/255712580?v=4&s=48" width="48" height="48" alt="uos-status" title="uos-status"/></a> <a href="https://github.com/gerardward2007"><img src="https://avatars.githubusercontent.com/u/3002155?v=4&s=48" width="48" height="48" alt="gerardward2007" title="gerardward2007"/></a> <a href="https://github.com/roshanasingh4"><img src="https://avatars.githubusercontent.com/u/88576930?v=4&s=48" width="48" height="48" alt="roshanasingh4" title="roshanasingh4"/></a> <a href="https://github.com/tosh-hamburg"><img src="https://avatars.githubusercontent.com/u/58424326?v=4&s=48" width="48" height="48" alt="tosh-hamburg" title="tosh-hamburg"/></a> <a href="https://github.com/azade-c"><img src="https://avatars.githubusercontent.com/u/252790079?v=4&s=48" width="48" height="48" alt="azade-c" title="azade-c"/></a> <a href="https://github.com/dlauer"><img src="https://avatars.githubusercontent.com/u/757041?v=4&s=48" width="48" height="48" alt="dlauer" title="dlauer"/></a>
<a href="https://github.com/JonUleis"><img src="https://avatars.githubusercontent.com/u/7644941?v=4&s=48" width="48" height="48" alt="JonUleis" title="JonUleis"/></a> <a href="https://github.com/bjesuiter"><img src="https://avatars.githubusercontent.com/u/2365676?v=4&s=48" width="48" height="48" alt="bjesuiter" title="bjesuiter"/></a> <a href="https://github.com/cheeeee"><img src="https://avatars.githubusercontent.com/u/21245729?v=4&s=48" width="48" height="48" alt="cheeeee" title="cheeeee"/></a> <a href="https://github.com/robbyczgw-cla"><img src="https://avatars.githubusercontent.com/u/239660374?v=4&s=48" width="48" height="48" alt="robbyczgw-cla" title="robbyczgw-cla"/></a> <a href="https://github.com/j1philli"><img src="https://avatars.githubusercontent.com/u/3744255?v=4&s=48" width="48" height="48" alt="Josh Phillips" title="Josh Phillips"/></a> <a href="https://github.com/YuriNachos"><img src="https://avatars.githubusercontent.com/u/19365375?v=4&s=48" width="48" height="48" alt="YuriNachos" title="YuriNachos"/></a> <a href="https://github.com/pookNast"><img src="https://avatars.githubusercontent.com/u/14242552?v=4&s=48" width="48" height="48" alt="pookNast" title="pookNast"/></a> <a href="https://github.com/Whoaa512"><img src="https://avatars.githubusercontent.com/u/1581943?v=4&s=48" width="48" height="48" alt="Whoaa512" title="Whoaa512"/></a> <a href="https://github.com/chriseidhof"><img src="https://avatars.githubusercontent.com/u/5382?v=4&s=48" width="48" height="48" alt="chriseidhof" title="chriseidhof"/></a> <a href="https://github.com/ngutman"><img src="https://avatars.githubusercontent.com/u/1540134?v=4&s=48" width="48" height="48" alt="ngutman" title="ngutman"/></a>
<a href="https://github.com/ysqander"><img src="https://avatars.githubusercontent.com/u/80843820?v=4&s=48" width="48" height="48" alt="ysqander" title="ysqander"/></a> <a href="https://github.com/aj47"><img src="https://avatars.githubusercontent.com/u/8023513?v=4&s=48" width="48" height="48" alt="aj47" title="aj47"/></a> <a href="https://github.com/kennyklee"><img src="https://avatars.githubusercontent.com/u/1432489?v=4&s=48" width="48" height="48" alt="kennyklee" title="kennyklee"/></a> <a href="https://github.com/superman32432432"><img src="https://avatars.githubusercontent.com/u/7228420?v=4&s=48" width="48" height="48" alt="superman32432432" title="superman32432432"/></a> <a href="https://github.com/search?q=Yurii%20Chukhlib"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Yurii Chukhlib" title="Yurii Chukhlib"/></a> <a href="https://github.com/grp06"><img src="https://avatars.githubusercontent.com/u/1573959?v=4&s=48" width="48" height="48" alt="grp06" title="grp06"/></a> <a href="https://github.com/antons"><img src="https://avatars.githubusercontent.com/u/129705?v=4&s=48" width="48" height="48" alt="antons" title="antons"/></a> <a href="https://github.com/austinm911"><img src="https://avatars.githubusercontent.com/u/31991302?v=4&s=48" width="48" height="48" alt="austinm911" title="austinm911"/></a> <a href="https://github.com/apps/blacksmith-sh"><img src="https://avatars.githubusercontent.com/in/807020?v=4&s=48" width="48" height="48" alt="blacksmith-sh[bot]" title="blacksmith-sh[bot]"/></a> <a href="https://github.com/damoahdominic"><img src="https://avatars.githubusercontent.com/u/4623434?v=4&s=48" width="48" height="48" alt="damoahdominic" title="damoahdominic"/></a>
<a href="https://github.com/dan-dr"><img src="https://avatars.githubusercontent.com/u/6669808?v=4&s=48" width="48" height="48" alt="dan-dr" title="dan-dr"/></a> <a href="https://github.com/HeimdallStrategy"><img src="https://avatars.githubusercontent.com/u/223014405?v=4&s=48" width="48" height="48" alt="HeimdallStrategy" title="HeimdallStrategy"/></a> <a href="https://github.com/imfing"><img src="https://avatars.githubusercontent.com/u/5097752?v=4&s=48" width="48" height="48" alt="imfing" title="imfing"/></a> <a href="https://github.com/jalehman"><img src="https://avatars.githubusercontent.com/u/550978?v=4&s=48" width="48" height="48" alt="jalehman" title="jalehman"/></a> <a href="https://github.com/jarvis-medmatic"><img src="https://avatars.githubusercontent.com/u/252428873?v=4&s=48" width="48" height="48" alt="jarvis-medmatic" title="jarvis-medmatic"/></a> <a href="https://github.com/kkarimi"><img src="https://avatars.githubusercontent.com/u/875218?v=4&s=48" width="48" height="48" alt="kkarimi" title="kkarimi"/></a> <a href="https://github.com/mahmoudashraf93"><img src="https://avatars.githubusercontent.com/u/9130129?v=4&s=48" width="48" height="48" alt="mahmoudashraf93" title="mahmoudashraf93"/></a> <a href="https://github.com/pkrmf"><img src="https://avatars.githubusercontent.com/u/1714267?v=4&s=48" width="48" height="48" alt="pkrmf" title="pkrmf"/></a> <a href="https://github.com/RandyVentures"><img src="https://avatars.githubusercontent.com/u/149904821?v=4&s=48" width="48" height="48" alt="RandyVentures" title="RandyVentures"/></a> <a href="https://github.com/search?q=Ryan%20Lisse"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ryan Lisse" title="Ryan Lisse"/></a>
<a href="https://github.com/dougvk"><img src="https://avatars.githubusercontent.com/u/401660?v=4&s=48" width="48" height="48" alt="dougvk" title="dougvk"/></a> <a href="https://github.com/erikpr1994"><img src="https://avatars.githubusercontent.com/u/6299331?v=4&s=48" width="48" height="48" alt="erikpr1994" title="erikpr1994"/></a> <a href="https://github.com/fal3"><img src="https://avatars.githubusercontent.com/u/6484295?v=4&s=48" width="48" height="48" alt="fal3" title="fal3"/></a> <a href="https://github.com/search?q=Ghost"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ghost" title="Ghost"/></a> <a href="https://github.com/jonasjancarik"><img src="https://avatars.githubusercontent.com/u/2459191?v=4&s=48" width="48" height="48" alt="jonasjancarik" title="jonasjancarik"/></a> <a href="https://github.com/search?q=Keith%20the%20Silly%20Goose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Keith the Silly Goose" title="Keith the Silly Goose"/></a> <a href="https://github.com/search?q=L36%20Server"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="L36 Server" title="L36 Server"/></a> <a href="https://github.com/search?q=Marc"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Marc" title="Marc"/></a> <a href="https://github.com/mitschabaude-bot"><img src="https://avatars.githubusercontent.com/u/247582884?v=4&s=48" width="48" height="48" alt="mitschabaude-bot" title="mitschabaude-bot"/></a> <a href="https://github.com/mkbehr"><img src="https://avatars.githubusercontent.com/u/1285?v=4&s=48" width="48" height="48" alt="mkbehr" title="mkbehr"/></a>
<a href="https://github.com/neist"><img src="https://avatars.githubusercontent.com/u/1029724?v=4&s=48" width="48" height="48" alt="neist" title="neist"/></a> <a href="https://github.com/sibbl"><img src="https://avatars.githubusercontent.com/u/866535?v=4&s=48" width="48" height="48" alt="sibbl" title="sibbl"/></a> <a href="https://github.com/chrisrodz"><img src="https://avatars.githubusercontent.com/u/2967620?v=4&s=48" width="48" height="48" alt="chrisrodz" title="chrisrodz"/></a> <a href="https://github.com/search?q=Friederike%20Seiler"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Friederike Seiler" title="Friederike Seiler"/></a> <a href="https://github.com/gabriel-trigo"><img src="https://avatars.githubusercontent.com/u/38991125?v=4&s=48" width="48" height="48" alt="gabriel-trigo" title="gabriel-trigo"/></a> <a href="https://github.com/Iamadig"><img src="https://avatars.githubusercontent.com/u/102129234?v=4&s=48" width="48" height="48" alt="iamadig" title="iamadig"/></a> <a href="https://github.com/jdrhyne"><img src="https://avatars.githubusercontent.com/u/7828464?v=4&s=48" width="48" height="48" alt="Jonathan D. Rhyne (DJ-D)" title="Jonathan D. Rhyne (DJ-D)"/></a> <a href="https://github.com/search?q=Joshua%20Mitchell"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Joshua Mitchell" title="Joshua Mitchell"/></a> <a href="https://github.com/search?q=Kit"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kit" title="Kit"/></a> <a href="https://github.com/koala73"><img src="https://avatars.githubusercontent.com/u/996596?v=4&s=48" width="48" height="48" alt="koala73" title="koala73"/></a>
<a href="https://github.com/manmal"><img src="https://avatars.githubusercontent.com/u/142797?v=4&s=48" width="48" height="48" alt="manmal" title="manmal"/></a> <a href="https://github.com/ogulcancelik"><img src="https://avatars.githubusercontent.com/u/7064011?v=4&s=48" width="48" height="48" alt="ogulcancelik" title="ogulcancelik"/></a> <a href="https://github.com/pasogott"><img src="https://avatars.githubusercontent.com/u/23458152?v=4&s=48" width="48" height="48" alt="pasogott" title="pasogott"/></a> <a href="https://github.com/petradonka"><img src="https://avatars.githubusercontent.com/u/7353770?v=4&s=48" width="48" height="48" alt="petradonka" title="petradonka"/></a> <a href="https://github.com/rubyrunsstuff"><img src="https://avatars.githubusercontent.com/u/246602379?v=4&s=48" width="48" height="48" alt="rubyrunsstuff" title="rubyrunsstuff"/></a> <a href="https://github.com/siddhantjain"><img src="https://avatars.githubusercontent.com/u/4835232?v=4&s=48" width="48" height="48" alt="siddhantjain" title="siddhantjain"/></a> <a href="https://github.com/suminhthanh"><img src="https://avatars.githubusercontent.com/u/2907636?v=4&s=48" width="48" height="48" alt="suminhthanh" title="suminhthanh"/></a> <a href="https://github.com/svkozak"><img src="https://avatars.githubusercontent.com/u/31941359?v=4&s=48" width="48" height="48" alt="svkozak" title="svkozak"/></a> <a href="https://github.com/VACInc"><img src="https://avatars.githubusercontent.com/u/3279061?v=4&s=48" width="48" height="48" alt="VACInc" title="VACInc"/></a> <a href="https://github.com/wes-davis"><img src="https://avatars.githubusercontent.com/u/16506720?v=4&s=48" width="48" height="48" alt="wes-davis" title="wes-davis"/></a>
<a href="https://github.com/zats"><img src="https://avatars.githubusercontent.com/u/2688806?v=4&s=48" width="48" height="48" alt="zats" title="zats"/></a> <a href="https://github.com/24601"><img src="https://avatars.githubusercontent.com/u/1157207?v=4&s=48" width="48" height="48" alt="24601" title="24601"/></a> <a href="https://github.com/ameno-"><img src="https://avatars.githubusercontent.com/u/2416135?v=4&s=48" width="48" height="48" alt="ameno-" title="ameno-"/></a> <a href="https://github.com/search?q=Chris%20Taylor"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Chris Taylor" title="Chris Taylor"/></a> <a href="https://github.com/dguido"><img src="https://avatars.githubusercontent.com/u/294844?v=4&s=48" width="48" height="48" alt="dguido" title="dguido"/></a> <a href="https://github.com/djangonavarro220"><img src="https://avatars.githubusercontent.com/u/251162586?v=4&s=48" width="48" height="48" alt="Django Navarro" title="Django Navarro"/></a> <a href="https://github.com/evalexpr"><img src="https://avatars.githubusercontent.com/u/23485511?v=4&s=48" width="48" height="48" alt="evalexpr" title="evalexpr"/></a> <a href="https://github.com/henrino3"><img src="https://avatars.githubusercontent.com/u/4260288?v=4&s=48" width="48" height="48" alt="henrino3" title="henrino3"/></a> <a href="https://github.com/humanwritten"><img src="https://avatars.githubusercontent.com/u/206531610?v=4&s=48" width="48" height="48" alt="humanwritten" title="humanwritten"/></a> <a href="https://github.com/larlyssa"><img src="https://avatars.githubusercontent.com/u/13128869?v=4&s=48" width="48" height="48" alt="larlyssa" title="larlyssa"/></a>
<a href="https://github.com/odysseus0"><img src="https://avatars.githubusercontent.com/u/8635094?v=4&s=48" width="48" height="48" alt="odysseus0" title="odysseus0"/></a> <a href="https://github.com/oswalpalash"><img src="https://avatars.githubusercontent.com/u/6431196?v=4&s=48" width="48" height="48" alt="oswalpalash" title="oswalpalash"/></a> <a href="https://github.com/pcty-nextgen-service-account"><img src="https://avatars.githubusercontent.com/u/112553441?v=4&s=48" width="48" height="48" alt="pcty-nextgen-service-account" title="pcty-nextgen-service-account"/></a> <a href="https://github.com/rmorse"><img src="https://avatars.githubusercontent.com/u/853547?v=4&s=48" width="48" height="48" alt="rmorse" title="rmorse"/></a> <a href="https://github.com/Syhids"><img src="https://avatars.githubusercontent.com/u/671202?v=4&s=48" width="48" height="48" alt="Syhids" title="Syhids"/></a> <a href="https://github.com/search?q=Aaron%20Konyer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Aaron Konyer" title="Aaron Konyer"/></a> <a href="https://github.com/aaronveklabs"><img src="https://avatars.githubusercontent.com/u/225997828?v=4&s=48" width="48" height="48" alt="aaronveklabs" title="aaronveklabs"/></a> <a href="https://github.com/andreabadesso"><img src="https://avatars.githubusercontent.com/u/3586068?v=4&s=48" width="48" height="48" alt="andreabadesso" title="andreabadesso"/></a> <a href="https://github.com/search?q=Andrii"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Andrii" title="Andrii"/></a> <a href="https://github.com/cash-echo-bot"><img src="https://avatars.githubusercontent.com/u/252747386?v=4&s=48" width="48" height="48" alt="cash-echo-bot" title="cash-echo-bot"/></a>
<a href="https://github.com/search?q=Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawd" title="Clawd"/></a> <a href="https://github.com/search?q=ClawdFx"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ClawdFx" title="ClawdFx"/></a> <a href="https://github.com/EnzeD"><img src="https://avatars.githubusercontent.com/u/9866900?v=4&s=48" width="48" height="48" alt="EnzeD" title="EnzeD"/></a> <a href="https://github.com/erik-agens"><img src="https://avatars.githubusercontent.com/u/80908960?v=4&s=48" width="48" height="48" alt="erik-agens" title="erik-agens"/></a> <a href="https://github.com/Evizero"><img src="https://avatars.githubusercontent.com/u/10854026?v=4&s=48" width="48" height="48" alt="Evizero" title="Evizero"/></a> <a href="https://github.com/fcatuhe"><img src="https://avatars.githubusercontent.com/u/17382215?v=4&s=48" width="48" height="48" alt="fcatuhe" title="fcatuhe"/></a> <a href="https://github.com/itsjaydesu"><img src="https://avatars.githubusercontent.com/u/220390?v=4&s=48" width="48" height="48" alt="itsjaydesu" title="itsjaydesu"/></a> <a href="https://github.com/ivancasco"><img src="https://avatars.githubusercontent.com/u/2452858?v=4&s=48" width="48" height="48" alt="ivancasco" title="ivancasco"/></a> <a href="https://github.com/ivanrvpereira"><img src="https://avatars.githubusercontent.com/u/183991?v=4&s=48" width="48" height="48" alt="ivanrvpereira" title="ivanrvpereira"/></a> <a href="https://github.com/jayhickey"><img src="https://avatars.githubusercontent.com/u/1676460?v=4&s=48" width="48" height="48" alt="jayhickey" title="jayhickey"/></a>
<a href="https://github.com/jeffersonwarrior"><img src="https://avatars.githubusercontent.com/u/89030989?v=4&s=48" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a> <a href="https://github.com/search?q=jeffersonwarrior"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a> <a href="https://github.com/jverdi"><img src="https://avatars.githubusercontent.com/u/345050?v=4&s=48" width="48" height="48" alt="jverdi" title="jverdi"/></a> <a href="https://github.com/longmaba"><img src="https://avatars.githubusercontent.com/u/9361500?v=4&s=48" width="48" height="48" alt="longmaba" title="longmaba"/></a> <a href="https://github.com/mickahouan"><img src="https://avatars.githubusercontent.com/u/31423109?v=4&s=48" width="48" height="48" alt="mickahouan" title="mickahouan"/></a> <a href="https://github.com/mjrussell"><img src="https://avatars.githubusercontent.com/u/1641895?v=4&s=48" width="48" height="48" alt="mjrussell" title="mjrussell"/></a> <a href="https://github.com/odnxe"><img src="https://avatars.githubusercontent.com/u/403141?v=4&s=48" width="48" height="48" alt="odnxe" title="odnxe"/></a> <a href="https://github.com/p6l-richard"><img src="https://avatars.githubusercontent.com/u/18185649?v=4&s=48" width="48" height="48" alt="p6l-richard" title="p6l-richard"/></a> <a href="https://github.com/philipp-spiess"><img src="https://avatars.githubusercontent.com/u/458591?v=4&s=48" width="48" height="48" alt="philipp-spiess" title="philipp-spiess"/></a> <a href="https://github.com/search?q=Pocket%20Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Pocket Clawd" title="Pocket Clawd"/></a>
<a href="https://github.com/robaxelsen"><img src="https://avatars.githubusercontent.com/u/13132899?v=4&s=48" width="48" height="48" alt="robaxelsen" title="robaxelsen"/></a> <a href="https://github.com/search?q=Sash%20Catanzarite"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Sash Catanzarite" title="Sash Catanzarite"/></a> <a href="https://github.com/T5-AndyML"><img src="https://avatars.githubusercontent.com/u/22801233?v=4&s=48" width="48" height="48" alt="T5-AndyML" title="T5-AndyML"/></a> <a href="https://github.com/travisp"><img src="https://avatars.githubusercontent.com/u/165698?v=4&s=48" width="48" height="48" alt="travisp" title="travisp"/></a> <a href="https://github.com/search?q=VAC"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="VAC" title="VAC"/></a> <a href="https://github.com/search?q=william%20arzt"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="william arzt" title="william arzt"/></a> <a href="https://github.com/zknicker"><img src="https://avatars.githubusercontent.com/u/1164085?v=4&s=48" width="48" height="48" alt="zknicker" title="zknicker"/></a> <a href="https://github.com/0oAstro"><img src="https://avatars.githubusercontent.com/u/79555780?v=4&s=48" width="48" height="48" alt="0oAstro" title="0oAstro"/></a> <a href="https://github.com/abhaymundhara"><img src="https://avatars.githubusercontent.com/u/62872231?v=4&s=48" width="48" height="48" alt="abhaymundhara" title="abhaymundhara"/></a> <a href="https://github.com/search?q=alejandro%20maza"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="alejandro maza" title="alejandro maza"/></a>
<a href="https://github.com/Alex-Alaniz"><img src="https://avatars.githubusercontent.com/u/88956822?v=4&s=48" width="48" height="48" alt="Alex-Alaniz" title="Alex-Alaniz"/></a> <a href="https://github.com/alexstyl"><img src="https://avatars.githubusercontent.com/u/1665273?v=4&s=48" width="48" height="48" alt="alexstyl" title="alexstyl"/></a> <a href="https://github.com/andrewting19"><img src="https://avatars.githubusercontent.com/u/10536704?v=4&s=48" width="48" height="48" alt="andrewting19" title="andrewting19"/></a> <a href="https://github.com/anpoirier"><img src="https://avatars.githubusercontent.com/u/1245729?v=4&s=48" width="48" height="48" alt="anpoirier" title="anpoirier"/></a> <a href="https://github.com/arthyn"><img src="https://avatars.githubusercontent.com/u/5466421?v=4&s=48" width="48" height="48" alt="arthyn" title="arthyn"/></a> <a href="https://github.com/Asleep123"><img src="https://avatars.githubusercontent.com/u/122379135?v=4&s=48" width="48" height="48" alt="Asleep123" title="Asleep123"/></a> <a href="https://github.com/bolismauro"><img src="https://avatars.githubusercontent.com/u/771999?v=4&s=48" width="48" height="48" alt="bolismauro" title="bolismauro"/></a> <a href="https://github.com/chenyuan99"><img src="https://avatars.githubusercontent.com/u/25518100?v=4&s=48" width="48" height="48" alt="chenyuan99" title="chenyuan99"/></a> <a href="https://github.com/search?q=Clawdbot%20Maintainers"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawdbot Maintainers" title="Clawdbot Maintainers"/></a> <a href="https://github.com/conhecendoia"><img src="https://avatars.githubusercontent.com/u/82890727?v=4&s=48" width="48" height="48" alt="conhecendoia" title="conhecendoia"/></a>
<a href="https://github.com/dasilva333"><img src="https://avatars.githubusercontent.com/u/947827?v=4&s=48" width="48" height="48" alt="dasilva333" title="dasilva333"/></a> <a href="https://github.com/David-Marsh-Photo"><img src="https://avatars.githubusercontent.com/u/228404527?v=4&s=48" width="48" height="48" alt="David-Marsh-Photo" title="David-Marsh-Photo"/></a> <a href="https://github.com/search?q=Developer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Developer" title="Developer"/></a> <a href="https://github.com/search?q=Dimitrios%20Ploutarchos"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Dimitrios Ploutarchos" title="Dimitrios Ploutarchos"/></a> <a href="https://github.com/search?q=Drake%20Thomsen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Drake Thomsen" title="Drake Thomsen"/></a> <a href="https://github.com/search?q=Felix%20Krause"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Felix Krause" title="Felix Krause"/></a> <a href="https://github.com/foeken"><img src="https://avatars.githubusercontent.com/u/13864?v=4&s=48" width="48" height="48" alt="foeken" title="foeken"/></a> <a href="https://github.com/search?q=ganghyun%20kim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ganghyun kim" title="ganghyun kim"/></a> <a href="https://github.com/grrowl"><img src="https://avatars.githubusercontent.com/u/907140?v=4&s=48" width="48" height="48" alt="grrowl" title="grrowl"/></a> <a href="https://github.com/gtsifrikas"><img src="https://avatars.githubusercontent.com/u/8904378?v=4&s=48" width="48" height="48" alt="gtsifrikas" title="gtsifrikas"/></a>
<a href="https://github.com/HazAT"><img src="https://avatars.githubusercontent.com/u/363802?v=4&s=48" width="48" height="48" alt="HazAT" title="HazAT"/></a> <a href="https://github.com/hrdwdmrbl"><img src="https://avatars.githubusercontent.com/u/554881?v=4&s=48" width="48" height="48" alt="hrdwdmrbl" title="hrdwdmrbl"/></a> <a href="https://github.com/hugobarauna"><img src="https://avatars.githubusercontent.com/u/2719?v=4&s=48" width="48" height="48" alt="hugobarauna" title="hugobarauna"/></a> <a href="https://github.com/search?q=Jamie%20Openshaw"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jamie Openshaw" title="Jamie Openshaw"/></a> <a href="https://github.com/search?q=Jane"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jane" title="Jane"/></a> <a href="https://github.com/search?q=Jarvis"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis" title="Jarvis"/></a> <a href="https://github.com/search?q=Jefferson%20Nunn"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jefferson Nunn" title="Jefferson Nunn"/></a> <a href="https://github.com/jogi47"><img src="https://avatars.githubusercontent.com/u/1710139?v=4&s=48" width="48" height="48" alt="jogi47" title="jogi47"/></a> <a href="https://github.com/kentaro"><img src="https://avatars.githubusercontent.com/u/3458?v=4&s=48" width="48" height="48" alt="kentaro" title="kentaro"/></a> <a href="https://github.com/search?q=Kevin%20Lin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kevin Lin" title="Kevin Lin"/></a>
<a href="https://github.com/kitze"><img src="https://avatars.githubusercontent.com/u/1160594?v=4&s=48" width="48" height="48" alt="kitze" title="kitze"/></a> <a href="https://github.com/Kiwitwitter"><img src="https://avatars.githubusercontent.com/u/25277769?v=4&s=48" width="48" height="48" alt="Kiwitwitter" title="Kiwitwitter"/></a> <a href="https://github.com/levifig"><img src="https://avatars.githubusercontent.com/u/1605?v=4&s=48" width="48" height="48" alt="levifig" title="levifig"/></a> <a href="https://github.com/search?q=Lloyd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Lloyd" title="Lloyd"/></a> <a href="https://github.com/longjos"><img src="https://avatars.githubusercontent.com/u/740160?v=4&s=48" width="48" height="48" alt="longjos" title="longjos"/></a> <a href="https://github.com/loukotal"><img src="https://avatars.githubusercontent.com/u/18210858?v=4&s=48" width="48" height="48" alt="loukotal" title="loukotal"/></a> <a href="https://github.com/louzhixian"><img src="https://avatars.githubusercontent.com/u/7994361?v=4&s=48" width="48" height="48" alt="louzhixian" title="louzhixian"/></a> <a href="https://github.com/martinpucik"><img src="https://avatars.githubusercontent.com/u/5503097?v=4&s=48" width="48" height="48" alt="martinpucik" title="martinpucik"/></a> <a href="https://github.com/search?q=Matt%20mini"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Matt mini" title="Matt mini"/></a> <a href="https://github.com/mertcicekci0"><img src="https://avatars.githubusercontent.com/u/179321902?v=4&s=48" width="48" height="48" alt="mertcicekci0" title="mertcicekci0"/></a>
<a href="https://github.com/search?q=Miles"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Miles" title="Miles"/></a> <a href="https://github.com/mrdbstn"><img src="https://avatars.githubusercontent.com/u/58957632?v=4&s=48" width="48" height="48" alt="mrdbstn" title="mrdbstn"/></a> <a href="https://github.com/MSch"><img src="https://avatars.githubusercontent.com/u/7475?v=4&s=48" width="48" height="48" alt="MSch" title="MSch"/></a> <a href="https://github.com/search?q=Mustafa%20Tag%20Eldeen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mustafa Tag Eldeen" title="Mustafa Tag Eldeen"/></a> <a href="https://github.com/ndraiman"><img src="https://avatars.githubusercontent.com/u/12609607?v=4&s=48" width="48" height="48" alt="ndraiman" title="ndraiman"/></a> <a href="https://github.com/nexty5870"><img src="https://avatars.githubusercontent.com/u/3869659?v=4&s=48" width="48" height="48" alt="nexty5870" title="nexty5870"/></a> <a href="https://github.com/Noctivoro"><img src="https://avatars.githubusercontent.com/u/183974570?v=4&s=48" width="48" height="48" alt="Noctivoro" title="Noctivoro"/></a> <a href="https://github.com/ppamment"><img src="https://avatars.githubusercontent.com/u/2122919?v=4&s=48" width="48" height="48" alt="ppamment" title="ppamment"/></a> <a href="https://github.com/prathamdby"><img src="https://avatars.githubusercontent.com/u/134331217?v=4&s=48" width="48" height="48" alt="prathamdby" title="prathamdby"/></a> <a href="https://github.com/ptn1411"><img src="https://avatars.githubusercontent.com/u/57529765?v=4&s=48" width="48" height="48" alt="ptn1411" title="ptn1411"/></a>
<a href="https://github.com/reeltimeapps"><img src="https://avatars.githubusercontent.com/u/637338?v=4&s=48" width="48" height="48" alt="reeltimeapps" title="reeltimeapps"/></a> <a href="https://github.com/RLTCmpe"><img src="https://avatars.githubusercontent.com/u/10762242?v=4&s=48" width="48" height="48" alt="RLTCmpe" title="RLTCmpe"/></a> <a href="https://github.com/search?q=Rolf%20Fredheim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rolf Fredheim" title="Rolf Fredheim"/></a> <a href="https://github.com/search?q=Rony%20Kelner"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rony Kelner" title="Rony Kelner"/></a> <a href="https://github.com/search?q=Samrat%20Jha"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Samrat Jha" title="Samrat Jha"/></a> <a href="https://github.com/senoldogann"><img src="https://avatars.githubusercontent.com/u/45736551?v=4&s=48" width="48" height="48" alt="senoldogann" title="senoldogann"/></a> <a href="https://github.com/Seredeep"><img src="https://avatars.githubusercontent.com/u/22802816?v=4&s=48" width="48" height="48" alt="Seredeep" title="Seredeep"/></a> <a href="https://github.com/sergical"><img src="https://avatars.githubusercontent.com/u/3760543?v=4&s=48" width="48" height="48" alt="sergical" title="sergical"/></a> <a href="https://github.com/shiv19"><img src="https://avatars.githubusercontent.com/u/9407019?v=4&s=48" width="48" height="48" alt="shiv19" title="shiv19"/></a> <a href="https://github.com/shiyuanhai"><img src="https://avatars.githubusercontent.com/u/1187370?v=4&s=48" width="48" height="48" alt="shiyuanhai" title="shiyuanhai"/></a>
<a href="https://github.com/siraht"><img src="https://avatars.githubusercontent.com/u/73152895?v=4&s=48" width="48" height="48" alt="siraht" title="siraht"/></a> <a href="https://github.com/snopoke"><img src="https://avatars.githubusercontent.com/u/249606?v=4&s=48" width="48" height="48" alt="snopoke" title="snopoke"/></a> <a href="https://github.com/Suksham-sharma"><img src="https://avatars.githubusercontent.com/u/94667656?v=4&s=48" width="48" height="48" alt="Suksham-sharma" title="Suksham-sharma"/></a> <a href="https://github.com/search?q=techboss"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="techboss" title="techboss"/></a> <a href="https://github.com/testingabc321"><img src="https://avatars.githubusercontent.com/u/8577388?v=4&s=48" width="48" height="48" alt="testingabc321" title="testingabc321"/></a> <a href="https://github.com/search?q=The%20Admiral"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="The Admiral" title="The Admiral"/></a> <a href="https://github.com/thesash"><img src="https://avatars.githubusercontent.com/u/1166151?v=4&s=48" width="48" height="48" alt="thesash" title="thesash"/></a> <a href="https://github.com/search?q=Ubuntu"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ubuntu" title="Ubuntu"/></a> <a href="https://github.com/voidserf"><img src="https://avatars.githubusercontent.com/u/477673?v=4&s=48" width="48" height="48" alt="voidserf" title="voidserf"/></a> <a href="https://github.com/search?q=Vultr-Clawd%20Admin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Vultr-Clawd Admin" title="Vultr-Clawd Admin"/></a>
<a href="https://github.com/search?q=Wimmie"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Wimmie" title="Wimmie"/></a> <a href="https://github.com/search?q=wolfred"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="wolfred" title="wolfred"/></a> <a href="https://github.com/wstock"><img src="https://avatars.githubusercontent.com/u/1394687?v=4&s=48" width="48" height="48" alt="wstock" title="wstock"/></a> <a href="https://github.com/yazinsai"><img src="https://avatars.githubusercontent.com/u/1846034?v=4&s=48" width="48" height="48" alt="yazinsai" title="yazinsai"/></a> <a href="https://github.com/YiWang24"><img src="https://avatars.githubusercontent.com/u/176262341?v=4&s=48" width="48" height="48" alt="YiWang24" title="YiWang24"/></a> <a href="https://github.com/search?q=ymat19"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ymat19" title="ymat19"/></a> <a href="https://github.com/search?q=Zach%20Knickerbocker"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Zach Knickerbocker" title="Zach Knickerbocker"/></a> <a href="https://github.com/0xJonHoldsCrypto"><img src="https://avatars.githubusercontent.com/u/81202085?v=4&s=48" width="48" height="48" alt="0xJonHoldsCrypto" title="0xJonHoldsCrypto"/></a> <a href="https://github.com/aaronn"><img src="https://avatars.githubusercontent.com/u/1653630?v=4&s=48" width="48" height="48" alt="aaronn" title="aaronn"/></a> <a href="https://github.com/Alphonse-arianee"><img src="https://avatars.githubusercontent.com/u/254457365?v=4&s=48" width="48" height="48" alt="Alphonse-arianee" title="Alphonse-arianee"/></a>
<a href="https://github.com/atalovesyou"><img src="https://avatars.githubusercontent.com/u/3534502?v=4&s=48" width="48" height="48" alt="atalovesyou" title="atalovesyou"/></a> <a href="https://github.com/search?q=Azade"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Azade" title="Azade"/></a> <a href="https://github.com/carlulsoe"><img src="https://avatars.githubusercontent.com/u/34673973?v=4&s=48" width="48" height="48" alt="carlulsoe" title="carlulsoe"/></a> <a href="https://github.com/search?q=ddyo"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ddyo" title="ddyo"/></a> <a href="https://github.com/search?q=Erik"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Erik" title="Erik"/></a> <a href="https://github.com/latitudeki5223"><img src="https://avatars.githubusercontent.com/u/119656367?v=4&s=48" width="48" height="48" alt="latitudeki5223" title="latitudeki5223"/></a> <a href="https://github.com/search?q=Manuel%20Maly"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Manuel Maly" title="Manuel Maly"/></a> <a href="https://github.com/search?q=Mourad%20Boustani"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mourad Boustani" title="Mourad Boustani"/></a> <a href="https://github.com/odrobnik"><img src="https://avatars.githubusercontent.com/u/333270?v=4&s=48" width="48" height="48" alt="odrobnik" title="odrobnik"/></a> <a href="https://github.com/pcty-nextgen-ios-builder"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pcty-nextgen-ios-builder" title="pcty-nextgen-ios-builder"/></a>
<a href="https://github.com/search?q=Quentin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Quentin" title="Quentin"/></a> <a href="https://github.com/search?q=Randy%20Torres"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Randy Torres" title="Randy Torres"/></a> <a href="https://github.com/rhjoh"><img src="https://avatars.githubusercontent.com/u/105699450?v=4&s=48" width="48" height="48" alt="rhjoh" title="rhjoh"/></a> <a href="https://github.com/ronak-guliani"><img src="https://avatars.githubusercontent.com/u/23518228?v=4&s=48" width="48" height="48" alt="ronak-guliani" title="ronak-guliani"/></a> <a href="https://github.com/search?q=William%20Stock"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="William Stock" title="William Stock"/></a>
<a href="https://github.com/steipete"><img src="https://avatars.githubusercontent.com/u/58493?v=4&s=48" width="48" height="48" alt="steipete" title="steipete"/></a> <a href="https://github.com/joshp123"><img src="https://avatars.githubusercontent.com/u/1497361?v=4&s=48" width="48" height="48" alt="joshp123" title="joshp123"/></a> <a href="https://github.com/cpojer"><img src="https://avatars.githubusercontent.com/u/13352?v=4&s=48" width="48" height="48" alt="cpojer" title="cpojer"/></a> <a href="https://github.com/mbelinky"><img src="https://avatars.githubusercontent.com/u/132747814?v=4&s=48" width="48" height="48" alt="Mariano Belinky" title="Mariano Belinky"/></a> <a href="https://github.com/sebslight"><img src="https://avatars.githubusercontent.com/u/19554889?v=4&s=48" width="48" height="48" alt="sebslight" title="sebslight"/></a> <a href="https://github.com/Takhoffman"><img src="https://avatars.githubusercontent.com/u/781889?v=4&s=48" width="48" height="48" alt="Takhoffman" title="Takhoffman"/></a> <a href="https://github.com/quotentiroler"><img src="https://avatars.githubusercontent.com/u/40643627?v=4&s=48" width="48" height="48" alt="quotentiroler" title="quotentiroler"/></a> <a href="https://github.com/bohdanpodvirnyi"><img src="https://avatars.githubusercontent.com/u/31819391?v=4&s=48" width="48" height="48" alt="bohdanpodvirnyi" title="bohdanpodvirnyi"/></a> <a href="https://github.com/tyler6204"><img src="https://avatars.githubusercontent.com/u/64381258?v=4&s=48" width="48" height="48" alt="tyler6204" title="tyler6204"/></a> <a href="https://github.com/iHildy"><img src="https://avatars.githubusercontent.com/u/25069719?v=4&s=48" width="48" height="48" alt="iHildy" title="iHildy"/></a>
<a href="https://github.com/jaydenfyi"><img src="https://avatars.githubusercontent.com/u/213395523?v=4&s=48" width="48" height="48" alt="jaydenfyi" title="jaydenfyi"/></a> <a href="https://github.com/gumadeiras"><img src="https://avatars.githubusercontent.com/u/5599352?v=4&s=48" width="48" height="48" alt="gumadeiras" title="gumadeiras"/></a> <a href="https://github.com/joaohlisboa"><img src="https://avatars.githubusercontent.com/u/8200873?v=4&s=48" width="48" height="48" alt="joaohlisboa" title="joaohlisboa"/></a> <a href="https://github.com/mneves75"><img src="https://avatars.githubusercontent.com/u/2423436?v=4&s=48" width="48" height="48" alt="mneves75" title="mneves75"/></a> <a href="https://github.com/MatthieuBizien"><img src="https://avatars.githubusercontent.com/u/173090?v=4&s=48" width="48" height="48" alt="MatthieuBizien" title="MatthieuBizien"/></a> <a href="https://github.com/Glucksberg"><img src="https://avatars.githubusercontent.com/u/80581902?v=4&s=48" width="48" height="48" alt="Glucksberg" title="Glucksberg"/></a> <a href="https://github.com/MaudeBot"><img src="https://avatars.githubusercontent.com/u/255777700?v=4&s=48" width="48" height="48" alt="MaudeBot" title="MaudeBot"/></a> <a href="https://github.com/rahthakor"><img src="https://avatars.githubusercontent.com/u/8470553?v=4&s=48" width="48" height="48" alt="rahthakor" title="rahthakor"/></a> <a href="https://github.com/vrknetha"><img src="https://avatars.githubusercontent.com/u/20596261?v=4&s=48" width="48" height="48" alt="vrknetha" title="vrknetha"/></a> <a href="https://github.com/vignesh07"><img src="https://avatars.githubusercontent.com/u/1436853?v=4&s=48" width="48" height="48" alt="vignesh07" title="vignesh07"/></a>
<a href="https://github.com/radek-paclt"><img src="https://avatars.githubusercontent.com/u/50451445?v=4&s=48" width="48" height="48" alt="radek-paclt" title="radek-paclt"/></a> <a href="https://github.com/abdelsfane"><img src="https://avatars.githubusercontent.com/u/32418586?v=4&s=48" width="48" height="48" alt="abdelsfane" title="abdelsfane"/></a> <a href="https://github.com/tobiasbischoff"><img src="https://avatars.githubusercontent.com/u/711564?v=4&s=48" width="48" height="48" alt="Tobias Bischoff" title="Tobias Bischoff"/></a> <a href="https://github.com/christianklotz"><img src="https://avatars.githubusercontent.com/u/69443?v=4&s=48" width="48" height="48" alt="christianklotz" title="christianklotz"/></a> <a href="https://github.com/czekaj"><img src="https://avatars.githubusercontent.com/u/1464539?v=4&s=48" width="48" height="48" alt="czekaj" title="czekaj"/></a> <a href="https://github.com/ethanpalm"><img src="https://avatars.githubusercontent.com/u/56270045?v=4&s=48" width="48" height="48" alt="ethanpalm" title="ethanpalm"/></a> <a href="https://github.com/mukhtharcm"><img src="https://avatars.githubusercontent.com/u/56378562?v=4&s=48" width="48" height="48" alt="mukhtharcm" title="mukhtharcm"/></a> <a href="https://github.com/maxsumrall"><img src="https://avatars.githubusercontent.com/u/628843?v=4&s=48" width="48" height="48" alt="maxsumrall" title="maxsumrall"/></a> <a href="https://github.com/rodrigouroz"><img src="https://avatars.githubusercontent.com/u/384037?v=4&s=48" width="48" height="48" alt="rodrigouroz" title="rodrigouroz"/></a> <a href="https://github.com/xadenryan"><img src="https://avatars.githubusercontent.com/u/165437834?v=4&s=48" width="48" height="48" alt="xadenryan" title="xadenryan"/></a>
<a href="https://github.com/VACInc"><img src="https://avatars.githubusercontent.com/u/3279061?v=4&s=48" width="48" height="48" alt="VACInc" title="VACInc"/></a> <a href="https://github.com/juanpablodlc"><img src="https://avatars.githubusercontent.com/u/92012363?v=4&s=48" width="48" height="48" alt="juanpablodlc" title="juanpablodlc"/></a> <a href="https://github.com/conroywhitney"><img src="https://avatars.githubusercontent.com/u/249891?v=4&s=48" width="48" height="48" alt="conroywhitney" title="conroywhitney"/></a> <a href="https://github.com/hsrvc"><img src="https://avatars.githubusercontent.com/u/129702169?v=4&s=48" width="48" height="48" alt="hsrvc" title="hsrvc"/></a> <a href="https://github.com/magimetal"><img src="https://avatars.githubusercontent.com/u/36491250?v=4&s=48" width="48" height="48" alt="magimetal" title="magimetal"/></a> <a href="https://github.com/zerone0x"><img src="https://avatars.githubusercontent.com/u/39543393?v=4&s=48" width="48" height="48" alt="zerone0x" title="zerone0x"/></a> <a href="https://github.com/advaitpaliwal"><img src="https://avatars.githubusercontent.com/u/66044327?v=4&s=48" width="48" height="48" alt="advaitpaliwal" title="advaitpaliwal"/></a> <a href="https://github.com/meaningfool"><img src="https://avatars.githubusercontent.com/u/2862331?v=4&s=48" width="48" height="48" alt="meaningfool" title="meaningfool"/></a> <a href="https://github.com/patelhiren"><img src="https://avatars.githubusercontent.com/u/172098?v=4&s=48" width="48" height="48" alt="patelhiren" title="patelhiren"/></a> <a href="https://github.com/NicholasSpisak"><img src="https://avatars.githubusercontent.com/u/129075147?v=4&s=48" width="48" height="48" alt="NicholasSpisak" title="NicholasSpisak"/></a>
<a href="https://github.com/jonisjongithub"><img src="https://avatars.githubusercontent.com/u/86072337?v=4&s=48" width="48" height="48" alt="jonisjongithub" title="jonisjongithub"/></a> <a href="https://github.com/AbhisekBasu1"><img src="https://avatars.githubusercontent.com/u/40645221?v=4&s=48" width="48" height="48" alt="abhisekbasu1" title="abhisekbasu1"/></a> <a href="https://github.com/theonejvo"><img src="https://avatars.githubusercontent.com/u/125909656?v=4&s=48" width="48" height="48" alt="theonejvo" title="theonejvo"/></a> <a href="https://github.com/jamesgroat"><img src="https://avatars.githubusercontent.com/u/2634024?v=4&s=48" width="48" height="48" alt="jamesgroat" title="jamesgroat"/></a> <a href="https://github.com/BunsDev"><img src="https://avatars.githubusercontent.com/u/68980965?v=4&s=48" width="48" height="48" alt="BunsDev" title="BunsDev"/></a> <a href="https://github.com/claude"><img src="https://avatars.githubusercontent.com/u/81847?v=4&s=48" width="48" height="48" alt="claude" title="claude"/></a> <a href="https://github.com/JustYannicc"><img src="https://avatars.githubusercontent.com/u/52761674?v=4&s=48" width="48" height="48" alt="JustYannicc" title="JustYannicc"/></a> <a href="https://github.com/Hyaxia"><img src="https://avatars.githubusercontent.com/u/36747317?v=4&s=48" width="48" height="48" alt="Hyaxia" title="Hyaxia"/></a> <a href="https://github.com/dantelex"><img src="https://avatars.githubusercontent.com/u/631543?v=4&s=48" width="48" height="48" alt="dantelex" title="dantelex"/></a> <a href="https://github.com/SocialNerd42069"><img src="https://avatars.githubusercontent.com/u/118244303?v=4&s=48" width="48" height="48" alt="SocialNerd42069" title="SocialNerd42069"/></a>
<a href="https://github.com/daveonkels"><img src="https://avatars.githubusercontent.com/u/533642?v=4&s=48" width="48" height="48" alt="daveonkels" title="daveonkels"/></a> <a href="https://github.com/Yida-Dev"><img src="https://avatars.githubusercontent.com/u/92713555?v=4&s=48" width="48" height="48" alt="Yida-Dev" title="Yida-Dev"/></a> <a href="https://github.com/apps/google-labs-jules"><img src="https://avatars.githubusercontent.com/in/842251?v=4&s=48" width="48" height="48" alt="google-labs-jules[bot]" title="google-labs-jules[bot]"/></a> <a href="https://github.com/riccardogiorato"><img src="https://avatars.githubusercontent.com/u/4527364?v=4&s=48" width="48" height="48" alt="riccardogiorato" title="riccardogiorato"/></a> <a href="https://github.com/lc0rp"><img src="https://avatars.githubusercontent.com/u/2609441?v=4&s=48" width="48" height="48" alt="lc0rp" title="lc0rp"/></a> <a href="https://github.com/adam91holt"><img src="https://avatars.githubusercontent.com/u/9592417?v=4&s=48" width="48" height="48" alt="adam91holt" title="adam91holt"/></a> <a href="https://github.com/mousberg"><img src="https://avatars.githubusercontent.com/u/57605064?v=4&s=48" width="48" height="48" alt="mousberg" title="mousberg"/></a> <a href="https://github.com/apps/clawdinator"><img src="https://avatars.githubusercontent.com/in/2607181?v=4&s=48" width="48" height="48" alt="clawdinator[bot]" title="clawdinator[bot]"/></a> <a href="https://github.com/hougangdev"><img src="https://avatars.githubusercontent.com/u/105773686?v=4&s=48" width="48" height="48" alt="hougangdev" title="hougangdev"/></a> <a href="https://github.com/shakkernerd"><img src="https://avatars.githubusercontent.com/u/165377636?v=4&s=48" width="48" height="48" alt="shakkernerd" title="shakkernerd"/></a>
<a href="https://github.com/coygeek"><img src="https://avatars.githubusercontent.com/u/65363919?v=4&s=48" width="48" height="48" alt="coygeek" title="coygeek"/></a> <a href="https://github.com/mteam88"><img src="https://avatars.githubusercontent.com/u/84196639?v=4&s=48" width="48" height="48" alt="mteam88" title="mteam88"/></a> <a href="https://github.com/hirefrank"><img src="https://avatars.githubusercontent.com/u/183158?v=4&s=48" width="48" height="48" alt="hirefrank" title="hirefrank"/></a> <a href="https://github.com/M00N7682"><img src="https://avatars.githubusercontent.com/u/170746674?v=4&s=48" width="48" height="48" alt="M00N7682" title="M00N7682"/></a> <a href="https://github.com/joeynyc"><img src="https://avatars.githubusercontent.com/u/17919866?v=4&s=48" width="48" height="48" alt="joeynyc" title="joeynyc"/></a> <a href="https://github.com/orlyjamie"><img src="https://avatars.githubusercontent.com/u/6668807?v=4&s=48" width="48" height="48" alt="orlyjamie" title="orlyjamie"/></a> <a href="https://github.com/dbhurley"><img src="https://avatars.githubusercontent.com/u/5251425?v=4&s=48" width="48" height="48" alt="dbhurley" title="dbhurley"/></a> <a href="https://github.com/omniwired"><img src="https://avatars.githubusercontent.com/u/322761?v=4&s=48" width="48" height="48" alt="Eng. Juan Combetto" title="Eng. Juan Combetto"/></a> <a href="https://github.com/TSavo"><img src="https://avatars.githubusercontent.com/u/877990?v=4&s=48" width="48" height="48" alt="TSavo" title="TSavo"/></a> <a href="https://github.com/aerolalit"><img src="https://avatars.githubusercontent.com/u/17166039?v=4&s=48" width="48" height="48" alt="aerolalit" title="aerolalit"/></a>
<a href="https://github.com/julianengel"><img src="https://avatars.githubusercontent.com/u/10634231?v=4&s=48" width="48" height="48" alt="julianengel" title="julianengel"/></a> <a href="https://github.com/bradleypriest"><img src="https://avatars.githubusercontent.com/u/167215?v=4&s=48" width="48" height="48" alt="bradleypriest" title="bradleypriest"/></a> <a href="https://github.com/benithors"><img src="https://avatars.githubusercontent.com/u/20652882?v=4&s=48" width="48" height="48" alt="benithors" title="benithors"/></a> <a href="https://github.com/lsh411"><img src="https://avatars.githubusercontent.com/u/6801488?v=4&s=48" width="48" height="48" alt="lsh411" title="lsh411"/></a> <a href="https://github.com/gut-puncture"><img src="https://avatars.githubusercontent.com/u/75851986?v=4&s=48" width="48" height="48" alt="gut-puncture" title="gut-puncture"/></a> <a href="https://github.com/rohannagpal"><img src="https://avatars.githubusercontent.com/u/4009239?v=4&s=48" width="48" height="48" alt="rohannagpal" title="rohannagpal"/></a> <a href="https://github.com/timolins"><img src="https://avatars.githubusercontent.com/u/1440854?v=4&s=48" width="48" height="48" alt="timolins" title="timolins"/></a> <a href="https://github.com/f-trycua"><img src="https://avatars.githubusercontent.com/u/195596869?v=4&s=48" width="48" height="48" alt="f-trycua" title="f-trycua"/></a> <a href="https://github.com/benostein"><img src="https://avatars.githubusercontent.com/u/31802821?v=4&s=48" width="48" height="48" alt="benostein" title="benostein"/></a> <a href="https://github.com/elliotsecops"><img src="https://avatars.githubusercontent.com/u/141947839?v=4&s=48" width="48" height="48" alt="elliotsecops" title="elliotsecops"/></a>
<a href="https://github.com/Nachx639"><img src="https://avatars.githubusercontent.com/u/71144023?v=4&s=48" width="48" height="48" alt="nachx639" title="nachx639"/></a> <a href="https://github.com/pvoo"><img src="https://avatars.githubusercontent.com/u/20116814?v=4&s=48" width="48" height="48" alt="pvoo" title="pvoo"/></a> <a href="https://github.com/sreekaransrinath"><img src="https://avatars.githubusercontent.com/u/50989977?v=4&s=48" width="48" height="48" alt="sreekaransrinath" title="sreekaransrinath"/></a> <a href="https://github.com/gupsammy"><img src="https://avatars.githubusercontent.com/u/20296019?v=4&s=48" width="48" height="48" alt="gupsammy" title="gupsammy"/></a> <a href="https://github.com/cristip73"><img src="https://avatars.githubusercontent.com/u/24499421?v=4&s=48" width="48" height="48" alt="cristip73" title="cristip73"/></a> <a href="https://github.com/stefangalescu"><img src="https://avatars.githubusercontent.com/u/52995748?v=4&s=48" width="48" height="48" alt="stefangalescu" title="stefangalescu"/></a> <a href="https://github.com/nachoiacovino"><img src="https://avatars.githubusercontent.com/u/50103937?v=4&s=48" width="48" height="48" alt="nachoiacovino" title="nachoiacovino"/></a> <a href="https://github.com/vsabavat"><img src="https://avatars.githubusercontent.com/u/50385532?v=4&s=48" width="48" height="48" alt="Vasanth Rao Naik Sabavat" title="Vasanth Rao Naik Sabavat"/></a> <a href="https://github.com/thewilloftheshadow"><img src="https://avatars.githubusercontent.com/u/35580099?v=4&s=48" width="48" height="48" alt="thewilloftheshadow" title="thewilloftheshadow"/></a> <a href="https://github.com/petter-b"><img src="https://avatars.githubusercontent.com/u/62076402?v=4&s=48" width="48" height="48" alt="petter-b" title="petter-b"/></a>
<a href="https://github.com/leszekszpunar"><img src="https://avatars.githubusercontent.com/u/13106764?v=4&s=48" width="48" height="48" alt="leszekszpunar" title="leszekszpunar"/></a> <a href="https://github.com/scald"><img src="https://avatars.githubusercontent.com/u/1215913?v=4&s=48" width="48" height="48" alt="scald" title="scald"/></a> <a href="https://github.com/pycckuu"><img src="https://avatars.githubusercontent.com/u/1489583?v=4&s=48" width="48" height="48" alt="pycckuu" title="pycckuu"/></a> <a href="https://github.com/AnonO6"><img src="https://avatars.githubusercontent.com/u/124311066?v=4&s=48" width="48" height="48" alt="AnonO6" title="AnonO6"/></a> <a href="https://github.com/andranik-sahakyan"><img src="https://avatars.githubusercontent.com/u/8908029?v=4&s=48" width="48" height="48" alt="andranik-sahakyan" title="andranik-sahakyan"/></a> <a href="https://github.com/davidguttman"><img src="https://avatars.githubusercontent.com/u/431696?v=4&s=48" width="48" height="48" alt="davidguttman" title="davidguttman"/></a> <a href="https://github.com/jarvis89757"><img src="https://avatars.githubusercontent.com/u/258175441?v=4&s=48" width="48" height="48" alt="jarvis89757" title="jarvis89757"/></a> <a href="https://github.com/sleontenko"><img src="https://avatars.githubusercontent.com/u/7135949?v=4&s=48" width="48" height="48" alt="sleontenko" title="sleontenko"/></a> <a href="https://github.com/denysvitali"><img src="https://avatars.githubusercontent.com/u/4939519?v=4&s=48" width="48" height="48" alt="denysvitali" title="denysvitali"/></a> <a href="https://github.com/TinyTb"><img src="https://avatars.githubusercontent.com/u/5957298?v=4&s=48" width="48" height="48" alt="TinyTb" title="TinyTb"/></a>
<a href="https://github.com/sircrumpet"><img src="https://avatars.githubusercontent.com/u/4436535?v=4&s=48" width="48" height="48" alt="sircrumpet" title="sircrumpet"/></a> <a href="https://github.com/peschee"><img src="https://avatars.githubusercontent.com/u/63866?v=4&s=48" width="48" height="48" alt="peschee" title="peschee"/></a> <a href="https://github.com/nicolasstanley"><img src="https://avatars.githubusercontent.com/u/60584925?v=4&s=48" width="48" height="48" alt="nicolasstanley" title="nicolasstanley"/></a> <a href="https://github.com/davidiach"><img src="https://avatars.githubusercontent.com/u/28102235?v=4&s=48" width="48" height="48" alt="davidiach" title="davidiach"/></a> <a href="https://github.com/nonggialiang"><img src="https://avatars.githubusercontent.com/u/14367839?v=4&s=48" width="48" height="48" alt="nonggia.liang" title="nonggia.liang"/></a> <a href="https://github.com/ironbyte-rgb"><img src="https://avatars.githubusercontent.com/u/230665944?v=4&s=48" width="48" height="48" alt="ironbyte-rgb" title="ironbyte-rgb"/></a> <a href="https://github.com/dominicnunez"><img src="https://avatars.githubusercontent.com/u/43616264?v=4&s=48" width="48" height="48" alt="dominicnunez" title="dominicnunez"/></a> <a href="https://github.com/lploc94"><img src="https://avatars.githubusercontent.com/u/28453843?v=4&s=48" width="48" height="48" alt="lploc94" title="lploc94"/></a> <a href="https://github.com/ratulsarna"><img src="https://avatars.githubusercontent.com/u/105903728?v=4&s=48" width="48" height="48" alt="ratulsarna" title="ratulsarna"/></a> <a href="https://github.com/sfo2001"><img src="https://avatars.githubusercontent.com/u/103369858?v=4&s=48" width="48" height="48" alt="sfo2001" title="sfo2001"/></a>
<a href="https://github.com/lutr0"><img src="https://avatars.githubusercontent.com/u/76906369?v=4&s=48" width="48" height="48" alt="lutr0" title="lutr0"/></a> <a href="https://github.com/kiranjd"><img src="https://avatars.githubusercontent.com/u/25822851?v=4&s=48" width="48" height="48" alt="kiranjd" title="kiranjd"/></a> <a href="https://github.com/danielz1z"><img src="https://avatars.githubusercontent.com/u/235270390?v=4&s=48" width="48" height="48" alt="danielz1z" title="danielz1z"/></a> <a href="https://github.com/Iranb"><img src="https://avatars.githubusercontent.com/u/49674669?v=4&s=48" width="48" height="48" alt="Iranb" title="Iranb"/></a> <a href="https://github.com/cdorsey"><img src="https://avatars.githubusercontent.com/u/12650570?v=4&s=48" width="48" height="48" alt="cdorsey" title="cdorsey"/></a> <a href="https://github.com/AdeboyeDN"><img src="https://avatars.githubusercontent.com/u/65312338?v=4&s=48" width="48" height="48" alt="AdeboyeDN" title="AdeboyeDN"/></a> <a href="https://github.com/obviyus"><img src="https://avatars.githubusercontent.com/u/22031114?v=4&s=48" width="48" height="48" alt="obviyus" title="obviyus"/></a> <a href="https://github.com/Alg0rix"><img src="https://avatars.githubusercontent.com/u/53804949?v=4&s=48" width="48" height="48" alt="Alg0rix" title="Alg0rix"/></a> <a href="https://github.com/papago2355"><img src="https://avatars.githubusercontent.com/u/68721273?v=4&s=48" width="48" height="48" alt="papago2355" title="papago2355"/></a> <a href="https://github.com/peetzweg"><img src="https://avatars.githubusercontent.com/u/839848?v=4&s=48" width="48" height="48" alt="peetzweg/" title="peetzweg/"/></a>
<a href="https://github.com/emanuelst"><img src="https://avatars.githubusercontent.com/u/9994339?v=4&s=48" width="48" height="48" alt="emanuelst" title="emanuelst"/></a> <a href="https://github.com/evanotero"><img src="https://avatars.githubusercontent.com/u/13204105?v=4&s=48" width="48" height="48" alt="evanotero" title="evanotero"/></a> <a href="https://github.com/KristijanJovanovski"><img src="https://avatars.githubusercontent.com/u/8942284?v=4&s=48" width="48" height="48" alt="KristijanJovanovski" title="KristijanJovanovski"/></a> <a href="https://github.com/jlowin"><img src="https://avatars.githubusercontent.com/u/153965?v=4&s=48" width="48" height="48" alt="jlowin" title="jlowin"/></a> <a href="https://github.com/rdev"><img src="https://avatars.githubusercontent.com/u/8418866?v=4&s=48" width="48" height="48" alt="rdev" title="rdev"/></a> <a href="https://github.com/rhuanssauro"><img src="https://avatars.githubusercontent.com/u/164682191?v=4&s=48" width="48" height="48" alt="rhuanssauro" title="rhuanssauro"/></a> <a href="https://github.com/joshrad-dev"><img src="https://avatars.githubusercontent.com/u/62785552?v=4&s=48" width="48" height="48" alt="joshrad-dev" title="joshrad-dev"/></a> <a href="https://github.com/osolmaz"><img src="https://avatars.githubusercontent.com/u/2453968?v=4&s=48" width="48" height="48" alt="osolmaz" title="osolmaz"/></a> <a href="https://github.com/adityashaw2"><img src="https://avatars.githubusercontent.com/u/41204444?v=4&s=48" width="48" height="48" alt="adityashaw2" title="adityashaw2"/></a> <a href="https://github.com/shadril238"><img src="https://avatars.githubusercontent.com/u/63901551?v=4&s=48" width="48" height="48" alt="shadril238" title="shadril238"/></a>
<a href="https://github.com/CashWilliams"><img src="https://avatars.githubusercontent.com/u/613573?v=4&s=48" width="48" height="48" alt="CashWilliams" title="CashWilliams"/></a> <a href="https://github.com/search?q=sheeek"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="sheeek" title="sheeek"/></a> <a href="https://github.com/ryancontent"><img src="https://avatars.githubusercontent.com/u/39743613?v=4&s=48" width="48" height="48" alt="ryan" title="ryan"/></a> <a href="https://github.com/jasonsschin"><img src="https://avatars.githubusercontent.com/u/1456889?v=4&s=48" width="48" height="48" alt="jasonsschin" title="jasonsschin"/></a> <a href="https://github.com/artuskg"><img src="https://avatars.githubusercontent.com/u/11966157?v=4&s=48" width="48" height="48" alt="artuskg" title="artuskg"/></a> <a href="https://github.com/onutc"><img src="https://avatars.githubusercontent.com/u/152018508?v=4&s=48" width="48" height="48" alt="onutc" title="onutc"/></a> <a href="https://github.com/pauloportella"><img src="https://avatars.githubusercontent.com/u/22947229?v=4&s=48" width="48" height="48" alt="pauloportella" title="pauloportella"/></a> <a href="https://github.com/HirokiKobayashi-R"><img src="https://avatars.githubusercontent.com/u/37167840?v=4&s=48" width="48" height="48" alt="HirokiKobayashi-R" title="HirokiKobayashi-R"/></a> <a href="https://github.com/ThanhNguyxn"><img src="https://avatars.githubusercontent.com/u/74597207?v=4&s=48" width="48" height="48" alt="ThanhNguyxn" title="ThanhNguyxn"/></a> <a href="https://github.com/18-RAJAT"><img src="https://avatars.githubusercontent.com/u/78920780?v=4&s=48" width="48" height="48" alt="18-RAJAT" title="18-RAJAT"/></a>
<a href="https://github.com/kimitaka"><img src="https://avatars.githubusercontent.com/u/167225?v=4&s=48" width="48" height="48" alt="kimitaka" title="kimitaka"/></a> <a href="https://github.com/yuting0624"><img src="https://avatars.githubusercontent.com/u/32728916?v=4&s=48" width="48" height="48" alt="yuting0624" title="yuting0624"/></a> <a href="https://github.com/neooriginal"><img src="https://avatars.githubusercontent.com/u/54811660?v=4&s=48" width="48" height="48" alt="neooriginal" title="neooriginal"/></a> <a href="https://github.com/ManuelHettich"><img src="https://avatars.githubusercontent.com/u/17690367?v=4&s=48" width="48" height="48" alt="manuelhettich" title="manuelhettich"/></a> <a href="https://github.com/unisone"><img src="https://avatars.githubusercontent.com/u/32521398?v=4&s=48" width="48" height="48" alt="unisone" title="unisone"/></a> <a href="https://github.com/baccula"><img src="https://avatars.githubusercontent.com/u/22080883?v=4&s=48" width="48" height="48" alt="baccula" title="baccula"/></a> <a href="https://github.com/manikv12"><img src="https://avatars.githubusercontent.com/u/49544491?v=4&s=48" width="48" height="48" alt="manikv12" title="manikv12"/></a> <a href="https://github.com/sbking"><img src="https://avatars.githubusercontent.com/u/3913213?v=4&s=48" width="48" height="48" alt="sbking" title="sbking"/></a> <a href="https://github.com/travisirby"><img src="https://avatars.githubusercontent.com/u/5958376?v=4&s=48" width="48" height="48" alt="travisirby" title="travisirby"/></a> <a href="https://github.com/fujiwara-tofu-shop"><img src="https://avatars.githubusercontent.com/u/259415332?v=4&s=48" width="48" height="48" alt="fujiwara-tofu-shop" title="fujiwara-tofu-shop"/></a>
<a href="https://github.com/buddyh"><img src="https://avatars.githubusercontent.com/u/31752869?v=4&s=48" width="48" height="48" alt="buddyh" title="buddyh"/></a> <a href="https://github.com/connorshea"><img src="https://avatars.githubusercontent.com/u/2977353?v=4&s=48" width="48" height="48" alt="connorshea" title="connorshea"/></a> <a href="https://github.com/bjesuiter"><img src="https://avatars.githubusercontent.com/u/2365676?v=4&s=48" width="48" height="48" alt="bjesuiter" title="bjesuiter"/></a> <a href="https://github.com/kyleok"><img src="https://avatars.githubusercontent.com/u/58307870?v=4&s=48" width="48" height="48" alt="kyleok" title="kyleok"/></a> <a href="https://github.com/mcinteerj"><img src="https://avatars.githubusercontent.com/u/3613653?v=4&s=48" width="48" height="48" alt="mcinteerj" title="mcinteerj"/></a> <a href="https://github.com/slonce70"><img src="https://avatars.githubusercontent.com/u/130596182?v=4&s=48" width="48" height="48" alt="slonce70" title="slonce70"/></a> <a href="https://github.com/calvin-hpnet"><img src="https://avatars.githubusercontent.com/u/258432838?v=4&s=48" width="48" height="48" alt="calvin-hpnet" title="calvin-hpnet"/></a> <a href="https://github.com/gitpds"><img src="https://avatars.githubusercontent.com/u/78130276?v=4&s=48" width="48" height="48" alt="gitpds" title="gitpds"/></a> <a href="https://github.com/ide-rea"><img src="https://avatars.githubusercontent.com/u/30512600?v=4&s=48" width="48" height="48" alt="ide-rea" title="ide-rea"/></a> <a href="https://github.com/badlogic"><img src="https://avatars.githubusercontent.com/u/514052?v=4&s=48" width="48" height="48" alt="badlogic" title="badlogic"/></a>
<a href="https://github.com/grp06"><img src="https://avatars.githubusercontent.com/u/1573959?v=4&s=48" width="48" height="48" alt="grp06" title="grp06"/></a> <a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4&s=48" width="48" height="48" alt="dependabot[bot]" title="dependabot[bot]"/></a> <a href="https://github.com/amitbiswal007"><img src="https://avatars.githubusercontent.com/u/108086198?v=4&s=48" width="48" height="48" alt="amitbiswal007" title="amitbiswal007"/></a> <a href="https://github.com/John-Rood"><img src="https://avatars.githubusercontent.com/u/62669593?v=4&s=48" width="48" height="48" alt="John-Rood" title="John-Rood"/></a> <a href="https://github.com/timkrase"><img src="https://avatars.githubusercontent.com/u/38947626?v=4&s=48" width="48" height="48" alt="timkrase" title="timkrase"/></a> <a href="https://github.com/gerardward2007"><img src="https://avatars.githubusercontent.com/u/3002155?v=4&s=48" width="48" height="48" alt="gerardward2007" title="gerardward2007"/></a> <a href="https://github.com/roshanasingh4"><img src="https://avatars.githubusercontent.com/u/88576930?v=4&s=48" width="48" height="48" alt="roshanasingh4" title="roshanasingh4"/></a> <a href="https://github.com/tosh-hamburg"><img src="https://avatars.githubusercontent.com/u/58424326?v=4&s=48" width="48" height="48" alt="tosh-hamburg" title="tosh-hamburg"/></a> <a href="https://github.com/azade-c"><img src="https://avatars.githubusercontent.com/u/252790079?v=4&s=48" width="48" height="48" alt="azade-c" title="azade-c"/></a> <a href="https://github.com/dlauer"><img src="https://avatars.githubusercontent.com/u/757041?v=4&s=48" width="48" height="48" alt="dlauer" title="dlauer"/></a>
<a href="https://github.com/ezhikkk"><img src="https://avatars.githubusercontent.com/u/105670095?v=4&s=48" width="48" height="48" alt="ezhikkk" title="ezhikkk"/></a> <a href="https://github.com/JonUleis"><img src="https://avatars.githubusercontent.com/u/7644941?v=4&s=48" width="48" height="48" alt="JonUleis" title="JonUleis"/></a> <a href="https://github.com/shivamraut101"><img src="https://avatars.githubusercontent.com/u/110457469?v=4&s=48" width="48" height="48" alt="shivamraut101" title="shivamraut101"/></a> <a href="https://github.com/cheeeee"><img src="https://avatars.githubusercontent.com/u/21245729?v=4&s=48" width="48" height="48" alt="cheeeee" title="cheeeee"/></a> <a href="https://github.com/jabezborja"><img src="https://avatars.githubusercontent.com/u/64759159?v=4&s=48" width="48" height="48" alt="jabezborja" title="jabezborja"/></a> <a href="https://github.com/robbyczgw-cla"><img src="https://avatars.githubusercontent.com/u/239660374?v=4&s=48" width="48" height="48" alt="robbyczgw-cla" title="robbyczgw-cla"/></a> <a href="https://github.com/YuriNachos"><img src="https://avatars.githubusercontent.com/u/19365375?v=4&s=48" width="48" height="48" alt="YuriNachos" title="YuriNachos"/></a> <a href="https://github.com/j1philli"><img src="https://avatars.githubusercontent.com/u/3744255?v=4&s=48" width="48" height="48" alt="Josh Phillips" title="Josh Phillips"/></a> <a href="https://github.com/Wangnov"><img src="https://avatars.githubusercontent.com/u/48670012?v=4&s=48" width="48" height="48" alt="Wangnov" title="Wangnov"/></a> <a href="https://github.com/kaizen403"><img src="https://avatars.githubusercontent.com/u/134706404?v=4&s=48" width="48" height="48" alt="kaizen403" title="kaizen403"/></a>
<a href="https://github.com/patrickshao"><img src="https://avatars.githubusercontent.com/u/5953037?v=4&s=48" width="48" height="48" alt="patrickshao" title="patrickshao"/></a> <a href="https://github.com/Whoaa512"><img src="https://avatars.githubusercontent.com/u/1581943?v=4&s=48" width="48" height="48" alt="Whoaa512" title="Whoaa512"/></a> <a href="https://github.com/chriseidhof"><img src="https://avatars.githubusercontent.com/u/5382?v=4&s=48" width="48" height="48" alt="chriseidhof" title="chriseidhof"/></a> <a href="https://github.com/ngutman"><img src="https://avatars.githubusercontent.com/u/1540134?v=4&s=48" width="48" height="48" alt="ngutman" title="ngutman"/></a> <a href="https://github.com/wangai-studio"><img src="https://avatars.githubusercontent.com/u/256938352?v=4&s=48" width="48" height="48" alt="wangai-studio" title="wangai-studio"/></a> <a href="https://github.com/ysqander"><img src="https://avatars.githubusercontent.com/u/80843820?v=4&s=48" width="48" height="48" alt="ysqander" title="ysqander"/></a> <a href="https://github.com/search?q=Yurii%20Chukhlib"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Yurii Chukhlib" title="Yurii Chukhlib"/></a> <a href="https://github.com/aj47"><img src="https://avatars.githubusercontent.com/u/8023513?v=4&s=48" width="48" height="48" alt="aj47" title="aj47"/></a> <a href="https://github.com/kennyklee"><img src="https://avatars.githubusercontent.com/u/1432489?v=4&s=48" width="48" height="48" alt="kennyklee" title="kennyklee"/></a> <a href="https://github.com/superman32432432"><img src="https://avatars.githubusercontent.com/u/7228420?v=4&s=48" width="48" height="48" alt="superman32432432" title="superman32432432"/></a>
<a href="https://github.com/Hisleren"><img src="https://avatars.githubusercontent.com/u/83217244?v=4&s=48" width="48" height="48" alt="Hisleren" title="Hisleren"/></a> <a href="https://github.com/antons"><img src="https://avatars.githubusercontent.com/u/129705?v=4&s=48" width="48" height="48" alt="antons" title="antons"/></a> <a href="https://github.com/austinm911"><img src="https://avatars.githubusercontent.com/u/31991302?v=4&s=48" width="48" height="48" alt="austinm911" title="austinm911"/></a> <a href="https://github.com/apps/blacksmith-sh"><img src="https://avatars.githubusercontent.com/in/807020?v=4&s=48" width="48" height="48" alt="blacksmith-sh[bot]" title="blacksmith-sh[bot]"/></a> <a href="https://github.com/damoahdominic"><img src="https://avatars.githubusercontent.com/u/4623434?v=4&s=48" width="48" height="48" alt="damoahdominic" title="damoahdominic"/></a> <a href="https://github.com/dan-dr"><img src="https://avatars.githubusercontent.com/u/6669808?v=4&s=48" width="48" height="48" alt="dan-dr" title="dan-dr"/></a> <a href="https://github.com/doodlewind"><img src="https://avatars.githubusercontent.com/u/7312949?v=4&s=48" width="48" height="48" alt="doodlewind" title="doodlewind"/></a> <a href="https://github.com/GHesericsu"><img src="https://avatars.githubusercontent.com/u/60202455?v=4&s=48" width="48" height="48" alt="GHesericsu" title="GHesericsu"/></a> <a href="https://github.com/HeimdallStrategy"><img src="https://avatars.githubusercontent.com/u/223014405?v=4&s=48" width="48" height="48" alt="HeimdallStrategy" title="HeimdallStrategy"/></a> <a href="https://github.com/imfing"><img src="https://avatars.githubusercontent.com/u/5097752?v=4&s=48" width="48" height="48" alt="imfing" title="imfing"/></a>
<a href="https://github.com/jalehman"><img src="https://avatars.githubusercontent.com/u/550978?v=4&s=48" width="48" height="48" alt="jalehman" title="jalehman"/></a> <a href="https://github.com/jarvis-medmatic"><img src="https://avatars.githubusercontent.com/u/252428873?v=4&s=48" width="48" height="48" alt="jarvis-medmatic" title="jarvis-medmatic"/></a> <a href="https://github.com/kkarimi"><img src="https://avatars.githubusercontent.com/u/875218?v=4&s=48" width="48" height="48" alt="kkarimi" title="kkarimi"/></a> <a href="https://github.com/Lukavyi"><img src="https://avatars.githubusercontent.com/u/1013690?v=4&s=48" width="48" height="48" alt="Lukavyi" title="Lukavyi"/></a> <a href="https://github.com/mahmoudashraf93"><img src="https://avatars.githubusercontent.com/u/9130129?v=4&s=48" width="48" height="48" alt="mahmoudashraf93" title="mahmoudashraf93"/></a> <a href="https://github.com/pkrmf"><img src="https://avatars.githubusercontent.com/u/1714267?v=4&s=48" width="48" height="48" alt="pkrmf" title="pkrmf"/></a> <a href="https://github.com/RandyVentures"><img src="https://avatars.githubusercontent.com/u/149904821?v=4&s=48" width="48" height="48" alt="RandyVentures" title="RandyVentures"/></a> <a href="https://github.com/search?q=Ryan%20Lisse"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ryan Lisse" title="Ryan Lisse"/></a> <a href="https://github.com/Yeom-JinHo"><img src="https://avatars.githubusercontent.com/u/81306489?v=4&s=48" width="48" height="48" alt="Yeom-JinHo" title="Yeom-JinHo"/></a> <a href="https://github.com/dougvk"><img src="https://avatars.githubusercontent.com/u/401660?v=4&s=48" width="48" height="48" alt="dougvk" title="dougvk"/></a>
<a href="https://github.com/erikpr1994"><img src="https://avatars.githubusercontent.com/u/6299331?v=4&s=48" width="48" height="48" alt="erikpr1994" title="erikpr1994"/></a> <a href="https://github.com/fal3"><img src="https://avatars.githubusercontent.com/u/6484295?v=4&s=48" width="48" height="48" alt="fal3" title="fal3"/></a> <a href="https://github.com/search?q=Ghost"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ghost" title="Ghost"/></a> <a href="https://github.com/hyf0-agent"><img src="https://avatars.githubusercontent.com/u/258783736?v=4&s=48" width="48" height="48" alt="hyf0-agent" title="hyf0-agent"/></a> <a href="https://github.com/jonasjancarik"><img src="https://avatars.githubusercontent.com/u/2459191?v=4&s=48" width="48" height="48" alt="jonasjancarik" title="jonasjancarik"/></a> <a href="https://github.com/search?q=Keith%20the%20Silly%20Goose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Keith the Silly Goose" title="Keith the Silly Goose"/></a> <a href="https://github.com/search?q=L36%20Server"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="L36 Server" title="L36 Server"/></a> <a href="https://github.com/search?q=Marc"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Marc" title="Marc"/></a> <a href="https://github.com/mitschabaude-bot"><img src="https://avatars.githubusercontent.com/u/247582884?v=4&s=48" width="48" height="48" alt="mitschabaude-bot" title="mitschabaude-bot"/></a> <a href="https://github.com/mkbehr"><img src="https://avatars.githubusercontent.com/u/1285?v=4&s=48" width="48" height="48" alt="mkbehr" title="mkbehr"/></a>
<a href="https://github.com/neist"><img src="https://avatars.githubusercontent.com/u/1029724?v=4&s=48" width="48" height="48" alt="neist" title="neist"/></a> <a href="https://github.com/orenyomtov"><img src="https://avatars.githubusercontent.com/u/168856?v=4&s=48" width="48" height="48" alt="orenyomtov" title="orenyomtov"/></a> <a href="https://github.com/sibbl"><img src="https://avatars.githubusercontent.com/u/866535?v=4&s=48" width="48" height="48" alt="sibbl" title="sibbl"/></a> <a href="https://github.com/zats"><img src="https://avatars.githubusercontent.com/u/2688806?v=4&s=48" width="48" height="48" alt="zats" title="zats"/></a> <a href="https://github.com/abhijeet117"><img src="https://avatars.githubusercontent.com/u/192859219?v=4&s=48" width="48" height="48" alt="abhijeet117" title="abhijeet117"/></a> <a href="https://github.com/chrisrodz"><img src="https://avatars.githubusercontent.com/u/2967620?v=4&s=48" width="48" height="48" alt="chrisrodz" title="chrisrodz"/></a> <a href="https://github.com/search?q=Friederike%20Seiler"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Friederike Seiler" title="Friederike Seiler"/></a> <a href="https://github.com/gabriel-trigo"><img src="https://avatars.githubusercontent.com/u/38991125?v=4&s=48" width="48" height="48" alt="gabriel-trigo" title="gabriel-trigo"/></a> <a href="https://github.com/hudson-rivera"><img src="https://avatars.githubusercontent.com/u/258693705?v=4&s=48" width="48" height="48" alt="hudson-rivera" title="hudson-rivera"/></a> <a href="https://github.com/Iamadig"><img src="https://avatars.githubusercontent.com/u/102129234?v=4&s=48" width="48" height="48" alt="iamadig" title="iamadig"/></a>
<a href="https://github.com/itsjling"><img src="https://avatars.githubusercontent.com/u/2521993?v=4&s=48" width="48" height="48" alt="itsjling" title="itsjling"/></a> <a href="https://github.com/jdrhyne"><img src="https://avatars.githubusercontent.com/u/7828464?v=4&s=48" width="48" height="48" alt="Jonathan D. Rhyne (DJ-D)" title="Jonathan D. Rhyne (DJ-D)"/></a> <a href="https://github.com/search?q=Joshua%20Mitchell"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Joshua Mitchell" title="Joshua Mitchell"/></a> <a href="https://github.com/kelvinCB"><img src="https://avatars.githubusercontent.com/u/50544379?v=4&s=48" width="48" height="48" alt="kelvinCB" title="kelvinCB"/></a> <a href="https://github.com/search?q=Kit"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kit" title="Kit"/></a> <a href="https://github.com/koala73"><img src="https://avatars.githubusercontent.com/u/996596?v=4&s=48" width="48" height="48" alt="koala73" title="koala73"/></a> <a href="https://github.com/lailoo"><img src="https://avatars.githubusercontent.com/u/20536249?v=4&s=48" width="48" height="48" alt="lailoo" title="lailoo"/></a> <a href="https://github.com/manmal"><img src="https://avatars.githubusercontent.com/u/142797?v=4&s=48" width="48" height="48" alt="manmal" title="manmal"/></a> <a href="https://github.com/mattqdev"><img src="https://avatars.githubusercontent.com/u/115874885?v=4&s=48" width="48" height="48" alt="mattqdev" title="mattqdev"/></a> <a href="https://github.com/mcaxtr"><img src="https://avatars.githubusercontent.com/u/7562095?v=4&s=48" width="48" height="48" alt="mcaxtr" title="mcaxtr"/></a>
<a href="https://github.com/mitsuhiko"><img src="https://avatars.githubusercontent.com/u/7396?v=4&s=48" width="48" height="48" alt="mitsuhiko" title="mitsuhiko"/></a> <a href="https://github.com/ogulcancelik"><img src="https://avatars.githubusercontent.com/u/7064011?v=4&s=48" width="48" height="48" alt="ogulcancelik" title="ogulcancelik"/></a> <a href="https://github.com/petradonka"><img src="https://avatars.githubusercontent.com/u/7353770?v=4&s=48" width="48" height="48" alt="petradonka" title="petradonka"/></a> <a href="https://github.com/rubyrunsstuff"><img src="https://avatars.githubusercontent.com/u/246602379?v=4&s=48" width="48" height="48" alt="rubyrunsstuff" title="rubyrunsstuff"/></a> <a href="https://github.com/rybnikov"><img src="https://avatars.githubusercontent.com/u/7761808?v=4&s=48" width="48" height="48" alt="rybnikov" title="rybnikov"/></a> <a href="https://github.com/siddhantjain"><img src="https://avatars.githubusercontent.com/u/4835232?v=4&s=48" width="48" height="48" alt="siddhantjain" title="siddhantjain"/></a> <a href="https://github.com/suminhthanh"><img src="https://avatars.githubusercontent.com/u/2907636?v=4&s=48" width="48" height="48" alt="suminhthanh" title="suminhthanh"/></a> <a href="https://github.com/svkozak"><img src="https://avatars.githubusercontent.com/u/31941359?v=4&s=48" width="48" height="48" alt="svkozak" title="svkozak"/></a> <a href="https://github.com/wes-davis"><img src="https://avatars.githubusercontent.com/u/16506720?v=4&s=48" width="48" height="48" alt="wes-davis" title="wes-davis"/></a> <a href="https://github.com/24601"><img src="https://avatars.githubusercontent.com/u/1157207?v=4&s=48" width="48" height="48" alt="24601" title="24601"/></a>
<a href="https://github.com/ameno-"><img src="https://avatars.githubusercontent.com/u/2416135?v=4&s=48" width="48" height="48" alt="ameno-" title="ameno-"/></a> <a href="https://github.com/bonald"><img src="https://avatars.githubusercontent.com/u/12394874?v=4&s=48" width="48" height="48" alt="bonald" title="bonald"/></a> <a href="https://github.com/bravostation"><img src="https://avatars.githubusercontent.com/u/257991910?v=4&s=48" width="48" height="48" alt="bravostation" title="bravostation"/></a> <a href="https://github.com/search?q=Chris%20Taylor"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Chris Taylor" title="Chris Taylor"/></a> <a href="https://github.com/search?q=damaozi"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="damaozi" title="damaozi"/></a> <a href="https://github.com/dguido"><img src="https://avatars.githubusercontent.com/u/294844?v=4&s=48" width="48" height="48" alt="dguido" title="dguido"/></a> <a href="https://github.com/djangonavarro220"><img src="https://avatars.githubusercontent.com/u/251162586?v=4&s=48" width="48" height="48" alt="Django Navarro" title="Django Navarro"/></a> <a href="https://github.com/evalexpr"><img src="https://avatars.githubusercontent.com/u/23485511?v=4&s=48" width="48" height="48" alt="evalexpr" title="evalexpr"/></a> <a href="https://github.com/henrino3"><img src="https://avatars.githubusercontent.com/u/4260288?v=4&s=48" width="48" height="48" alt="henrino3" title="henrino3"/></a> <a href="https://github.com/humanwritten"><img src="https://avatars.githubusercontent.com/u/206531610?v=4&s=48" width="48" height="48" alt="humanwritten" title="humanwritten"/></a>
<a href="https://github.com/j2h4u"><img src="https://avatars.githubusercontent.com/u/39818683?v=4&s=48" width="48" height="48" alt="j2h4u" title="j2h4u"/></a> <a href="https://github.com/larlyssa"><img src="https://avatars.githubusercontent.com/u/13128869?v=4&s=48" width="48" height="48" alt="larlyssa" title="larlyssa"/></a> <a href="https://github.com/liuxiaopai-ai"><img src="https://avatars.githubusercontent.com/u/73659136?v=4&s=48" width="48" height="48" alt="liuxiaopai-ai" title="liuxiaopai-ai"/></a> <a href="https://github.com/odysseus0"><img src="https://avatars.githubusercontent.com/u/8635094?v=4&s=48" width="48" height="48" alt="odysseus0" title="odysseus0"/></a> <a href="https://github.com/oswalpalash"><img src="https://avatars.githubusercontent.com/u/6431196?v=4&s=48" width="48" height="48" alt="oswalpalash" title="oswalpalash"/></a> <a href="https://github.com/pcty-nextgen-service-account"><img src="https://avatars.githubusercontent.com/u/112553441?v=4&s=48" width="48" height="48" alt="pcty-nextgen-service-account" title="pcty-nextgen-service-account"/></a> <a href="https://github.com/pi0"><img src="https://avatars.githubusercontent.com/u/5158436?v=4&s=48" width="48" height="48" alt="pi0" title="pi0"/></a> <a href="https://github.com/rmorse"><img src="https://avatars.githubusercontent.com/u/853547?v=4&s=48" width="48" height="48" alt="rmorse" title="rmorse"/></a> <a href="https://github.com/search?q=Roopak%20Nijhara"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Roopak Nijhara" title="Roopak Nijhara"/></a> <a href="https://github.com/Syhids"><img src="https://avatars.githubusercontent.com/u/671202?v=4&s=48" width="48" height="48" alt="Syhids" title="Syhids"/></a>
<a href="https://github.com/tmchow"><img src="https://avatars.githubusercontent.com/u/517103?v=4&s=48" width="48" height="48" alt="tmchow" title="tmchow"/></a> <a href="https://github.com/search?q=Ubuntu"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ubuntu" title="Ubuntu"/></a> <a href="https://github.com/search?q=xiaose"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="xiaose" title="xiaose"/></a> <a href="https://github.com/search?q=Aaron%20Konyer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Aaron Konyer" title="Aaron Konyer"/></a> <a href="https://github.com/aaronveklabs"><img src="https://avatars.githubusercontent.com/u/225997828?v=4&s=48" width="48" height="48" alt="aaronveklabs" title="aaronveklabs"/></a> <a href="https://github.com/akramcodez"><img src="https://avatars.githubusercontent.com/u/179671552?v=4&s=48" width="48" height="48" alt="akramcodez" title="akramcodez"/></a> <a href="https://github.com/aldoeliacim"><img src="https://avatars.githubusercontent.com/u/17973757?v=4&s=48" width="48" height="48" alt="aldoeliacim" title="aldoeliacim"/></a> <a href="https://github.com/andreabadesso"><img src="https://avatars.githubusercontent.com/u/3586068?v=4&s=48" width="48" height="48" alt="andreabadesso" title="andreabadesso"/></a> <a href="https://github.com/search?q=Andrii"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Andrii" title="Andrii"/></a> <a href="https://github.com/BinaryMuse"><img src="https://avatars.githubusercontent.com/u/189606?v=4&s=48" width="48" height="48" alt="BinaryMuse" title="BinaryMuse"/></a>
<a href="https://github.com/bqcfjwhz85-arch"><img src="https://avatars.githubusercontent.com/u/239267175?v=4&s=48" width="48" height="48" alt="bqcfjwhz85-arch" title="bqcfjwhz85-arch"/></a> <a href="https://github.com/cash-echo-bot"><img src="https://avatars.githubusercontent.com/u/252747386?v=4&s=48" width="48" height="48" alt="cash-echo-bot" title="cash-echo-bot"/></a> <a href="https://github.com/search?q=Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawd" title="Clawd"/></a> <a href="https://github.com/search?q=ClawdFx"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ClawdFx" title="ClawdFx"/></a> <a href="https://github.com/danballance"><img src="https://avatars.githubusercontent.com/u/13839912?v=4&s=48" width="48" height="48" alt="danballance" title="danballance"/></a> <a href="https://github.com/danielcadenhead"><img src="https://avatars.githubusercontent.com/u/195258443?v=4&s=48" width="48" height="48" alt="danielcadenhead" title="danielcadenhead"/></a> <a href="https://github.com/Elarwei001"><img src="https://avatars.githubusercontent.com/u/168552401?v=4&s=48" width="48" height="48" alt="Elarwei001" title="Elarwei001"/></a> <a href="https://github.com/EnzeD"><img src="https://avatars.githubusercontent.com/u/9866900?v=4&s=48" width="48" height="48" alt="EnzeD" title="EnzeD"/></a> <a href="https://github.com/erik-agens"><img src="https://avatars.githubusercontent.com/u/80908960?v=4&s=48" width="48" height="48" alt="erik-agens" title="erik-agens"/></a> <a href="https://github.com/Evizero"><img src="https://avatars.githubusercontent.com/u/10854026?v=4&s=48" width="48" height="48" alt="Evizero" title="Evizero"/></a>
<a href="https://github.com/fcatuhe"><img src="https://avatars.githubusercontent.com/u/17382215?v=4&s=48" width="48" height="48" alt="fcatuhe" title="fcatuhe"/></a> <a href="https://github.com/gildo"><img src="https://avatars.githubusercontent.com/u/133645?v=4&s=48" width="48" height="48" alt="gildo" title="gildo"/></a> <a href="https://github.com/hclsys"><img src="https://avatars.githubusercontent.com/u/7755017?v=4&s=48" width="48" height="48" alt="hclsys" title="hclsys"/></a> <a href="https://github.com/itsjaydesu"><img src="https://avatars.githubusercontent.com/u/220390?v=4&s=48" width="48" height="48" alt="itsjaydesu" title="itsjaydesu"/></a> <a href="https://github.com/ivancasco"><img src="https://avatars.githubusercontent.com/u/2452858?v=4&s=48" width="48" height="48" alt="ivancasco" title="ivancasco"/></a> <a href="https://github.com/ivanrvpereira"><img src="https://avatars.githubusercontent.com/u/183991?v=4&s=48" width="48" height="48" alt="ivanrvpereira" title="ivanrvpereira"/></a> <a href="https://github.com/search?q=Jarvis"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis" title="Jarvis"/></a> <a href="https://github.com/jayhickey"><img src="https://avatars.githubusercontent.com/u/1676460?v=4&s=48" width="48" height="48" alt="jayhickey" title="jayhickey"/></a> <a href="https://github.com/jeffersonwarrior"><img src="https://avatars.githubusercontent.com/u/89030989?v=4&s=48" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a> <a href="https://github.com/search?q=jeffersonwarrior"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="jeffersonwarrior" title="jeffersonwarrior"/></a>
<a href="https://github.com/jverdi"><img src="https://avatars.githubusercontent.com/u/345050?v=4&s=48" width="48" height="48" alt="jverdi" title="jverdi"/></a> <a href="https://github.com/longmaba"><img src="https://avatars.githubusercontent.com/u/9361500?v=4&s=48" width="48" height="48" alt="longmaba" title="longmaba"/></a> <a href="https://github.com/search?q=Marco%20Marandiz"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Marco Marandiz" title="Marco Marandiz"/></a> <a href="https://github.com/MarvinCui"><img src="https://avatars.githubusercontent.com/u/130876763?v=4&s=48" width="48" height="48" alt="MarvinCui" title="MarvinCui"/></a> <a href="https://github.com/mattezell"><img src="https://avatars.githubusercontent.com/u/361409?v=4&s=48" width="48" height="48" alt="mattezell" title="mattezell"/></a> <a href="https://github.com/mjrussell"><img src="https://avatars.githubusercontent.com/u/1641895?v=4&s=48" width="48" height="48" alt="mjrussell" title="mjrussell"/></a> <a href="https://github.com/odnxe"><img src="https://avatars.githubusercontent.com/u/403141?v=4&s=48" width="48" height="48" alt="odnxe" title="odnxe"/></a> <a href="https://github.com/optimikelabs"><img src="https://avatars.githubusercontent.com/u/31423109?v=4&s=48" width="48" height="48" alt="optimikelabs" title="optimikelabs"/></a> <a href="https://github.com/p6l-richard"><img src="https://avatars.githubusercontent.com/u/18185649?v=4&s=48" width="48" height="48" alt="p6l-richard" title="p6l-richard"/></a> <a href="https://github.com/philipp-spiess"><img src="https://avatars.githubusercontent.com/u/458591?v=4&s=48" width="48" height="48" alt="philipp-spiess" title="philipp-spiess"/></a>
<a href="https://github.com/search?q=Pocket%20Clawd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Pocket Clawd" title="Pocket Clawd"/></a> <a href="https://github.com/RayBB"><img src="https://avatars.githubusercontent.com/u/921217?v=4&s=48" width="48" height="48" alt="RayBB" title="RayBB"/></a> <a href="https://github.com/robaxelsen"><img src="https://avatars.githubusercontent.com/u/13132899?v=4&s=48" width="48" height="48" alt="robaxelsen" title="robaxelsen"/></a> <a href="https://github.com/search?q=Sash%20Catanzarite"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Sash Catanzarite" title="Sash Catanzarite"/></a> <a href="https://github.com/Suksham-sharma"><img src="https://avatars.githubusercontent.com/u/94667656?v=4&s=48" width="48" height="48" alt="Suksham-sharma" title="Suksham-sharma"/></a> <a href="https://github.com/T5-AndyML"><img src="https://avatars.githubusercontent.com/u/22801233?v=4&s=48" width="48" height="48" alt="T5-AndyML" title="T5-AndyML"/></a> <a href="https://github.com/thejhinvirtuoso"><img src="https://avatars.githubusercontent.com/u/258521837?v=4&s=48" width="48" height="48" alt="thejhinvirtuoso" title="thejhinvirtuoso"/></a> <a href="https://github.com/travisp"><img src="https://avatars.githubusercontent.com/u/165698?v=4&s=48" width="48" height="48" alt="travisp" title="travisp"/></a> <a href="https://github.com/search?q=VAC"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="VAC" title="VAC"/></a> <a href="https://github.com/search?q=william%20arzt"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="william arzt" title="william arzt"/></a>
<a href="https://github.com/yudshj"><img src="https://avatars.githubusercontent.com/u/16971372?v=4&s=48" width="48" height="48" alt="yudshj" title="yudshj"/></a> <a href="https://github.com/zknicker"><img src="https://avatars.githubusercontent.com/u/1164085?v=4&s=48" width="48" height="48" alt="zknicker" title="zknicker"/></a> <a href="https://github.com/0oAstro"><img src="https://avatars.githubusercontent.com/u/79555780?v=4&s=48" width="48" height="48" alt="0oAstro" title="0oAstro"/></a> <a href="https://github.com/Abdul535"><img src="https://avatars.githubusercontent.com/u/54276938?v=4&s=48" width="48" height="48" alt="Abdul535" title="Abdul535"/></a> <a href="https://github.com/abhaymundhara"><img src="https://avatars.githubusercontent.com/u/62872231?v=4&s=48" width="48" height="48" alt="abhaymundhara" title="abhaymundhara"/></a> <a href="https://github.com/aduk059"><img src="https://avatars.githubusercontent.com/u/257603478?v=4&s=48" width="48" height="48" alt="aduk059" title="aduk059"/></a> <a href="https://github.com/aisling404"><img src="https://avatars.githubusercontent.com/u/211950534?v=4&s=48" width="48" height="48" alt="aisling404" title="aisling404"/></a> <a href="https://github.com/search?q=alejandro%20maza"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="alejandro maza" title="alejandro maza"/></a> <a href="https://github.com/Alex-Alaniz"><img src="https://avatars.githubusercontent.com/u/88956822?v=4&s=48" width="48" height="48" alt="Alex-Alaniz" title="Alex-Alaniz"/></a> <a href="https://github.com/alexanderatallah"><img src="https://avatars.githubusercontent.com/u/1011391?v=4&s=48" width="48" height="48" alt="alexanderatallah" title="alexanderatallah"/></a>
<a href="https://github.com/alexstyl"><img src="https://avatars.githubusercontent.com/u/1665273?v=4&s=48" width="48" height="48" alt="alexstyl" title="alexstyl"/></a> <a href="https://github.com/AlexZhangji"><img src="https://avatars.githubusercontent.com/u/3280924?v=4&s=48" width="48" height="48" alt="AlexZhangji" title="AlexZhangji"/></a> <a href="https://github.com/andrewting19"><img src="https://avatars.githubusercontent.com/u/10536704?v=4&s=48" width="48" height="48" alt="andrewting19" title="andrewting19"/></a> <a href="https://github.com/anpoirier"><img src="https://avatars.githubusercontent.com/u/1245729?v=4&s=48" width="48" height="48" alt="anpoirier" title="anpoirier"/></a> <a href="https://github.com/araa47"><img src="https://avatars.githubusercontent.com/u/22760261?v=4&s=48" width="48" height="48" alt="araa47" title="araa47"/></a> <a href="https://github.com/arthyn"><img src="https://avatars.githubusercontent.com/u/5466421?v=4&s=48" width="48" height="48" alt="arthyn" title="arthyn"/></a> <a href="https://github.com/Asleep123"><img src="https://avatars.githubusercontent.com/u/122379135?v=4&s=48" width="48" height="48" alt="Asleep123" title="Asleep123"/></a> <a href="https://github.com/search?q=Ayush%20Ojha"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Ayush Ojha" title="Ayush Ojha"/></a> <a href="https://github.com/Ayush10"><img src="https://avatars.githubusercontent.com/u/7945279?v=4&s=48" width="48" height="48" alt="Ayush10" title="Ayush10"/></a> <a href="https://github.com/bguidolim"><img src="https://avatars.githubusercontent.com/u/987360?v=4&s=48" width="48" height="48" alt="bguidolim" title="bguidolim"/></a>
<a href="https://github.com/bolismauro"><img src="https://avatars.githubusercontent.com/u/771999?v=4&s=48" width="48" height="48" alt="bolismauro" title="bolismauro"/></a> <a href="https://github.com/caelum0x"><img src="https://avatars.githubusercontent.com/u/130079063?v=4&s=48" width="48" height="48" alt="caelum0x" title="caelum0x"/></a> <a href="https://github.com/championswimmer"><img src="https://avatars.githubusercontent.com/u/1327050?v=4&s=48" width="48" height="48" alt="championswimmer" title="championswimmer"/></a> <a href="https://github.com/chenyuan99"><img src="https://avatars.githubusercontent.com/u/25518100?v=4&s=48" width="48" height="48" alt="chenyuan99" title="chenyuan99"/></a> <a href="https://github.com/Chloe-VP"><img src="https://avatars.githubusercontent.com/u/257371598?v=4&s=48" width="48" height="48" alt="Chloe-VP" title="Chloe-VP"/></a> <a href="https://github.com/search?q=Claude%20Code"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Claude Code" title="Claude Code"/></a> <a href="https://github.com/search?q=Clawdbot%20Maintainers"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Clawdbot Maintainers" title="Clawdbot Maintainers"/></a> <a href="https://github.com/conhecendoia"><img src="https://avatars.githubusercontent.com/u/82890727?v=4&s=48" width="48" height="48" alt="conhecendoia" title="conhecendoia"/></a> <a href="https://github.com/dasilva333"><img src="https://avatars.githubusercontent.com/u/947827?v=4&s=48" width="48" height="48" alt="dasilva333" title="dasilva333"/></a> <a href="https://github.com/David-Marsh-Photo"><img src="https://avatars.githubusercontent.com/u/228404527?v=4&s=48" width="48" height="48" alt="David-Marsh-Photo" title="David-Marsh-Photo"/></a>
<a href="https://github.com/deepsoumya617"><img src="https://avatars.githubusercontent.com/u/80877391?v=4&s=48" width="48" height="48" alt="deepsoumya617" title="deepsoumya617"/></a> <a href="https://github.com/search?q=Developer"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Developer" title="Developer"/></a> <a href="https://github.com/search?q=Dimitrios%20Ploutarchos"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Dimitrios Ploutarchos" title="Dimitrios Ploutarchos"/></a> <a href="https://github.com/search?q=Drake%20Thomsen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Drake Thomsen" title="Drake Thomsen"/></a> <a href="https://github.com/dvrshil"><img src="https://avatars.githubusercontent.com/u/81693876?v=4&s=48" width="48" height="48" alt="dvrshil" title="dvrshil"/></a> <a href="https://github.com/dxd5001"><img src="https://avatars.githubusercontent.com/u/1886046?v=4&s=48" width="48" height="48" alt="dxd5001" title="dxd5001"/></a> <a href="https://github.com/dylanneve1"><img src="https://avatars.githubusercontent.com/u/31746704?v=4&s=48" width="48" height="48" alt="dylanneve1" title="dylanneve1"/></a> <a href="https://github.com/search?q=Felix%20Krause"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Felix Krause" title="Felix Krause"/></a> <a href="https://github.com/foeken"><img src="https://avatars.githubusercontent.com/u/13864?v=4&s=48" width="48" height="48" alt="foeken" title="foeken"/></a> <a href="https://github.com/frankekn"><img src="https://avatars.githubusercontent.com/u/4488090?v=4&s=48" width="48" height="48" alt="frankekn" title="frankekn"/></a>
<a href="https://github.com/fredheir"><img src="https://avatars.githubusercontent.com/u/3304869?v=4&s=48" width="48" height="48" alt="fredheir" title="fredheir"/></a> <a href="https://github.com/Fronut"><img src="https://avatars.githubusercontent.com/u/165925262?v=4&s=48" width="48" height="48" alt="Fronut" title="Fronut"/></a> <a href="https://github.com/search?q=ganghyun%20kim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ganghyun kim" title="ganghyun kim"/></a> <a href="https://github.com/grrowl"><img src="https://avatars.githubusercontent.com/u/907140?v=4&s=48" width="48" height="48" alt="grrowl" title="grrowl"/></a> <a href="https://github.com/gtsifrikas"><img src="https://avatars.githubusercontent.com/u/8904378?v=4&s=48" width="48" height="48" alt="gtsifrikas" title="gtsifrikas"/></a> <a href="https://github.com/HassanFleyah"><img src="https://avatars.githubusercontent.com/u/228002017?v=4&s=48" width="48" height="48" alt="HassanFleyah" title="HassanFleyah"/></a> <a href="https://github.com/HazAT"><img src="https://avatars.githubusercontent.com/u/363802?v=4&s=48" width="48" height="48" alt="HazAT" title="HazAT"/></a> <a href="https://github.com/hrdwdmrbl"><img src="https://avatars.githubusercontent.com/u/554881?v=4&s=48" width="48" height="48" alt="hrdwdmrbl" title="hrdwdmrbl"/></a> <a href="https://github.com/hugobarauna"><img src="https://avatars.githubusercontent.com/u/2719?v=4&s=48" width="48" height="48" alt="hugobarauna" title="hugobarauna"/></a> <a href="https://github.com/iamEvanYT"><img src="https://avatars.githubusercontent.com/u/47493765?v=4&s=48" width="48" height="48" alt="iamEvanYT" title="iamEvanYT"/></a>
<a href="https://github.com/ichbinlucaskim"><img src="https://avatars.githubusercontent.com/u/125564751?v=4&s=48" width="48" height="48" alt="ichbinlucaskim" title="ichbinlucaskim"/></a> <a href="https://github.com/search?q=Jamie%20Openshaw"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jamie Openshaw" title="Jamie Openshaw"/></a> <a href="https://github.com/search?q=Jane"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jane" title="Jane"/></a> <a href="https://github.com/search?q=Jarvis%20Deploy"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jarvis Deploy" title="Jarvis Deploy"/></a> <a href="https://github.com/search?q=Jefferson%20Nunn"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Jefferson Nunn" title="Jefferson Nunn"/></a> <a href="https://github.com/jogi47"><img src="https://avatars.githubusercontent.com/u/1710139?v=4&s=48" width="48" height="48" alt="jogi47" title="jogi47"/></a> <a href="https://github.com/kentaro"><img src="https://avatars.githubusercontent.com/u/3458?v=4&s=48" width="48" height="48" alt="kentaro" title="kentaro"/></a> <a href="https://github.com/search?q=Kevin%20Lin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Kevin Lin" title="Kevin Lin"/></a> <a href="https://github.com/kira-ariaki"><img src="https://avatars.githubusercontent.com/u/257352493?v=4&s=48" width="48" height="48" alt="kira-ariaki" title="kira-ariaki"/></a> <a href="https://github.com/kitze"><img src="https://avatars.githubusercontent.com/u/1160594?v=4&s=48" width="48" height="48" alt="kitze" title="kitze"/></a>
<a href="https://github.com/Kiwitwitter"><img src="https://avatars.githubusercontent.com/u/25277769?v=4&s=48" width="48" height="48" alt="Kiwitwitter" title="Kiwitwitter"/></a> <a href="https://github.com/kossoy"><img src="https://avatars.githubusercontent.com/u/51094?v=4&s=48" width="48" height="48" alt="kossoy" title="kossoy"/></a> <a href="https://github.com/levifig"><img src="https://avatars.githubusercontent.com/u/1605?v=4&s=48" width="48" height="48" alt="levifig" title="levifig"/></a> <a href="https://github.com/liuy"><img src="https://avatars.githubusercontent.com/u/1192888?v=4&s=48" width="48" height="48" alt="liuy" title="liuy"/></a> <a href="https://github.com/search?q=Lloyd"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Lloyd" title="Lloyd"/></a> <a href="https://github.com/loganaden"><img src="https://avatars.githubusercontent.com/u/1688420?v=4&s=48" width="48" height="48" alt="loganaden" title="loganaden"/></a> <a href="https://github.com/longjos"><img src="https://avatars.githubusercontent.com/u/740160?v=4&s=48" width="48" height="48" alt="longjos" title="longjos"/></a> <a href="https://github.com/loukotal"><img src="https://avatars.githubusercontent.com/u/18210858?v=4&s=48" width="48" height="48" alt="loukotal" title="loukotal"/></a> <a href="https://github.com/search?q=mac%20mimi"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="mac mimi" title="mac mimi"/></a> <a href="https://github.com/markusbkoch"><img src="https://avatars.githubusercontent.com/u/34865315?v=4&s=48" width="48" height="48" alt="markusbkoch" title="markusbkoch"/></a>
<a href="https://github.com/martinpucik"><img src="https://avatars.githubusercontent.com/u/5503097?v=4&s=48" width="48" height="48" alt="martinpucik" title="martinpucik"/></a> <a href="https://github.com/search?q=Matt%20mini"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Matt mini" title="Matt mini"/></a> <a href="https://github.com/mertcicekci0"><img src="https://avatars.githubusercontent.com/u/179321902?v=4&s=48" width="48" height="48" alt="mertcicekci0" title="mertcicekci0"/></a> <a href="https://github.com/search?q=Miles"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Miles" title="Miles"/></a> <a href="https://github.com/search?q=minghinmatthewlam"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/mrdbstn"><img src="https://avatars.githubusercontent.com/u/58957632?v=4&s=48" width="48" height="48" alt="mrdbstn" title="mrdbstn"/></a> <a href="https://github.com/MSch"><img src="https://avatars.githubusercontent.com/u/7475?v=4&s=48" width="48" height="48" alt="MSch" title="MSch"/></a> <a href="https://github.com/search?q=mudrii"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="mudrii" title="mudrii"/></a> <a href="https://github.com/search?q=Mustafa%20Tag%20Eldeen"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mustafa Tag Eldeen" title="Mustafa Tag Eldeen"/></a> <a href="https://github.com/search?q=myfunc"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="myfunc" title="myfunc"/></a>
<a href="https://github.com/mylukin"><img src="https://avatars.githubusercontent.com/u/1021019?v=4&s=48" width="48" height="48" alt="mylukin" title="mylukin"/></a> <a href="https://github.com/nathanbosse"><img src="https://avatars.githubusercontent.com/u/4040669?v=4&s=48" width="48" height="48" alt="nathanbosse" title="nathanbosse"/></a> <a href="https://github.com/ndraiman"><img src="https://avatars.githubusercontent.com/u/12609607?v=4&s=48" width="48" height="48" alt="ndraiman" title="ndraiman"/></a> <a href="https://github.com/nexty5870"><img src="https://avatars.githubusercontent.com/u/3869659?v=4&s=48" width="48" height="48" alt="nexty5870" title="nexty5870"/></a> <a href="https://github.com/Noctivoro"><img src="https://avatars.githubusercontent.com/u/183974570?v=4&s=48" width="48" height="48" alt="Noctivoro" title="Noctivoro"/></a> <a href="https://github.com/Omar-Khaleel"><img src="https://avatars.githubusercontent.com/u/240748662?v=4&s=48" width="48" height="48" alt="Omar-Khaleel" title="Omar-Khaleel"/></a> <a href="https://github.com/ozgur-polat"><img src="https://avatars.githubusercontent.com/u/26483942?v=4&s=48" width="48" height="48" alt="ozgur-polat" title="ozgur-polat"/></a> <a href="https://github.com/search?q=pasogott"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pasogott" title="pasogott"/></a> <a href="https://github.com/search?q=plum-dawg"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="plum-dawg" title="plum-dawg"/></a> <a href="https://github.com/search?q=pookNast"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pookNast" title="pookNast"/></a>
<a href="https://github.com/ppamment"><img src="https://avatars.githubusercontent.com/u/2122919?v=4&s=48" width="48" height="48" alt="ppamment" title="ppamment"/></a> <a href="https://github.com/prathamdby"><img src="https://avatars.githubusercontent.com/u/134331217?v=4&s=48" width="48" height="48" alt="prathamdby" title="prathamdby"/></a> <a href="https://github.com/ptn1411"><img src="https://avatars.githubusercontent.com/u/57529765?v=4&s=48" width="48" height="48" alt="ptn1411" title="ptn1411"/></a> <a href="https://github.com/search?q=rafaelreis-r"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a> <a href="https://github.com/rafelbev"><img src="https://avatars.githubusercontent.com/u/467120?v=4&s=48" width="48" height="48" alt="rafelbev" title="rafelbev"/></a> <a href="https://github.com/reeltimeapps"><img src="https://avatars.githubusercontent.com/u/637338?v=4&s=48" width="48" height="48" alt="reeltimeapps" title="reeltimeapps"/></a> <a href="https://github.com/RLTCmpe"><img src="https://avatars.githubusercontent.com/u/10762242?v=4&s=48" width="48" height="48" alt="RLTCmpe" title="RLTCmpe"/></a> <a href="https://github.com/search?q=robhparker"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="robhparker" title="robhparker"/></a> <a href="https://github.com/rohansachinpatil"><img src="https://avatars.githubusercontent.com/u/172933149?v=4&s=48" width="48" height="48" alt="rohansachinpatil" title="rohansachinpatil"/></a> <a href="https://github.com/search?q=Rony%20Kelner"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rony Kelner" title="Rony Kelner"/></a>
<a href="https://github.com/ryancnelson"><img src="https://avatars.githubusercontent.com/u/347171?v=4&s=48" width="48" height="48" alt="ryancnelson" title="ryancnelson"/></a> <a href="https://github.com/search?q=Samrat%20Jha"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Samrat Jha" title="Samrat Jha"/></a> <a href="https://github.com/search?q=seans-openclawbot"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="seans-openclawbot" title="seans-openclawbot"/></a> <a href="https://github.com/senoldogann"><img src="https://avatars.githubusercontent.com/u/45736551?v=4&s=48" width="48" height="48" alt="senoldogann" title="senoldogann"/></a> <a href="https://github.com/Seredeep"><img src="https://avatars.githubusercontent.com/u/22802816?v=4&s=48" width="48" height="48" alt="Seredeep" title="Seredeep"/></a> <a href="https://github.com/sergical"><img src="https://avatars.githubusercontent.com/u/3760543?v=4&s=48" width="48" height="48" alt="sergical" title="sergical"/></a> <a href="https://github.com/search?q=shatner"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="shatner" title="shatner"/></a> <a href="https://github.com/shiv19"><img src="https://avatars.githubusercontent.com/u/9407019?v=4&s=48" width="48" height="48" alt="shiv19" title="shiv19"/></a> <a href="https://github.com/shiyuanhai"><img src="https://avatars.githubusercontent.com/u/1187370?v=4&s=48" width="48" height="48" alt="shiyuanhai" title="shiyuanhai"/></a> <a href="https://github.com/Shrinija17"><img src="https://avatars.githubusercontent.com/u/199155426?v=4&s=48" width="48" height="48" alt="Shrinija17" title="Shrinija17"/></a>
<a href="https://github.com/siraht"><img src="https://avatars.githubusercontent.com/u/73152895?v=4&s=48" width="48" height="48" alt="siraht" title="siraht"/></a> <a href="https://github.com/snopoke"><img src="https://avatars.githubusercontent.com/u/249606?v=4&s=48" width="48" height="48" alt="snopoke" title="snopoke"/></a> <a href="https://github.com/search?q=spiceoogway"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="spiceoogway" title="spiceoogway"/></a> <a href="https://github.com/stephenchen2025"><img src="https://avatars.githubusercontent.com/u/218387130?v=4&s=48" width="48" height="48" alt="stephenchen2025" title="stephenchen2025"/></a> <a href="https://github.com/search?q=succ985"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="succ985" title="succ985"/></a> <a href="https://github.com/Suvink"><img src="https://avatars.githubusercontent.com/u/10671497?v=4&s=48" width="48" height="48" alt="Suvink" title="Suvink"/></a> <a href="https://github.com/search?q=techboss"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="techboss" title="techboss"/></a> <a href="https://github.com/testingabc321"><img src="https://avatars.githubusercontent.com/u/8577388?v=4&s=48" width="48" height="48" alt="testingabc321" title="testingabc321"/></a> <a href="https://github.com/search?q=tewatia"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="tewatia" title="tewatia"/></a> <a href="https://github.com/search?q=The%20Admiral"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="The Admiral" title="The Admiral"/></a>
<a href="https://github.com/search?q=therealZpoint-bot"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="therealZpoint-bot" title="therealZpoint-bot"/></a> <a href="https://github.com/thesash"><img src="https://avatars.githubusercontent.com/u/1166151?v=4&s=48" width="48" height="48" alt="thesash" title="thesash"/></a> <a href="https://github.com/search?q=uos-status"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="uos-status" title="uos-status"/></a> <a href="https://github.com/vcastellm"><img src="https://avatars.githubusercontent.com/u/47026?v=4&s=48" width="48" height="48" alt="vcastellm" title="vcastellm"/></a> <a href="https://github.com/search?q=Vibe%20Kanban"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Vibe Kanban" title="Vibe Kanban"/></a> <a href="https://github.com/vincentkoc"><img src="https://avatars.githubusercontent.com/u/25068?v=4&s=48" width="48" height="48" alt="vincentkoc" title="vincentkoc"/></a> <a href="https://github.com/search?q=void"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="void" title="void"/></a> <a href="https://github.com/search?q=Vultr-Clawd%20Admin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Vultr-Clawd Admin" title="Vultr-Clawd Admin"/></a> <a href="https://github.com/search?q=Wimmie"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Wimmie" title="Wimmie"/></a> <a href="https://github.com/search?q=wolfred"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="wolfred" title="wolfred"/></a>
<a href="https://github.com/wstock"><img src="https://avatars.githubusercontent.com/u/1394687?v=4&s=48" width="48" height="48" alt="wstock" title="wstock"/></a> <a href="https://github.com/wytheme"><img src="https://avatars.githubusercontent.com/u/5009358?v=4&s=48" width="48" height="48" alt="wytheme" title="wytheme"/></a> <a href="https://github.com/YangHuang2280"><img src="https://avatars.githubusercontent.com/u/201681634?v=4&s=48" width="48" height="48" alt="YangHuang2280" title="YangHuang2280"/></a> <a href="https://github.com/yazinsai"><img src="https://avatars.githubusercontent.com/u/1846034?v=4&s=48" width="48" height="48" alt="yazinsai" title="yazinsai"/></a> <a href="https://github.com/yevhen"><img src="https://avatars.githubusercontent.com/u/107726?v=4&s=48" width="48" height="48" alt="yevhen" title="yevhen"/></a> <a href="https://github.com/YiWang24"><img src="https://avatars.githubusercontent.com/u/176262341?v=4&s=48" width="48" height="48" alt="YiWang24" title="YiWang24"/></a> <a href="https://github.com/search?q=ymat19"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ymat19" title="ymat19"/></a> <a href="https://github.com/search?q=Zach%20Knickerbocker"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Zach Knickerbocker" title="Zach Knickerbocker"/></a> <a href="https://github.com/zackerthescar"><img src="https://avatars.githubusercontent.com/u/38077284?v=4&s=48" width="48" height="48" alt="zackerthescar" title="zackerthescar"/></a> <a href="https://github.com/search?q=zhixian"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="zhixian" title="zhixian"/></a>
<a href="https://github.com/0xJonHoldsCrypto"><img src="https://avatars.githubusercontent.com/u/81202085?v=4&s=48" width="48" height="48" alt="0xJonHoldsCrypto" title="0xJonHoldsCrypto"/></a> <a href="https://github.com/aaronn"><img src="https://avatars.githubusercontent.com/u/1653630?v=4&s=48" width="48" height="48" alt="aaronn" title="aaronn"/></a> <a href="https://github.com/Alphonse-arianee"><img src="https://avatars.githubusercontent.com/u/254457365?v=4&s=48" width="48" height="48" alt="Alphonse-arianee" title="Alphonse-arianee"/></a> <a href="https://github.com/atalovesyou"><img src="https://avatars.githubusercontent.com/u/3534502?v=4&s=48" width="48" height="48" alt="atalovesyou" title="atalovesyou"/></a> <a href="https://github.com/search?q=Azade"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Azade" title="Azade"/></a> <a href="https://github.com/carlulsoe"><img src="https://avatars.githubusercontent.com/u/34673973?v=4&s=48" width="48" height="48" alt="carlulsoe" title="carlulsoe"/></a> <a href="https://github.com/search?q=ddyo"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="ddyo" title="ddyo"/></a> <a href="https://github.com/search?q=Erik"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Erik" title="Erik"/></a> <a href="https://github.com/jiulingyun"><img src="https://avatars.githubusercontent.com/u/126459548?v=4&s=48" width="48" height="48" alt="jiulingyun" title="jiulingyun"/></a> <a href="https://github.com/latitudeki5223"><img src="https://avatars.githubusercontent.com/u/119656367?v=4&s=48" width="48" height="48" alt="latitudeki5223" title="latitudeki5223"/></a>
<a href="https://github.com/search?q=Manuel%20Maly"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Manuel Maly" title="Manuel Maly"/></a> <a href="https://github.com/minghinmatthewlam"><img src="https://avatars.githubusercontent.com/u/14224566?v=4&s=48" width="48" height="48" alt="minghinmatthewlam" title="minghinmatthewlam"/></a> <a href="https://github.com/search?q=Mourad%20Boustani"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Mourad Boustani" title="Mourad Boustani"/></a> <a href="https://github.com/odrobnik"><img src="https://avatars.githubusercontent.com/u/333270?v=4&s=48" width="48" height="48" alt="odrobnik" title="odrobnik"/></a> <a href="https://github.com/pcty-nextgen-ios-builder"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="pcty-nextgen-ios-builder" title="pcty-nextgen-ios-builder"/></a> <a href="https://github.com/search?q=Quentin"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Quentin" title="Quentin"/></a> <a href="https://github.com/rafaelreis-r"><img src="https://avatars.githubusercontent.com/u/57492577?v=4&s=48" width="48" height="48" alt="rafaelreis-r" title="rafaelreis-r"/></a> <a href="https://github.com/search?q=Randy%20Torres"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Randy Torres" title="Randy Torres"/></a> <a href="https://github.com/rhjoh"><img src="https://avatars.githubusercontent.com/u/105699450?v=4&s=48" width="48" height="48" alt="rhjoh" title="rhjoh"/></a> <a href="https://github.com/search?q=Rolf%20Fredheim"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="Rolf Fredheim" title="Rolf Fredheim"/></a>
<a href="https://github.com/ronak-guliani"><img src="https://avatars.githubusercontent.com/u/23518228?v=4&s=48" width="48" height="48" alt="ronak-guliani" title="ronak-guliani"/></a> <a href="https://github.com/search?q=William%20Stock"><img src="assets/avatar-placeholder.svg" width="48" height="48" alt="William Stock" title="William Stock"/></a>
</p>

View File

@@ -1,27 +1,65 @@
# Security Policy
If you believe you've found a security issue in Moltbot, please report it privately.
If you believe you've found a security issue in OpenClaw, please report it privately.
## Reporting
- Email: `steipete@gmail.com`
- What to include: reproduction steps, impact assessment, and (if possible) a minimal PoC.
Report vulnerabilities directly to the repository where the issue lives:
- **Core CLI and gateway** — [openclaw/openclaw](https://github.com/openclaw/openclaw)
- **macOS desktop app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/macos)
- **iOS app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/ios)
- **Android app** — [openclaw/openclaw](https://github.com/openclaw/openclaw) (apps/android)
- **ClawHub** — [openclaw/clawhub](https://github.com/openclaw/clawhub)
- **Trust and threat model** — [openclaw/trust](https://github.com/openclaw/trust)
For issues that don't fit a specific repo, or if you're unsure, email **security@openclaw.ai** and we'll route it.
For full reporting instructions see our [Trust page](https://trust.openclaw.ai).
### Required in Reports
1. **Title**
2. **Severity Assessment**
3. **Impact**
4. **Affected Component**
5. **Technical Reproduction**
6. **Demonstrated Impact**
7. **Environment**
8. **Remediation Advice**
Reports without reproduction steps, demonstrated impact, and remediation advice will be deprioritized. Given the volume of AI-generated scanner findings, we must ensure we're receiving vetted reports from researchers who understand the issues.
## Security & Trust
**Jamieson O'Reilly** ([@theonejvo](https://twitter.com/theonejvo)) is Security & Trust at OpenClaw. Jamieson is the founder of [Dvuln](https://dvuln.com) and brings extensive experience in offensive security, penetration testing, and security program development.
## Bug Bounties
OpenClaw is a labor of love. There is no bug bounty program and no budget for paid reports. Please still disclose responsibly so we can fix issues quickly.
The best way to help the project right now is by sending PRs.
## Out of Scope
- Public Internet Exposure
- Using OpenClaw in ways that the docs recommend not to
- Prompt injection attacks
## Operational Guidance
For threat model + hardening guidance (including `moltbot security audit --deep` and `--fix`), see:
For threat model + hardening guidance (including `openclaw security audit --deep` and `--fix`), see:
- `https://docs.molt.bot/gateway/security`
- `https://docs.openclaw.ai/gateway/security`
### Web Interface Safety
Moltbot's web interface is intended for local use only. Do **not** bind it to the public internet; it is not hardened for public exposure.
OpenClaw's web interface is intended for local use only. Do **not** bind it to the public internet; it is not hardened for public exposure.
## Runtime Requirements
### Node.js Version
Moltbot requires **Node.js 22.12.0 or later** (LTS). This version includes important security patches:
OpenClaw requires **Node.js 22.12.0 or later** (LTS). This version includes important security patches:
- CVE-2025-59466: async_hooks DoS vulnerability
- CVE-2026-21636: Permission model bypass vulnerability
@@ -34,7 +72,7 @@ node --version # Should be v22.12.0 or later
### Docker Security
When running Moltbot in Docker:
When running OpenClaw in Docker:
1. The official image runs as a non-root user (`node`) for reduced attack surface
2. Use `--read-only` flag when possible for additional filesystem protection
@@ -44,8 +82,8 @@ Example secure Docker run:
```bash
docker run --read-only --cap-drop=ALL \
-v moltbot-data:/app/data \
moltbot/moltbot:latest
-v openclaw-data:/app/data \
openclaw/openclaw:latest
```
## Security Scanning

View File

@@ -1,5 +1,5 @@
{
"originHash" : "c0677e232394b5f6b0191b6dbb5bae553d55264f65ae725cd03a8ffdfda9cdd3",
"originHash" : "24a723309d7a0039d3df3051106f77ac1ed7068a02508e3a6804e41d757e6c72",
"pins" : [
{
"identity" : "commander",
@@ -10,6 +10,24 @@
"version" : "0.2.1"
}
},
{
"identity" : "elevenlabskit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/steipete/ElevenLabsKit",
"state" : {
"revision" : "7e3c948d8340abe3977014f3de020edf221e9269",
"version" : "0.1.0"
}
},
{
"identity" : "swift-concurrency-extras",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-concurrency-extras",
"state" : {
"revision" : "5a3825302b1a0d744183200915a47b508c828e6f",
"version" : "1.3.2"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
@@ -27,6 +45,24 @@
"revision" : "399f76dcd91e4c688ca2301fa24a8cc6d9927211",
"version" : "0.99.0"
}
},
{
"identity" : "swiftui-math",
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/swiftui-math",
"state" : {
"revision" : "0b5c2cfaaec8d6193db206f675048eeb5ce95f71",
"version" : "0.1.0"
}
},
{
"identity" : "textual",
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/textual",
"state" : {
"revision" : "5b06b811c0f5313b6b84bbef98c635a630638c38",
"version" : "0.3.1"
}
}
],
"version" : 3

View File

@@ -1,188 +1,156 @@
<?xml version="1.0" standalone="yes"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<title>Moltbot</title>
<title>OpenClaw</title>
<item>
<title>2026.1.24-1</title>
<pubDate>Sun, 25 Jan 2026 14:05:25 +0000</pubDate>
<link>https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml</link>
<sparkle:version>7952</sparkle:version>
<sparkle:shortVersionString>2026.1.24-1</sparkle:shortVersionString>
<title>2026.2.9</title>
<pubDate>Mon, 09 Feb 2026 13:23:25 -0600</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>9194</sparkle:version>
<sparkle:shortVersionString>2026.2.9</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>Moltbot 2026.1.24-1</h2>
<description><![CDATA[<h2>OpenClaw 2026.2.9</h2>
<h3>Added</h3>
<ul>
<li>iOS: alpha node app + setup-code onboarding. (#11756) Thanks @mbelinky.</li>
<li>Channels: comprehensive BlueBubbles and channel cleanup. (#11093) Thanks @tyler6204.</li>
<li>Plugins: device pairing + phone control plugins (Telegram <code>/pair</code>, iOS/Android node controls). (#11755) Thanks @mbelinky.</li>
<li>Tools: add Grok (xAI) as a <code>web_search</code> provider. (#12419) Thanks @tmchow.</li>
<li>Gateway: add agent management RPC methods for the web UI (<code>agents.create</code>, <code>agents.update</code>, <code>agents.delete</code>). (#11045) Thanks @advaitpaliwal.</li>
<li>Web UI: show a Compaction divider in chat history. (#11341) Thanks @Takhoffman.</li>
<li>Agents: include runtime shell in agent envelopes. (#1835) Thanks @Takhoffman.</li>
<li>Paths: add <code>OPENCLAW_HOME</code> for overriding the home directory used by internal path resolution. (#12091) Thanks @sebslight.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Packaging: include dist/shared output in npm tarball (fixes missing reasoning-tags import on install).</li>
<li>Telegram: harden quote parsing; preserve quote context; avoid QUOTE_TEXT_INVALID; avoid nested reply quote misclassification. (#12156) Thanks @rybnikov.</li>
<li>Telegram: recover proactive sends when stale topic thread IDs are used by retrying without <code>message_thread_id</code>. (#11620)</li>
<li>Telegram: render markdown spoilers with <code><tg-spoiler></code> HTML tags. (#11543) Thanks @ezhikkk.</li>
<li>Telegram: truncate command registration to 100 entries to avoid <code>BOT_COMMANDS_TOO_MUCH</code> failures on startup. (#12356) Thanks @arosstale.</li>
<li>Telegram: match DM <code>allowFrom</code> against sender user id (fallback to chat id) and clarify pairing logs. (#12779) Thanks @liuxiaopai-ai.</li>
<li>Onboarding: QuickStart now auto-installs shell completion (prompt only in Manual).</li>
<li>Auth: strip embedded line breaks from pasted API keys and tokens before storing/resolving credentials.</li>
<li>Web UI: make chat refresh smoothly scroll to the latest messages and suppress new-messages badge flash during manual refresh.</li>
<li>Tools/web_search: include provider-specific settings in the web search cache key, and pass <code>inlineCitations</code> for Grok. (#12419) Thanks @tmchow.</li>
<li>Tools/web_search: normalize direct Perplexity model IDs while keeping OpenRouter model IDs unchanged. (#12795) Thanks @cdorsey.</li>
<li>Model failover: treat HTTP 400 errors as failover-eligible, enabling automatic model fallback. (#1879) Thanks @orenyomtov.</li>
<li>Errors: prevent false positive context overflow detection when conversation mentions "context overflow" topic. (#2078) Thanks @sbking.</li>
<li>Gateway: no more post-compaction amnesia; injected transcript writes now preserve Pi session <code>parentId</code> chain so agents can remember again. (#12283) Thanks @Takhoffman.</li>
<li>Gateway: fix multi-agent sessions.usage discovery. (#11523) Thanks @Takhoffman.</li>
<li>Agents: recover from context overflow caused by oversized tool results (pre-emptive capping + fallback truncation). (#11579) Thanks @tyler6204.</li>
<li>Subagents/compaction: stabilize announce timing and preserve compaction metrics across retries. (#11664) Thanks @tyler6204.</li>
<li>Cron: share isolated announce flow and harden scheduling/delivery reliability. (#11641) Thanks @tyler6204.</li>
<li>Cron tool: recover flat params when LLM omits the <code>job</code> wrapper for add requests. (#12124) Thanks @tyler6204.</li>
<li>Gateway/CLI: when <code>gateway.bind=lan</code>, use a LAN IP for probe URLs and Control UI links. (#11448) Thanks @AnonO6.</li>
<li>Hooks: fix bundled hooks broken since 2026.2.2 (tsdown migration). (#9295) Thanks @patrickshao.</li>
<li>Routing: refresh bindings per message by loading config at route resolution so binding changes apply without restart. (#11372) Thanks @juanpablodlc.</li>
<li>Exec approvals: render forwarded commands in monospace for safer approval scanning. (#11937) Thanks @sebslight.</li>
<li>Config: clamp <code>maxTokens</code> to <code>contextWindow</code> to prevent invalid model configs. (#5516) Thanks @lailoo.</li>
<li>Thinking: allow xhigh for <code>github-copilot/gpt-5.2-codex</code> and <code>github-copilot/gpt-5.2</code>. (#11646) Thanks @LatencyTDH.</li>
<li>Discord: support forum/media thread-create starter messages, wire <code>message thread create --message</code>, and harden routing. (#10062) Thanks @jarvis89757.</li>
<li>Paths: structurally resolve <code>OPENCLAW_HOME</code>-derived home paths and fix Windows drive-letter handling in tool meta shortening. (#12125) Thanks @mcaxtr.</li>
<li>Memory: set Voyage embeddings <code>input_type</code> for improved retrieval. (#10818) Thanks @mcinteerj.</li>
<li>Memory/QMD: reuse default model cache across agents instead of re-downloading per agent. (#12114) Thanks @tyler6204.</li>
<li>Media understanding: recognize <code>.caf</code> audio attachments for transcription. (#10982) Thanks @succ985.</li>
<li>State dir: honor <code>OPENCLAW_STATE_DIR</code> for default device identity and canvas storage paths. (#4824) Thanks @kossoy.</li>
</ul>
<p><a href="https://github.com/moltbot/moltbot/blob/main/CHANGELOG.md">View full changelog</a></p>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/moltbot/moltbot/releases/download/v2026.1.24-1/Moltbot-2026.1.24-1.zip" length="12396699" type="application/octet-stream" sparkle:edSignature="VaEdWIgEJBrZLIp2UmigoQ6vaq4P/jNFXpHYXvXHD5MsATS0CqBl6ugyyxRq+/GbpUqmdgdlht4dTUVbLRw6BA=="/>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.2.9/OpenClaw-2026.2.9.zip" length="22872529" type="application/octet-stream" sparkle:edSignature="zvgwqlgqI7J5Gsi9VSULIQTMKqLiGE5ulC6NnRLKtOPphQsHZVdYSWm0E90+Yq8mG4lpsvbxQOSSPxpl43QTAw=="/>
</item>
<item>
<title>2026.1.24</title>
<pubDate>Sun, 25 Jan 2026 13:31:05 +0000</pubDate>
<link>https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml</link>
<sparkle:version>7944</sparkle:version>
<sparkle:shortVersionString>2026.1.24</sparkle:shortVersionString>
<title>2026.2.3</title>
<pubDate>Wed, 04 Feb 2026 17:47:10 -0800</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>8900</sparkle:version>
<sparkle:shortVersionString>2026.2.3</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>Moltbot 2026.1.24</h2>
<h3>Highlights</h3>
<ul>
<li>Providers: Ollama discovery + docs; Venice guide upgrades + cross-links. (#1606) Thanks @abhaymundhara. https://docs.molt.bot/providers/ollama https://docs.molt.bot/providers/venice</li>
<li>Channels: LINE plugin (Messaging API) with rich replies + quick replies. (#1630) Thanks @plum-dawg.</li>
<li>TTS: Edge fallback (keyless) + <code>/tts</code> auto modes. (#1668, #1667) Thanks @steipete, @sebslight. https://docs.molt.bot/tts</li>
<li>Exec approvals: approve in-chat via <code>/approve</code> across all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands</li>
<li>Telegram: DM topics as separate sessions + outbound link preview toggle. (#1597, #1700) Thanks @rohannagpal, @zerone0x. https://docs.molt.bot/channels/telegram</li>
</ul>
<description><![CDATA[<h2>OpenClaw 2026.2.3</h2>
<h3>Changes</h3>
<ul>
<li>Channels: add LINE plugin (Messaging API) with rich replies, quick replies, and plugin HTTP registry. (#1630) Thanks @plum-dawg.</li>
<li>TTS: add Edge TTS provider fallback, defaulting to keyless Edge with MP3 retry on format failures. (#1668) Thanks @steipete. https://docs.molt.bot/tts</li>
<li>TTS: add auto mode enum (off/always/inbound/tagged) with per-session <code>/tts</code> override. (#1667) Thanks @sebslight. https://docs.molt.bot/tts</li>
<li>Telegram: treat DM topics as separate sessions and keep DM history limits stable with thread suffixes. (#1597) Thanks @rohannagpal.</li>
<li>Telegram: add <code>channels.telegram.linkPreview</code> to toggle outbound link previews. (#1700) Thanks @zerone0x. https://docs.molt.bot/channels/telegram</li>
<li>Web search: add Brave freshness filter parameter for time-scoped results. (#1688) Thanks @JonUleis. https://docs.molt.bot/tools/web</li>
<li>UI: refresh Control UI dashboard design system (typography, colors, spacing). (#1786) Thanks @mousberg.</li>
<li>Exec approvals: forward approval prompts to chat with <code>/approve</code> for all channels (including plugins). (#1621) Thanks @czekaj. https://docs.molt.bot/tools/exec-approvals https://docs.molt.bot/tools/slash-commands</li>
<li>Gateway: expose config.patch in the gateway tool with safe partial updates + restart sentinel. (#1653) Thanks @Glucksberg.</li>
<li>Diagnostics: add diagnostic flags for targeted debug logs (config + env override). https://docs.molt.bot/diagnostics/flags</li>
<li>Docs: expand FAQ (migration, scheduling, concurrency, model recommendations, OpenAI subscription auth, Pi sizing, hackable install, docs SSL workaround).</li>
<li>Docs: add verbose installer troubleshooting guidance.</li>
<li>Docs: add macOS VM guide with local/hosted options + VPS/nodes guidance. (#1693) Thanks @f-trycua.</li>
<li>Docs: add Bedrock EC2 instance role setup + IAM steps. (#1625) Thanks @sergical. https://docs.molt.bot/bedrock</li>
<li>Docs: update Fly.io guide notes.</li>
<li>Dev: add prek pre-commit hooks + dependabot config for weekly updates. (#1720) Thanks @dguido.</li>
<li>Telegram: remove last <code>@ts-nocheck</code> from <code>bot-handlers.ts</code>, use Grammy types directly, deduplicate <code>StickerMetadata</code>. Zero <code>@ts-nocheck</code> remaining in <code>src/telegram/</code>. (#9206)</li>
<li>Telegram: remove <code>@ts-nocheck</code> from <code>bot-message.ts</code>, type deps via <code>Omit<BuildTelegramMessageContextParams></code>, widen <code>allMedia</code> to <code>TelegramMediaRef[]</code>. (#9180)</li>
<li>Telegram: remove <code>@ts-nocheck</code> from <code>bot.ts</code>, fix duplicate <code>bot.catch</code> error handler (Grammy overrides), remove dead reaction <code>message_thread_id</code> routing, harden sticker cache guard. (#9077)</li>
<li>Onboarding: add Cloudflare AI Gateway provider setup and docs. (#7914) Thanks @roerohan.</li>
<li>Onboarding: add Moonshot (.cn) auth choice and keep the China base URL when preserving defaults. (#7180) Thanks @waynelwz.</li>
<li>Docs: clarify tmux send-keys for TUI by splitting text and Enter. (#7737) Thanks @Wangnov.</li>
<li>Docs: mirror the landing page revamp for zh-CN (features, quickstart, docs directory, network model, credits). (#8994) Thanks @joshp123.</li>
<li>Messages: add per-channel and per-account responsePrefix overrides across channels. (#9001) Thanks @mudrii.</li>
<li>Cron: add announce delivery mode for isolated jobs (CLI + Control UI) and delivery mode config.</li>
<li>Cron: default isolated jobs to announce delivery; accept ISO 8601 <code>schedule.at</code> in tool inputs.</li>
<li>Cron: hard-migrate isolated jobs to announce/none delivery; drop legacy post-to-main/payload delivery fields and <code>atMs</code> inputs.</li>
<li>Cron: delete one-shot jobs after success by default; add <code>--keep-after-run</code> for CLI.</li>
<li>Cron: suppress messaging tools during announce delivery so summaries post consistently.</li>
<li>Cron: avoid duplicate deliveries when isolated runs send messages directly.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Web UI: fix config/debug layout overflow, scrolling, and code block sizing. (#1715) Thanks @saipreetham589.</li>
<li>Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent.</li>
<li>Web UI: clear stale disconnect banners on reconnect; allow form saves with unsupported schema paths but block missing schema. (#1707) Thanks @Glucksberg.</li>
<li>Web UI: hide internal <code>message_id</code> hints in chat bubbles.</li>
<li>Gateway: allow Control UI token-only auth to skip device pairing even when device identity is present (<code>gateway.controlUi.allowInsecureAuth</code>). (#1679) Thanks @steipete.</li>
<li>Matrix: decrypt E2EE media attachments with preflight size guard. (#1744) Thanks @araa47.</li>
<li>BlueBubbles: route phone-number targets to DMs, avoid leaking routing IDs, and auto-create missing DMs (Private API required). (#1751) Thanks @tyler6204. https://docs.molt.bot/channels/bluebubbles</li>
<li>BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing.</li>
<li>Signal: repair reaction sends (group/UUID targets + CLI author flags). (#1651) Thanks @vilkasdev.</li>
<li>Signal: add configurable signal-cli startup timeout + external daemon mode docs. (#1677) https://docs.molt.bot/channels/signal</li>
<li>Telegram: set fetch duplex="half" for uploads on Node 22 to avoid sendPhoto failures. (#1684) Thanks @commdata2338.</li>
<li>Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639)</li>
<li>Telegram: honor per-account proxy for outbound API calls. (#1774) Thanks @radek-paclt.</li>
<li>Telegram: fall back to text when voice notes are blocked by privacy settings. (#1725) Thanks @foeken.</li>
<li>Voice Call: return stream TwiML for outbound conversation calls on initial Twilio webhook. (#1634)</li>
<li>Voice Call: serialize Twilio TTS playback and cancel on barge-in to prevent overlap. (#1713) Thanks @dguido.</li>
<li>Google Chat: tighten email allowlist matching, typing cleanup, media caps, and onboarding/docs/tests. (#1635) Thanks @iHildy.</li>
<li>Google Chat: normalize space targets without double <code>spaces/</code> prefix.</li>
<li>Agents: auto-compact on context overflow prompt errors before failing. (#1627) Thanks @rodrigouroz.</li>
<li>Agents: use the active auth profile for auto-compaction recovery.</li>
<li>Media understanding: skip image understanding when the primary model already supports vision. (#1747) Thanks @tyler6204.</li>
<li>Models: default missing custom provider fields so minimal configs are accepted.</li>
<li>Messaging: keep newline chunking safe for fenced markdown blocks across channels.</li>
<li>TUI: reload history after gateway reconnect to restore session state. (#1663)</li>
<li>Heartbeat: normalize target identifiers for consistent routing.</li>
<li>Exec: keep approvals for elevated ask unless full mode. (#1616) Thanks @ivancasco.</li>
<li>Exec: treat Windows platform labels as Windows for node shell selection. (#1760) Thanks @ymat19.</li>
<li>Gateway: include inline config env vars in service install environments. (#1735) Thanks @Seredeep.</li>
<li>Gateway: skip Tailscale DNS probing when tailscale.mode is off. (#1671)</li>
<li>Gateway: reduce log noise for late invokes + remote node probes; debounce skills refresh. (#1607) Thanks @petter-b.</li>
<li>Gateway: clarify Control UI/WebChat auth error hints for missing tokens. (#1690)</li>
<li>Gateway: listen on IPv6 loopback when bound to 127.0.0.1 so localhost webhooks work.</li>
<li>Gateway: store lock files in the temp directory to avoid stale locks on persistent volumes. (#1676)</li>
<li>macOS: default direct-transport <code>ws://</code> URLs to port 18789; document <code>gateway.remote.transport</code>. (#1603) Thanks @ngutman.</li>
<li>Tests: cap Vitest workers on CI macOS to reduce timeouts. (#1597) Thanks @rohannagpal.</li>
<li>Tests: avoid fake-timer dependency in embedded runner stream mock to reduce CI flakes. (#1597) Thanks @rohannagpal.</li>
<li>Tests: increase embedded runner ordering test timeout to reduce CI flakes. (#1597) Thanks @rohannagpal.</li>
<li>Heartbeat: allow explicit accountId routing for multi-account channels. (#8702) Thanks @lsh411.</li>
<li>TUI/Gateway: handle non-streaming finals, refresh history for non-local chat runs, and avoid event gap warnings for targeted tool streams. (#8432) Thanks @gumadeiras.</li>
<li>Shell completion: auto-detect and migrate slow dynamic patterns to cached files for faster terminal startup; add completion health checks to doctor/update/onboard.</li>
<li>Telegram: honor session model overrides in inline model selection. (#8193) Thanks @gildo.</li>
<li>Web UI: fix agent model selection saves for default/non-default agents and wrap long workspace paths. Thanks @Takhoffman.</li>
<li>Web UI: resolve header logo path when <code>gateway.controlUi.basePath</code> is set. (#7178) Thanks @Yeom-JinHo.</li>
<li>Web UI: apply button styling to the new-messages indicator.</li>
<li>Security: keep untrusted channel metadata out of system prompts (Slack/Discord). Thanks @KonstantinMirin.</li>
<li>Security: enforce sandboxed media paths for message tool attachments. (#9182) Thanks @victormier.</li>
<li>Security: require explicit credentials for gateway URL overrides to prevent credential leakage. (#8113) Thanks @victormier.</li>
<li>Security: gate <code>whatsapp_login</code> tool to owner senders and default-deny non-owner contexts. (#8768) Thanks @victormier.</li>
<li>Voice call: harden webhook verification with host allowlists/proxy trust and keep ngrok loopback bypass.</li>
<li>Voice call: add regression coverage for anonymous inbound caller IDs with allowlist policy. (#8104) Thanks @victormier.</li>
<li>Cron: accept epoch timestamps and 0ms durations in CLI <code>--at</code> parsing.</li>
<li>Cron: reload store data when the store file is recreated or mtime changes.</li>
<li>Cron: deliver announce runs directly, honor delivery mode, and respect wakeMode for summaries. (#8540) Thanks @tyler6204.</li>
<li>Telegram: include forward_from_chat metadata in forwarded messages and harden cron delivery target checks. (#8392) Thanks @Glucksberg.</li>
<li>macOS: fix cron payload summary rendering and ISO 8601 formatter concurrency safety.</li>
</ul>
<p><a href="https://github.com/moltbot/moltbot/blob/main/CHANGELOG.md">View full changelog</a></p>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/moltbot/moltbot/releases/download/v2026.1.24/Moltbot-2026.1.24.zip" length="12396700" type="application/octet-stream" sparkle:edSignature="u+XzKD3YwV8s79gIr7LK4OtDCcmp/b+cjNC6SHav3/1CVJegh02SsBKatrampox32XGx8P2+8c/+fHV+qpkHCA=="/>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.2.3/OpenClaw-2026.2.3.zip" length="22530161" type="application/octet-stream" sparkle:edSignature="7eHUaQC6cx87HWbcaPh9T437+LqfE9VtQBf4p9JBjIyBrqGYxxp9KPvI5unEjg55j9j2djCXhseSMeyyRmvYBg=="/>
</item>
<item>
<title>2026.1.23</title>
<pubDate>Sat, 24 Jan 2026 13:02:18 +0000</pubDate>
<link>https://raw.githubusercontent.com/moltbot/moltbot/main/appcast.xml</link>
<sparkle:version>7750</sparkle:version>
<sparkle:shortVersionString>2026.1.23</sparkle:shortVersionString>
<title>2026.2.2</title>
<pubDate>Tue, 03 Feb 2026 17:04:17 -0800</pubDate>
<link>https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml</link>
<sparkle:version>8809</sparkle:version>
<sparkle:shortVersionString>2026.2.2</sparkle:shortVersionString>
<sparkle:minimumSystemVersion>15.0</sparkle:minimumSystemVersion>
<description><![CDATA[<h2>Moltbot 2026.1.23</h2>
<h3>Highlights</h3>
<ul>
<li>TTS: allow model-driven TTS tags by default for expressive audio replies (laughter, singing cues, etc.).</li>
</ul>
<description><![CDATA[<h2>OpenClaw 2026.2.2</h2>
<h3>Changes</h3>
<ul>
<li>Gateway: add /tools/invoke HTTP endpoint for direct tool calls and document it. (#1575) Thanks @vignesh07.</li>
<li>Agents: keep system prompt time zone-only and move current time to <code>session_status</code> for better cache hits.</li>
<li>Agents: remove redundant bash tool alias from tool registration/display. (#1571) Thanks @Takhoffman.</li>
<li>Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node).</li>
<li>Heartbeat: add per-channel visibility controls (OK/alerts/indicator). (#1452) Thanks @dlauer.</li>
<li>Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07.</li>
<li>CLI: restart the gateway by default after <code>moltbot update</code>; add <code>--no-restart</code> to skip it.</li>
<li>CLI: add live auth probes to <code>moltbot models status</code> for per-profile verification.</li>
<li>CLI: add <code>moltbot system</code> for system events + heartbeat controls; remove standalone <code>wake</code>.</li>
<li>Agents: add Bedrock auto-discovery defaults + config overrides. (#1553) Thanks @fal3.</li>
<li>Docs: add cron vs heartbeat decision guide (with Lobster workflow notes). (#1533) Thanks @JustYannicc.</li>
<li>Docs: clarify HEARTBEAT.md empty file skips heartbeats, missing file still runs. (#1535) Thanks @JustYannicc.</li>
<li>Markdown: add per-channel table conversion (bullets for Signal/WhatsApp, code blocks elsewhere). (#1495) Thanks @odysseus0.</li>
<li>Tlon: add Urbit channel plugin (DMs, group mentions, thread replies). (#1544) Thanks @wca4a.</li>
<li>Channels: allow per-group tool allow/deny policies across built-in + plugin channels. (#1546) Thanks @adam91holt.</li>
<li>TTS: move Telegram TTS into core with auto-replies, commands, and gateway methods. (#1559) Thanks @Glucksberg.</li>
<li>Feishu: add Feishu/Lark plugin support + docs. (#7313) Thanks @jiulingyun (openclaw-cn).</li>
<li>Web UI: add Agents dashboard for managing agent files, tools, skills, models, channels, and cron jobs.</li>
<li>Memory: implement the opt-in QMD backend for workspace memory. (#3160) Thanks @vignesh07.</li>
<li>Security: add healthcheck skill and bootstrap audit guidance. (#7641) Thanks @Takhoffman.</li>
<li>Config: allow setting a default subagent thinking level via <code>agents.defaults.subagents.thinking</code> (and per-agent <code>agents.list[].subagents.thinking</code>). (#7372) Thanks @tyler6204.</li>
<li>Docs: zh-CN translations seed + polish, pipeline guidance, nav/landing updates, and typo fixes. (#8202, #6995, #6619, #7242, #7303, #7415) Thanks @AaronWander, @taiyi747, @Explorer1092, @rendaoyuan, @joshp123, @lailoo.</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>Sessions: accept non-UUID sessionIds for history/send/status while preserving agent scoping. (#1518)</li>
<li>Gateway: compare Linux process start time to avoid PID recycling lock loops; keep locks unless stale. (#1572) Thanks @steipete.</li>
<li>Messaging: mirror outbound sends into target session keys (threads + dmScope) and create session entries on send. (#1520)</li>
<li>Sessions: normalize session key casing to lowercase for consistent routing.</li>
<li>BlueBubbles: normalize group session keys for outbound mirroring. (#1520)</li>
<li>Skills: gate bird Homebrew install to macOS. (#1569) Thanks @bradleypriest.</li>
<li>Slack: honor open groupPolicy for unlisted channels in message + slash gating. (#1563) Thanks @itsjaydesu.</li>
<li>Agents: show tool error fallback when the last assistant turn only invoked tools (prevents silent stops).</li>
<li>Agents: ignore IDENTITY.md template placeholders when parsing identity to avoid placeholder replies. (#1556)</li>
<li>Agents: drop orphaned OpenAI Responses reasoning blocks on model switches. (#1562) Thanks @roshanasingh4.</li>
<li>Docker: update gateway command in docker-compose and Hetzner guide. (#1514)</li>
<li>Sessions: reject array-backed session stores to prevent silent wipes. (#1469)</li>
<li>Voice wake: auto-save wake words on blur/submit across iOS/Android and align limits with macOS.</li>
<li>UI: keep the Control UI sidebar visible while scrolling long pages. (#1515) Thanks @pookNast.</li>
<li>UI: cache Control UI markdown rendering + memoize chat text extraction to reduce Safari typing jank.</li>
<li>Tailscale: retry serve/funnel with sudo only for permission errors and keep original failure details. (#1551) Thanks @sweepies.</li>
<li>Agents: add CLI log hint to "agent failed before reply" messages. (#1550) Thanks @sweepies.</li>
<li>Discord: limit autoThread mention bypass to bot-owned threads; keep ack reactions mention-gated. (#1511) Thanks @pvoo.</li>
<li>Discord: retry rate-limited allowlist resolution + command deploy to avoid gateway crashes.</li>
<li>Mentions: ignore mentionPattern matches when another explicit mention is present in group chats (Slack/Discord/Telegram/WhatsApp).</li>
<li>Gateway: accept null optional fields in exec approval requests. (#1511) Thanks @pvoo.</li>
<li>Exec: honor tools.exec ask/security defaults for elevated approvals (avoid unwanted prompts).</li>
<li>TUI: forward unknown slash commands (for example, <code>/context</code>) to the Gateway.</li>
<li>TUI: include Gateway slash commands in autocomplete and <code>/help</code>.</li>
<li>CLI: skip usage lines in <code>moltbot models status</code> when provider usage is unavailable.</li>
<li>CLI: suppress diagnostic session/run noise during auth probes.</li>
<li>CLI: hide auth probe timeout warnings from embedded runs.</li>
<li>CLI: render auth probe results as a table in <code>moltbot models status</code>.</li>
<li>CLI: suppress probe-only embedded logs unless <code>--verbose</code> is set.</li>
<li>CLI: move auth probe errors below the table to reduce wrapping.</li>
<li>CLI: prevent ANSI color bleed when table cells wrap.</li>
<li>CLI: explain when auth profiles are excluded by auth.order in probe details.</li>
<li>CLI: drop the em dash when the banner tagline wraps to a second line.</li>
<li>CLI: inline auth probe errors in status rows to reduce wrapping.</li>
<li>Telegram: render markdown in media captions. (#1478)</li>
<li>Agents: honor enqueue overrides for embedded runs to avoid queue deadlocks in tests.</li>
<li>Agents: trigger model fallback when auth profiles are all in cooldown or unavailable. (#1522)</li>
<li>Daemon: use platform PATH delimiters when building minimal service paths.</li>
<li>Tests: skip embedded runner ordering assertion on Windows to avoid CI timeouts.</li>
<li>Linux: include env-configured user bin roots in systemd PATH and align PATH audits. (#1512) Thanks @robbyczgw-cla.</li>
<li>TUI: render Gateway slash-command replies as system output (for example, <code>/context</code>).</li>
<li>Media: only parse <code>MEDIA:</code> tags when they start the line to avoid stripping prose mentions. (#1206)</li>
<li>Media: preserve PNG alpha when possible; fall back to JPEG when still over size cap. (#1491) Thanks @robbyczgw-cla.</li>
<li>Agents: treat plugin-only tool allowlists as opt-ins; keep core tools enabled. (#1467)</li>
<li>Exec approvals: persist allowlist entry ids to keep macOS allowlist rows stable. (#1521) Thanks @ngutman.</li>
<li>MS Teams (plugin): remove <code>.default</code> suffix from Graph scopes to avoid double-appending. (#1507) Thanks @Evizero.</li>
<li>MS Teams (plugin): remove <code>.default</code> suffix from Bot Framework probe scope to avoid double-appending. (#1574) Thanks @Evizero.</li>
<li>Browser: keep extension relay tabs controllable when the extension reuses a session id after switching tabs. (#1160)</li>
<li>Agents: warn and ignore tool allowlists that only reference unknown or unloaded plugin tools. (#1566)</li>
<li>Security: require operator.approvals for gateway /approve commands. (#1) Thanks @mitsuhiko, @yueyueL.</li>
<li>Security: Matrix allowlists now require full MXIDs; ambiguous name resolution no longer grants access. Thanks @MegaManSec.</li>
<li>Security: enforce access-group gating for Slack slash commands when channel type lookup fails.</li>
<li>Security: require validated shared-secret auth before skipping device identity on gateway connect.</li>
<li>Security: guard skill installer downloads with SSRF checks (block private/localhost URLs).</li>
<li>Security: harden Windows exec allowlist; block cmd.exe bypass via single &. Thanks @simecek.</li>
<li>fix(voice-call): harden inbound allowlist; reject anonymous callers; require Telnyx publicKey for allowlist; token-gate Twilio media streams; cap webhook body size (thanks @simecek)</li>
<li>Media understanding: apply SSRF guardrails to provider fetches; allow private baseUrl overrides explicitly.</li>
<li>fix(webchat): respect user scroll position during streaming and refresh (#7226) (thanks @marcomarandiz)</li>
<li>Telegram: recover from grammY long-poll timed out errors. (#7466) Thanks @macmimi23.</li>
<li>Agents: repair malformed tool calls and session transcripts. (#7473) Thanks @justinhuangcode.</li>
<li>fix(agents): validate AbortSignal instances before calling AbortSignal.any() (#7277) (thanks @Elarwei001)</li>
<li>Media understanding: skip binary media from file text extraction. (#7475) Thanks @AlexZhangji.</li>
<li>Onboarding: keep TUI flow exclusive (skip completion prompt + background Web UI seed); completion prompt now handled by install/update.</li>
<li>TUI: block onboarding output while TUI is active and restore terminal state on exit.</li>
<li>CLI/Zsh completion: cache scripts in state dir and escape option descriptions to avoid invalid option errors.</li>
<li>fix(ui): resolve Control UI asset path correctly.</li>
<li>fix(ui): refresh agent files after external edits.</li>
<li>Docs: finish renaming the QMD memory docs to reference the OpenClaw state dir.</li>
<li>Tests: stub SSRF DNS pinning in web auto-reply + Gemini video coverage. (#6619) Thanks @joshp123.</li>
</ul>
<p><a href="https://github.com/moltbot/moltbot/blob/main/CHANGELOG.md">View full changelog</a></p>
<p><a href="https://github.com/openclaw/openclaw/blob/main/CHANGELOG.md">View full changelog</a></p>
]]></description>
<enclosure url="https://github.com/moltbot/moltbot/releases/download/v2026.1.23/Moltbot-2026.1.23.zip" length="22326233" type="application/octet-stream" sparkle:edSignature="p40dFczUfmMpsif4BrEUYVqUPG2WiBXleWgefwu4WiqjuyXbw7CAaH5CpQKig/k2qRLlE59kX7AR/qJqmy+yCA=="/>
<enclosure url="https://github.com/openclaw/openclaw/releases/download/v2026.2.2/OpenClaw-2026.2.2.zip" length="22519052" type="application/octet-stream" sparkle:edSignature="a6viD+aS5EfY/RkPIPMfoQQNkJCk6QTdV5WobXFxyYwURskUm8/nXTHVXsCh1c5+0WKUnmlDIyf0i+6IWiavAA=="/>
</item>
</channel>
</rss>

View File

@@ -1,6 +1,6 @@
## Clawdbot Node (Android) (internal)
## OpenClaw Node (Android) (internal)
Modern Android node app: connects to the **Gateway WebSocket** (`_clawdbot-gw._tcp`) and exposes **Canvas + Chat + Camera**.
Modern Android node app: connects to the **Gateway WebSocket** (`_openclaw-gw._tcp`) and exposes **Canvas + Chat + Camera**.
Notes:
- The node keeps the connection alive via a **foreground service** (persistent notification with a Disconnect action).
@@ -25,7 +25,7 @@ cd apps/android
1) Start the gateway (on your “master” machine):
```bash
pnpm clawdbot gateway --port 18789 --verbose
pnpm openclaw gateway --port 18789 --verbose
```
2) In the Android app:
@@ -34,8 +34,8 @@ pnpm clawdbot gateway --port 18789 --verbose
3) Approve pairing (on the gateway machine):
```bash
clawdbot nodes pending
clawdbot nodes approve <requestId>
openclaw nodes pending
openclaw nodes approve <requestId>
```
More details: `docs/platforms/android.md`.

View File

@@ -8,21 +8,21 @@ plugins {
}
android {
namespace = "bot.molt.android"
namespace = "ai.openclaw.android"
compileSdk = 36
sourceSets {
getByName("main") {
assets.srcDir(file("../../shared/MoltbotKit/Sources/MoltbotKit/Resources"))
assets.srcDir(file("../../shared/OpenClawKit/Sources/OpenClawKit/Resources"))
}
}
defaultConfig {
applicationId = "bot.molt.android"
applicationId = "ai.openclaw.android"
minSdk = 31
targetSdk = 36
versionCode = 202601260
versionName = "2026.1.27-beta.1"
versionCode = 202602120
versionName = "2026.2.12"
}
buildTypes {
@@ -65,7 +65,7 @@ androidComponents {
val versionName = output.versionName.orNull ?: "0"
val buildType = variant.buildType
val outputFileName = "moltbot-${versionName}-${buildType}.apk"
val outputFileName = "openclaw-${versionName}-${buildType}.apk"
output.outputFileName = outputFileName
}
}

View File

@@ -32,7 +32,7 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:theme="@style/Theme.MoltbotNode">
android:theme="@style/Theme.OpenClawNode">
<service
android:name=".NodeForegroundService"
android:exported="false"

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
enum class CameraHudKind {
Photo,

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.content.Context
import android.os.Build

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
enum class LocationMode(val rawValue: String) {
Off("off"),

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.Manifest
import android.content.pm.ApplicationInfo
@@ -18,8 +18,8 @@ import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import bot.molt.android.ui.RootScreen
import bot.molt.android.ui.MoltbotTheme
import ai.openclaw.android.ui.RootScreen
import ai.openclaw.android.ui.OpenClawTheme
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() {
@@ -56,7 +56,7 @@ class MainActivity : ComponentActivity() {
}
setContent {
MoltbotTheme {
OpenClawTheme {
Surface(modifier = Modifier) {
RootScreen(viewModel = viewModel)
}

View File

@@ -1,13 +1,13 @@
package bot.molt.android
package ai.openclaw.android
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import bot.molt.android.gateway.GatewayEndpoint
import bot.molt.android.chat.OutgoingAttachment
import bot.molt.android.node.CameraCaptureManager
import bot.molt.android.node.CanvasController
import bot.molt.android.node.ScreenRecordManager
import bot.molt.android.node.SmsManager
import ai.openclaw.android.gateway.GatewayEndpoint
import ai.openclaw.android.chat.OutgoingAttachment
import ai.openclaw.android.node.CameraCaptureManager
import ai.openclaw.android.node.CanvasController
import ai.openclaw.android.node.ScreenRecordManager
import ai.openclaw.android.node.SmsManager
import kotlinx.coroutines.flow.StateFlow
class MainViewModel(app: Application) : AndroidViewModel(app) {

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.app.Application
import android.os.StrictMode

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.app.Notification
import android.app.NotificationChannel
@@ -29,7 +29,7 @@ class NodeForegroundService : Service() {
override fun onCreate() {
super.onCreate()
ensureChannel()
val initial = buildNotification(title = "Moltbot Node", text = "Starting…")
val initial = buildNotification(title = "OpenClaw Node", text = "Starting…")
startForegroundWithTypes(notification = initial, requiresMic = false)
val runtime = (application as NodeApp).runtime
@@ -44,7 +44,7 @@ class NodeForegroundService : Service() {
) { status, server, connected, voiceMode, voiceListening ->
Quint(status, server, connected, voiceMode, voiceListening)
}.collect { (status, server, connected, voiceMode, voiceListening) ->
val title = if (connected) "Moltbot Node · Connected" else "Moltbot Node"
val title = if (connected) "OpenClaw Node · Connected" else "OpenClaw Node"
val voiceSuffix =
if (voiceMode == VoiceWakeMode.Always) {
if (voiceListening) " · Voice Wake: Listening" else " · Voice Wake: Paused"
@@ -91,7 +91,7 @@ class NodeForegroundService : Service() {
"Connection",
NotificationManager.IMPORTANCE_LOW,
).apply {
description = "Moltbot node connection status"
description = "OpenClaw node connection status"
setShowBadge(false)
}
mgr.createNotificationChannel(channel)
@@ -163,7 +163,7 @@ class NodeForegroundService : Service() {
private const val CHANNEL_ID = "connection"
private const val NOTIFICATION_ID = 1
private const val ACTION_STOP = "bot.molt.android.action.STOP"
private const val ACTION_STOP = "ai.openclaw.android.action.STOP"
fun start(context: Context) {
val intent = Intent(context, NodeForegroundService::class.java)

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.Manifest
import android.content.Context
@@ -7,35 +7,35 @@ import android.location.LocationManager
import android.os.Build
import android.os.SystemClock
import androidx.core.content.ContextCompat
import bot.molt.android.chat.ChatController
import bot.molt.android.chat.ChatMessage
import bot.molt.android.chat.ChatPendingToolCall
import bot.molt.android.chat.ChatSessionEntry
import bot.molt.android.chat.OutgoingAttachment
import bot.molt.android.gateway.DeviceAuthStore
import bot.molt.android.gateway.DeviceIdentityStore
import bot.molt.android.gateway.GatewayClientInfo
import bot.molt.android.gateway.GatewayConnectOptions
import bot.molt.android.gateway.GatewayDiscovery
import bot.molt.android.gateway.GatewayEndpoint
import bot.molt.android.gateway.GatewaySession
import bot.molt.android.gateway.GatewayTlsParams
import bot.molt.android.node.CameraCaptureManager
import bot.molt.android.node.LocationCaptureManager
import bot.molt.android.BuildConfig
import bot.molt.android.node.CanvasController
import bot.molt.android.node.ScreenRecordManager
import bot.molt.android.node.SmsManager
import bot.molt.android.protocol.MoltbotCapability
import bot.molt.android.protocol.MoltbotCameraCommand
import bot.molt.android.protocol.MoltbotCanvasA2UIAction
import bot.molt.android.protocol.MoltbotCanvasA2UICommand
import bot.molt.android.protocol.MoltbotCanvasCommand
import bot.molt.android.protocol.MoltbotScreenCommand
import bot.molt.android.protocol.MoltbotLocationCommand
import bot.molt.android.protocol.MoltbotSmsCommand
import bot.molt.android.voice.TalkModeManager
import bot.molt.android.voice.VoiceWakeManager
import ai.openclaw.android.chat.ChatController
import ai.openclaw.android.chat.ChatMessage
import ai.openclaw.android.chat.ChatPendingToolCall
import ai.openclaw.android.chat.ChatSessionEntry
import ai.openclaw.android.chat.OutgoingAttachment
import ai.openclaw.android.gateway.DeviceAuthStore
import ai.openclaw.android.gateway.DeviceIdentityStore
import ai.openclaw.android.gateway.GatewayClientInfo
import ai.openclaw.android.gateway.GatewayConnectOptions
import ai.openclaw.android.gateway.GatewayDiscovery
import ai.openclaw.android.gateway.GatewayEndpoint
import ai.openclaw.android.gateway.GatewaySession
import ai.openclaw.android.gateway.GatewayTlsParams
import ai.openclaw.android.node.CameraCaptureManager
import ai.openclaw.android.node.LocationCaptureManager
import ai.openclaw.android.BuildConfig
import ai.openclaw.android.node.CanvasController
import ai.openclaw.android.node.ScreenRecordManager
import ai.openclaw.android.node.SmsManager
import ai.openclaw.android.protocol.OpenClawCapability
import ai.openclaw.android.protocol.OpenClawCameraCommand
import ai.openclaw.android.protocol.OpenClawCanvasA2UIAction
import ai.openclaw.android.protocol.OpenClawCanvasA2UICommand
import ai.openclaw.android.protocol.OpenClawCanvasCommand
import ai.openclaw.android.protocol.OpenClawScreenCommand
import ai.openclaw.android.protocol.OpenClawLocationCommand
import ai.openclaw.android.protocol.OpenClawSmsCommand
import ai.openclaw.android.voice.TalkModeManager
import ai.openclaw.android.voice.VoiceWakeManager
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -451,38 +451,38 @@ class NodeRuntime(context: Context) {
private fun buildInvokeCommands(): List<String> =
buildList {
add(MoltbotCanvasCommand.Present.rawValue)
add(MoltbotCanvasCommand.Hide.rawValue)
add(MoltbotCanvasCommand.Navigate.rawValue)
add(MoltbotCanvasCommand.Eval.rawValue)
add(MoltbotCanvasCommand.Snapshot.rawValue)
add(MoltbotCanvasA2UICommand.Push.rawValue)
add(MoltbotCanvasA2UICommand.PushJSONL.rawValue)
add(MoltbotCanvasA2UICommand.Reset.rawValue)
add(MoltbotScreenCommand.Record.rawValue)
add(OpenClawCanvasCommand.Present.rawValue)
add(OpenClawCanvasCommand.Hide.rawValue)
add(OpenClawCanvasCommand.Navigate.rawValue)
add(OpenClawCanvasCommand.Eval.rawValue)
add(OpenClawCanvasCommand.Snapshot.rawValue)
add(OpenClawCanvasA2UICommand.Push.rawValue)
add(OpenClawCanvasA2UICommand.PushJSONL.rawValue)
add(OpenClawCanvasA2UICommand.Reset.rawValue)
add(OpenClawScreenCommand.Record.rawValue)
if (cameraEnabled.value) {
add(MoltbotCameraCommand.Snap.rawValue)
add(MoltbotCameraCommand.Clip.rawValue)
add(OpenClawCameraCommand.Snap.rawValue)
add(OpenClawCameraCommand.Clip.rawValue)
}
if (locationMode.value != LocationMode.Off) {
add(MoltbotLocationCommand.Get.rawValue)
add(OpenClawLocationCommand.Get.rawValue)
}
if (sms.canSendSms()) {
add(MoltbotSmsCommand.Send.rawValue)
add(OpenClawSmsCommand.Send.rawValue)
}
}
private fun buildCapabilities(): List<String> =
buildList {
add(MoltbotCapability.Canvas.rawValue)
add(MoltbotCapability.Screen.rawValue)
if (cameraEnabled.value) add(MoltbotCapability.Camera.rawValue)
if (sms.canSendSms()) add(MoltbotCapability.Sms.rawValue)
add(OpenClawCapability.Canvas.rawValue)
add(OpenClawCapability.Screen.rawValue)
if (cameraEnabled.value) add(OpenClawCapability.Camera.rawValue)
if (sms.canSendSms()) add(OpenClawCapability.Sms.rawValue)
if (voiceWakeMode.value != VoiceWakeMode.Off && hasRecordAudioPermission()) {
add(MoltbotCapability.VoiceWake.rawValue)
add(OpenClawCapability.VoiceWake.rawValue)
}
if (locationMode.value != LocationMode.Off) {
add(MoltbotCapability.Location.rawValue)
add(OpenClawCapability.Location.rawValue)
}
}
@@ -506,7 +506,7 @@ class NodeRuntime(context: Context) {
val version = resolvedVersionName()
val release = Build.VERSION.RELEASE?.trim().orEmpty()
val releaseLabel = if (release.isEmpty()) "unknown" else release
return "MoltbotAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
return "OpenClawAndroid/$version (Android $releaseLabel; SDK ${Build.VERSION.SDK_INT})"
}
private fun buildClientInfo(clientId: String, clientMode: String): GatewayClientInfo {
@@ -529,7 +529,7 @@ class NodeRuntime(context: Context) {
caps = buildCapabilities(),
commands = buildInvokeCommands(),
permissions = emptyMap(),
client = buildClientInfo(clientId = "moltbot-android", clientMode = "node"),
client = buildClientInfo(clientId = "openclaw-android", clientMode = "node"),
userAgent = buildUserAgent(),
)
}
@@ -541,7 +541,7 @@ class NodeRuntime(context: Context) {
caps = emptyList(),
commands = emptyList(),
permissions = emptyMap(),
client = buildClientInfo(clientId = "moltbot-control-ui", clientMode = "ui"),
client = buildClientInfo(clientId = "openclaw-control-ui", clientMode = "ui"),
userAgent = buildUserAgent(),
)
}
@@ -665,7 +665,7 @@ class NodeRuntime(context: Context) {
val actionId = (userActionObj["id"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty {
java.util.UUID.randomUUID().toString()
}
val name = MoltbotCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
val name = OpenClawCanvasA2UIAction.extractActionName(userActionObj) ?: return@launch
val surfaceId =
(userActionObj["surfaceId"] as? JsonPrimitive)?.content?.trim().orEmpty().ifEmpty { "main" }
@@ -675,7 +675,7 @@ class NodeRuntime(context: Context) {
val sessionKey = resolveMainSessionKey()
val message =
MoltbotCanvasA2UIAction.formatAgentMessage(
OpenClawCanvasA2UIAction.formatAgentMessage(
actionName = name,
sessionKey = sessionKey,
surfaceId = surfaceId,
@@ -709,7 +709,7 @@ class NodeRuntime(context: Context) {
try {
canvas.eval(
MoltbotCanvasA2UIAction.jsDispatchA2UIActionStatus(
OpenClawCanvasA2UIAction.jsDispatchA2UIActionStatus(
actionId = actionId,
ok = connected && error == null,
error = error,
@@ -827,10 +827,10 @@ class NodeRuntime(context: Context) {
private suspend fun handleInvoke(command: String, paramsJson: String?): GatewaySession.InvokeResult {
if (
command.startsWith(MoltbotCanvasCommand.NamespacePrefix) ||
command.startsWith(MoltbotCanvasA2UICommand.NamespacePrefix) ||
command.startsWith(MoltbotCameraCommand.NamespacePrefix) ||
command.startsWith(MoltbotScreenCommand.NamespacePrefix)
command.startsWith(OpenClawCanvasCommand.NamespacePrefix) ||
command.startsWith(OpenClawCanvasA2UICommand.NamespacePrefix) ||
command.startsWith(OpenClawCameraCommand.NamespacePrefix) ||
command.startsWith(OpenClawScreenCommand.NamespacePrefix)
) {
if (!isForeground.value) {
return GatewaySession.InvokeResult.error(
@@ -839,13 +839,13 @@ class NodeRuntime(context: Context) {
)
}
}
if (command.startsWith(MoltbotCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
if (command.startsWith(OpenClawCameraCommand.NamespacePrefix) && !cameraEnabled.value) {
return GatewaySession.InvokeResult.error(
code = "CAMERA_DISABLED",
message = "CAMERA_DISABLED: enable Camera in Settings",
)
}
if (command.startsWith(MoltbotLocationCommand.NamespacePrefix) &&
if (command.startsWith(OpenClawLocationCommand.NamespacePrefix) &&
locationMode.value == LocationMode.Off
) {
return GatewaySession.InvokeResult.error(
@@ -855,18 +855,18 @@ class NodeRuntime(context: Context) {
}
return when (command) {
MoltbotCanvasCommand.Present.rawValue -> {
OpenClawCanvasCommand.Present.rawValue -> {
val url = CanvasController.parseNavigateUrl(paramsJson)
canvas.navigate(url)
GatewaySession.InvokeResult.ok(null)
}
MoltbotCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
MoltbotCanvasCommand.Navigate.rawValue -> {
OpenClawCanvasCommand.Hide.rawValue -> GatewaySession.InvokeResult.ok(null)
OpenClawCanvasCommand.Navigate.rawValue -> {
val url = CanvasController.parseNavigateUrl(paramsJson)
canvas.navigate(url)
GatewaySession.InvokeResult.ok(null)
}
MoltbotCanvasCommand.Eval.rawValue -> {
OpenClawCanvasCommand.Eval.rawValue -> {
val js =
CanvasController.parseEvalJs(paramsJson)
?: return GatewaySession.InvokeResult.error(
@@ -884,7 +884,7 @@ class NodeRuntime(context: Context) {
}
GatewaySession.InvokeResult.ok("""{"result":${result.toJsonString()}}""")
}
MoltbotCanvasCommand.Snapshot.rawValue -> {
OpenClawCanvasCommand.Snapshot.rawValue -> {
val snapshotParams = CanvasController.parseSnapshotParams(paramsJson)
val base64 =
try {
@@ -901,7 +901,7 @@ class NodeRuntime(context: Context) {
}
GatewaySession.InvokeResult.ok("""{"format":"${snapshotParams.format.rawValue}","base64":"$base64"}""")
}
MoltbotCanvasA2UICommand.Reset.rawValue -> {
OpenClawCanvasA2UICommand.Reset.rawValue -> {
val a2uiUrl = resolveA2uiHostUrl()
?: return GatewaySession.InvokeResult.error(
code = "A2UI_HOST_NOT_CONFIGURED",
@@ -917,7 +917,7 @@ class NodeRuntime(context: Context) {
val res = canvas.eval(a2uiResetJS)
GatewaySession.InvokeResult.ok(res)
}
MoltbotCanvasA2UICommand.Push.rawValue, MoltbotCanvasA2UICommand.PushJSONL.rawValue -> {
OpenClawCanvasA2UICommand.Push.rawValue, OpenClawCanvasA2UICommand.PushJSONL.rawValue -> {
val messages =
try {
decodeA2uiMessages(command, paramsJson)
@@ -940,7 +940,7 @@ class NodeRuntime(context: Context) {
val res = canvas.eval(js)
GatewaySession.InvokeResult.ok(res)
}
MoltbotCameraCommand.Snap.rawValue -> {
OpenClawCameraCommand.Snap.rawValue -> {
showCameraHud(message = "Taking photo…", kind = CameraHudKind.Photo)
triggerCameraFlash()
val res =
@@ -954,7 +954,7 @@ class NodeRuntime(context: Context) {
showCameraHud(message = "Photo captured", kind = CameraHudKind.Success, autoHideMs = 1600)
GatewaySession.InvokeResult.ok(res.payloadJson)
}
MoltbotCameraCommand.Clip.rawValue -> {
OpenClawCameraCommand.Clip.rawValue -> {
val includeAudio = paramsJson?.contains("\"includeAudio\":true") != false
if (includeAudio) externalAudioCaptureActive.value = true
try {
@@ -973,7 +973,7 @@ class NodeRuntime(context: Context) {
if (includeAudio) externalAudioCaptureActive.value = false
}
}
MoltbotLocationCommand.Get.rawValue -> {
OpenClawLocationCommand.Get.rawValue -> {
val mode = locationMode.value
if (!isForeground.value && mode != LocationMode.Always) {
return GatewaySession.InvokeResult.error(
@@ -1026,7 +1026,7 @@ class NodeRuntime(context: Context) {
GatewaySession.InvokeResult.error(code = "LOCATION_UNAVAILABLE", message = message)
}
}
MoltbotScreenCommand.Record.rawValue -> {
OpenClawScreenCommand.Record.rawValue -> {
// Status pill mirrors screen recording state so it stays visible without overlay stacking.
_screenRecordActive.value = true
try {
@@ -1042,7 +1042,7 @@ class NodeRuntime(context: Context) {
_screenRecordActive.value = false
}
}
MoltbotSmsCommand.Send.rawValue -> {
OpenClawSmsCommand.Send.rawValue -> {
val res = sms.send(paramsJson)
if (res.ok) {
GatewaySession.InvokeResult.ok(res.payloadJson)
@@ -1115,7 +1115,7 @@ class NodeRuntime(context: Context) {
val raw = if (nodeRaw.isNotBlank()) nodeRaw else operatorRaw
if (raw.isBlank()) return null
val base = raw.trimEnd('/')
return "${base}/__moltbot__/a2ui/?platform=android"
return "${base}/__openclaw__/a2ui/?platform=android"
}
private suspend fun ensureA2uiReady(a2uiUrl: String): Boolean {
@@ -1150,7 +1150,7 @@ class NodeRuntime(context: Context) {
val jsonlField = (obj["jsonl"] as? JsonPrimitive)?.content?.trim().orEmpty()
val hasMessagesArray = obj["messages"] is JsonArray
if (command == MoltbotCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
if (command == OpenClawCanvasA2UICommand.PushJSONL.rawValue || (!hasMessagesArray && jsonlField.isNotBlank())) {
val jsonl = jsonlField
if (jsonl.isBlank()) throw IllegalArgumentException("INVALID_REQUEST: jsonl required")
val messages =
@@ -1207,7 +1207,8 @@ private const val a2uiReadyCheckJS: String =
"""
(() => {
try {
return !!globalThis.clawdbotA2UI && typeof globalThis.clawdbotA2UI.applyMessages === 'function';
const host = globalThis.openclawA2UI;
return !!host && typeof host.applyMessages === 'function';
} catch (_) {
return false;
}
@@ -1218,8 +1219,9 @@ private const val a2uiResetJS: String =
"""
(() => {
try {
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
return globalThis.clawdbotA2UI.reset();
const host = globalThis.openclawA2UI;
if (!host) return { ok: false, error: "missing openclawA2UI" };
return host.reset();
} catch (e) {
return { ok: false, error: String(e?.message ?? e) };
}
@@ -1230,9 +1232,10 @@ private fun a2uiApplyMessagesJS(messagesJson: String): String {
return """
(() => {
try {
if (!globalThis.clawdbotA2UI) return { ok: false, error: "missing moltbotA2UI" };
const host = globalThis.openclawA2UI;
if (!host) return { ok: false, error: "missing openclawA2UI" };
const messages = $messagesJson;
return globalThis.clawdbotA2UI.applyMessages(messages);
return host.applyMessages(messages);
} catch (e) {
return { ok: false, error: String(e?.message ?? e) };
}

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.content.pm.PackageManager
import android.content.Intent
@@ -115,7 +115,7 @@ class PermissionRequester(private val activity: ComponentActivity) {
private fun buildRationaleMessage(permissions: List<String>): String {
val labels = permissions.map { permissionLabel(it) }
return "Moltbot needs ${labels.joinToString(", ")} permissions to continue."
return "OpenClaw needs ${labels.joinToString(", ")} permissions to continue."
}
private fun buildSettingsMessage(permissions: List<String>): String {

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
import android.app.Activity
import android.content.Context
@@ -55,7 +55,7 @@ class ScreenCaptureRequester(private val activity: ComponentActivity) {
suspendCancellableCoroutine { cont ->
AlertDialog.Builder(activity)
.setTitle("Screen recording required")
.setMessage("Moltbot needs to record the screen for this command.")
.setMessage("OpenClaw needs to record the screen for this command.")
.setPositiveButton("Continue") { _, _ -> cont.resume(true) }
.setNegativeButton("Not now") { _, _ -> cont.resume(false) }
.setOnCancelListener { cont.resume(false) }

View File

@@ -1,8 +1,9 @@
@file:Suppress("DEPRECATION")
package bot.molt.android
package ai.openclaw.android
import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey
@@ -16,11 +17,12 @@ import java.util.UUID
class SecurePrefs(context: Context) {
companion object {
val defaultWakeWords: List<String> = listOf("clawd", "claude")
val defaultWakeWords: List<String> = listOf("openclaw", "claude")
private const val displayNameKey = "node.displayName"
private const val voiceWakeModeKey = "voiceWake.mode"
}
private val appContext = context.applicationContext
private val json = Json { ignoreUnknownKeys = true }
private val masterKey =
@@ -28,14 +30,9 @@ class SecurePrefs(context: Context) {
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
private val prefs =
EncryptedSharedPreferences.create(
context,
"moltbot.node.secure",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
)
private val prefs: SharedPreferences by lazy {
createPrefs(appContext, "openclaw.node.secure")
}
private val _instanceId = MutableStateFlow(loadOrCreateInstanceId())
val instanceId: StateFlow<String> = _instanceId
@@ -59,28 +56,24 @@ class SecurePrefs(context: Context) {
val preventSleep: StateFlow<Boolean> = _preventSleep
private val _manualEnabled =
MutableStateFlow(readBoolWithMigration("gateway.manual.enabled", "bridge.manual.enabled", false))
MutableStateFlow(prefs.getBoolean("gateway.manual.enabled", false))
val manualEnabled: StateFlow<Boolean> = _manualEnabled
private val _manualHost =
MutableStateFlow(readStringWithMigration("gateway.manual.host", "bridge.manual.host", ""))
MutableStateFlow(prefs.getString("gateway.manual.host", "") ?: "")
val manualHost: StateFlow<String> = _manualHost
private val _manualPort =
MutableStateFlow(readIntWithMigration("gateway.manual.port", "bridge.manual.port", 18789))
MutableStateFlow(prefs.getInt("gateway.manual.port", 18789))
val manualPort: StateFlow<Int> = _manualPort
private val _manualTls =
MutableStateFlow(readBoolWithMigration("gateway.manual.tls", null, true))
MutableStateFlow(prefs.getBoolean("gateway.manual.tls", true))
val manualTls: StateFlow<Boolean> = _manualTls
private val _lastDiscoveredStableId =
MutableStateFlow(
readStringWithMigration(
"gateway.lastDiscoveredStableID",
"bridge.lastDiscoveredStableId",
"",
),
prefs.getString("gateway.lastDiscoveredStableID", "") ?: "",
)
val lastDiscoveredStableId: StateFlow<String> = _lastDiscoveredStableId
@@ -158,9 +151,7 @@ class SecurePrefs(context: Context) {
fun loadGatewayToken(): String? {
val key = "gateway.token.${_instanceId.value}"
val stored = prefs.getString(key, null)?.trim()
if (!stored.isNullOrEmpty()) return stored
val legacy = prefs.getString("bridge.token.${_instanceId.value}", null)?.trim()
return legacy?.takeIf { it.isNotEmpty() }
return stored?.takeIf { it.isNotEmpty() }
}
fun saveGatewayToken(token: String) {
@@ -201,6 +192,16 @@ class SecurePrefs(context: Context) {
prefs.edit { remove(key) }
}
private fun createPrefs(context: Context, name: String): SharedPreferences {
return EncryptedSharedPreferences.create(
context,
name,
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
)
}
private fun loadOrCreateInstanceId(): String {
val existing = prefs.getString("node.instanceId", null)?.trim()
if (!existing.isNullOrBlank()) return existing
@@ -270,39 +271,4 @@ class SecurePrefs(context: Context) {
}
}
private fun readBoolWithMigration(newKey: String, oldKey: String?, defaultValue: Boolean): Boolean {
if (prefs.contains(newKey)) {
return prefs.getBoolean(newKey, defaultValue)
}
if (oldKey != null && prefs.contains(oldKey)) {
val value = prefs.getBoolean(oldKey, defaultValue)
prefs.edit { putBoolean(newKey, value) }
return value
}
return defaultValue
}
private fun readStringWithMigration(newKey: String, oldKey: String?, defaultValue: String): String {
if (prefs.contains(newKey)) {
return prefs.getString(newKey, defaultValue) ?: defaultValue
}
if (oldKey != null && prefs.contains(oldKey)) {
val value = prefs.getString(oldKey, defaultValue) ?: defaultValue
prefs.edit { putString(newKey, value) }
return value
}
return defaultValue
}
private fun readIntWithMigration(newKey: String, oldKey: String?, defaultValue: Int): Int {
if (prefs.contains(newKey)) {
return prefs.getInt(newKey, defaultValue)
}
if (oldKey != null && prefs.contains(oldKey)) {
val value = prefs.getInt(oldKey, defaultValue)
prefs.edit { putInt(newKey, value) }
return value
}
return defaultValue
}
}

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
internal fun normalizeMainKey(raw: String?): String {
val trimmed = raw?.trim()

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
enum class VoiceWakeMode(val rawValue: String) {
Off("off"),

View File

@@ -1,4 +1,4 @@
package bot.molt.android
package ai.openclaw.android
object WakeWords {
const val maxWords: Int = 32

View File

@@ -1,6 +1,6 @@
package bot.molt.android.chat
package ai.openclaw.android.chat
import bot.molt.android.gateway.GatewaySession
import ai.openclaw.android.gateway.GatewaySession
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope

View File

@@ -1,4 +1,4 @@
package bot.molt.android.chat
package ai.openclaw.android.chat
data class ChatMessage(
val id: String,

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
object BonjourEscapes {
fun decode(input: String): String {

View File

@@ -1,6 +1,6 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
import bot.molt.android.SecurePrefs
import ai.openclaw.android.SecurePrefs
class DeviceAuthStore(private val prefs: SecurePrefs) {
fun loadToken(deviceId: String, role: String): String? {

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
import android.content.Context
import android.util.Base64
@@ -21,7 +21,7 @@ data class DeviceIdentity(
class DeviceIdentityStore(context: Context) {
private val json = Json { ignoreUnknownKeys = true }
private val identityFile = File(context.filesDir, "moltbot/identity/device.json")
private val identityFile = File(context.filesDir, "openclaw/identity/device.json")
@Synchronized
fun loadOrCreate(): DeviceIdentity {
@@ -65,9 +65,13 @@ class DeviceIdentityStore(context: Context) {
}
private fun load(): DeviceIdentity? {
return readIdentity(identityFile)
}
private fun readIdentity(file: File): DeviceIdentity? {
return try {
if (!identityFile.exists()) return null
val raw = identityFile.readText(Charsets.UTF_8)
if (!file.exists()) return null
val raw = file.readText(Charsets.UTF_8)
val decoded = json.decodeFromString(DeviceIdentity.serializer(), raw)
if (decoded.deviceId.isBlank() ||
decoded.publicKeyRawBase64.isBlank() ||

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
import android.content.Context
import android.net.ConnectivityManager
@@ -51,9 +51,9 @@ class GatewayDiscovery(
private val nsd = context.getSystemService(NsdManager::class.java)
private val connectivity = context.getSystemService(ConnectivityManager::class.java)
private val dns = DnsResolver.getInstance()
private val serviceType = "_moltbot-gw._tcp."
private val wideAreaDomain = "moltbot.internal."
private val logTag = "Moltbot/GatewayDiscovery"
private val serviceType = "_openclaw-gw._tcp."
private val wideAreaDomain = System.getenv("OPENCLAW_WIDE_AREA_DOMAIN")
private val logTag = "OpenClaw/GatewayDiscovery"
private val localById = ConcurrentHashMap<String, GatewayEndpoint>()
private val unicastById = ConcurrentHashMap<String, GatewayEndpoint>()
@@ -91,7 +91,9 @@ class GatewayDiscovery(
init {
startLocalDiscovery()
startUnicastDiscovery(wideAreaDomain)
if (!wideAreaDomain.isNullOrBlank()) {
startUnicastDiscovery(wideAreaDomain)
}
}
private fun startLocalDiscovery() {

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
data class GatewayEndpoint(
val stableId: String,

View File

@@ -1,3 +1,3 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
const val GATEWAY_PROTOCOL_VERSION = 3

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
import android.util.Log
import java.util.Locale
@@ -148,7 +148,7 @@ class GatewaySession(
try {
conn.request("node.event", params, timeoutMs = 8_000)
} catch (err: Throwable) {
Log.w("MoltbotGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
Log.w("OpenClawGateway", "node.event failed: ${err.message ?: err::class.java.simpleName}")
}
}
@@ -181,7 +181,7 @@ class GatewaySession(
private val connectNonceDeferred = CompletableDeferred<String?>()
private val client: OkHttpClient = buildClient()
private var socket: WebSocket? = null
private val loggerTag = "MoltbotGateway"
private val loggerTag = "OpenClawGateway"
val remoteAddress: String =
if (endpoint.host.contains(":")) {

View File

@@ -1,4 +1,4 @@
package bot.molt.android.gateway
package ai.openclaw.android.gateway
import android.annotation.SuppressLint
import java.security.MessageDigest

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import android.Manifest
import android.content.Context
@@ -22,7 +22,7 @@ import androidx.camera.video.VideoRecordEvent
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.checkSelfPermission
import androidx.core.graphics.scale
import bot.molt.android.PermissionRequester
import ai.openclaw.android.PermissionRequester
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeout
@@ -155,7 +155,7 @@ class CameraCaptureManager(private val context: Context) {
provider.unbindAll()
provider.bindToLifecycle(owner, selector, videoCapture)
val file = File.createTempFile("moltbot-clip-", ".mp4")
val file = File.createTempFile("openclaw-clip-", ".mp4")
val outputOptions = FileOutputOptions.Builder(file).build()
val finalized = kotlinx.coroutines.CompletableDeferred<VideoRecordEvent.Finalize>()
@@ -285,7 +285,7 @@ private suspend fun Context.cameraProvider(): ProcessCameraProvider =
/** Returns (jpegBytes, exifOrientation) so caller can rotate the decoded bitmap. */
private suspend fun ImageCapture.takeJpegWithExif(executor: Executor): Pair<ByteArray, Int> =
suspendCancellableCoroutine { cont ->
val file = File.createTempFile("moltbot-snap-", ".jpg")
val file = File.createTempFile("openclaw-snap-", ".jpg")
val options = ImageCapture.OutputFileOptions.Builder(file).build()
takePicture(
options,

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import android.graphics.Bitmap
import android.graphics.Canvas
@@ -17,7 +17,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import bot.molt.android.BuildConfig
import ai.openclaw.android.BuildConfig
import kotlin.coroutines.resume
class CanvasController {
@@ -84,12 +84,12 @@ class CanvasController {
withWebViewOnMain { wv ->
if (currentUrl == null) {
if (BuildConfig.DEBUG) {
Log.d("MoltbotCanvas", "load scaffold: $scaffoldAssetUrl")
Log.d("OpenClawCanvas", "load scaffold: $scaffoldAssetUrl")
}
wv.loadUrl(scaffoldAssetUrl)
} else {
if (BuildConfig.DEBUG) {
Log.d("MoltbotCanvas", "load url: $currentUrl")
Log.d("OpenClawCanvas", "load url: $currentUrl")
}
wv.loadUrl(currentUrl)
}
@@ -106,7 +106,7 @@ class CanvasController {
val js = """
(() => {
try {
const api = globalThis.__moltbot;
const api = globalThis.__openclaw;
if (!api) return;
if (typeof api.setDebugStatusEnabled === 'function') {
api.setDebugStatusEnabled(${if (enabled) "true" else "false"});

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import kotlin.math.max
import kotlin.math.min

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import android.Manifest
import android.content.Context

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import android.content.Context
import android.hardware.display.DisplayManager
@@ -6,7 +6,7 @@ import android.media.MediaRecorder
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.util.Base64
import bot.molt.android.ScreenCaptureRequester
import ai.openclaw.android.ScreenCaptureRequester
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
@@ -17,13 +17,13 @@ class ScreenRecordManager(private val context: Context) {
data class Payload(val payloadJson: String)
@Volatile private var screenCaptureRequester: ScreenCaptureRequester? = null
@Volatile private var permissionRequester: bot.molt.android.PermissionRequester? = null
@Volatile private var permissionRequester: ai.openclaw.android.PermissionRequester? = null
fun attachScreenCaptureRequester(requester: ScreenCaptureRequester) {
screenCaptureRequester = requester
}
fun attachPermissionRequester(requester: bot.molt.android.PermissionRequester) {
fun attachPermissionRequester(requester: ai.openclaw.android.PermissionRequester) {
permissionRequester = requester
}
@@ -63,7 +63,7 @@ class ScreenRecordManager(private val context: Context) {
val height = metrics.heightPixels
val densityDpi = metrics.densityDpi
val file = File.createTempFile("moltbot-screen-", ".mp4")
val file = File.createTempFile("openclaw-screen-", ".mp4")
if (includeAudio) ensureMicPermission()
val recorder = createMediaRecorder()
@@ -90,7 +90,7 @@ class ScreenRecordManager(private val context: Context) {
val surface = recorder.surface
virtualDisplay =
projection.createVirtualDisplay(
"moltbot-screen",
"openclaw-screen",
width,
height,
densityDpi,

View File

@@ -1,4 +1,4 @@
package bot.molt.android.node
package ai.openclaw.android.node
import android.Manifest
import android.content.Context
@@ -11,7 +11,7 @@ import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.encodeToString
import bot.molt.android.PermissionRequester
import ai.openclaw.android.PermissionRequester
/**
* Sends SMS messages via the Android SMS API.

View File

@@ -1,9 +1,9 @@
package bot.molt.android.protocol
package ai.openclaw.android.protocol
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
object MoltbotCanvasA2UIAction {
object OpenClawCanvasA2UIAction {
fun extractActionName(userAction: JsonObject): String? {
val name =
(userAction["name"] as? JsonPrimitive)
@@ -61,6 +61,6 @@ object MoltbotCanvasA2UIAction {
val err = (error ?: "").replace("\\", "\\\\").replace("\"", "\\\"")
val okLiteral = if (ok) "true" else "false"
val idEscaped = actionId.replace("\\", "\\\\").replace("\"", "\\\"")
return "window.dispatchEvent(new CustomEvent('moltbot:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
return "window.dispatchEvent(new CustomEvent('openclaw:a2ui-action-status', { detail: { id: \"${idEscaped}\", ok: ${okLiteral}, error: \"${err}\" } }));"
}
}

View File

@@ -1,6 +1,6 @@
package bot.molt.android.protocol
package ai.openclaw.android.protocol
enum class MoltbotCapability(val rawValue: String) {
enum class OpenClawCapability(val rawValue: String) {
Canvas("canvas"),
Camera("camera"),
Screen("screen"),
@@ -9,7 +9,7 @@ enum class MoltbotCapability(val rawValue: String) {
Location("location"),
}
enum class MoltbotCanvasCommand(val rawValue: String) {
enum class OpenClawCanvasCommand(val rawValue: String) {
Present("canvas.present"),
Hide("canvas.hide"),
Navigate("canvas.navigate"),
@@ -22,7 +22,7 @@ enum class MoltbotCanvasCommand(val rawValue: String) {
}
}
enum class MoltbotCanvasA2UICommand(val rawValue: String) {
enum class OpenClawCanvasA2UICommand(val rawValue: String) {
Push("canvas.a2ui.push"),
PushJSONL("canvas.a2ui.pushJSONL"),
Reset("canvas.a2ui.reset"),
@@ -33,7 +33,7 @@ enum class MoltbotCanvasA2UICommand(val rawValue: String) {
}
}
enum class MoltbotCameraCommand(val rawValue: String) {
enum class OpenClawCameraCommand(val rawValue: String) {
Snap("camera.snap"),
Clip("camera.clip"),
;
@@ -43,7 +43,7 @@ enum class MoltbotCameraCommand(val rawValue: String) {
}
}
enum class MoltbotScreenCommand(val rawValue: String) {
enum class OpenClawScreenCommand(val rawValue: String) {
Record("screen.record"),
;
@@ -52,7 +52,7 @@ enum class MoltbotScreenCommand(val rawValue: String) {
}
}
enum class MoltbotSmsCommand(val rawValue: String) {
enum class OpenClawSmsCommand(val rawValue: String) {
Send("sms.send"),
;
@@ -61,7 +61,7 @@ enum class MoltbotSmsCommand(val rawValue: String) {
}
}
enum class MoltbotLocationCommand(val rawValue: String) {
enum class OpenClawLocationCommand(val rawValue: String) {
Get("location.get"),
;

Some files were not shown because too many files have changed in this diff Show More