Commit graph

378 commits

Author SHA1 Message Date
lsr911
1beea4b873
fix(qqbot): use truncateUtf16Safe for debug log URL/key truncation (#102572)
* fix(qqbot): use truncateUtf16Safe for debug log URL/key truncation

Replace naive .slice(0, N) with truncateUtf16Safe() in:
- image-size.ts: URL preview in debug logs (2 sites)
- upload-cache.ts: cache key preview in debug logs (2 sites)

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

* test(qqbot): cover UTF-16-safe debug previews

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-09 11:20:38 +01:00
Peter Steinberger
42a8679c4d
fix: restore release validation on main (#102516)
* test(qqbot): assert approval fence exactly

* fix(release): wait for ClawHub publish logging

* fix(ci): repair Bun global install smoke

* test(slack): fix app identity fixture
2026-07-09 09:39:08 +01:00
Vincent Koc
6f6c3e2602
refactor(qqbot): localize internal declarations (#102048) 2026-07-07 23:01:33 -07:00
Vincent Koc
b88f80940a
refactor: localize internal implementation symbols (#101831) 2026-07-07 10:23:36 -07:00
Vincent Koc
ccbd9eb28d
fix(qqbot): keep bounded previews UTF-16 safe (#101516)
Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>
2026-07-07 02:18:57 -07:00
Peter Steinberger
a2a68d154b
fix(agent): preserve explicit recipient sessions (#101507)
* fix(agent): honor recipient session routing

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: pingfanfan <pingfan.work@gmail.com>

* fix(agent): preserve canonical recipient routes

* fix(agent): require exact recipient routes

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>

Co-authored-by: pingfanfan <pingfan.work@gmail.com>

* fix(agent): harden recipient route resolution

* fix(imessage): require canonical recipient handles

* fix(agent): refine provider recipient exactness

* fix(agent): bound direct alias session routing

* fix(signal): preserve direct alias route type

* fix(agent): honor binding-scoped recipient sessions

* fix(routing): honor configured main session aliases

* fix(clickclack): align account-owned session routes

* fix(twitch): preserve canonical recipient sessions

* fix(routing): isolate stable outbound identities

* chore: defer recipient session changelog

* fix(sms): use dedicated channel SDK facade

---------

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: pingfanfan <pingfan.work@gmail.com>
2026-07-07 10:07:13 +01:00
wangmiao0668000666
048fe0850e
fix(qqbot): use UTF-16-safe truncation for messaging reply and approval previews (#101421)
* fix(qqbot): use UTF-16-safe truncation for messaging reply and approval previews

* test(qqbot): focus UTF-16 approval regression

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-07 08:22:06 +01:00
Dallin Romney
5af7ba92ce
refactor: consolidate byte-size formatting (#99768)
* refactor: consolidate byte-size formatting

* fix: stabilize byte formatter import boundary

* fix: keep byte formatter within SDK budget

* fix: emit byte formatter package entry

* refactor: trim byte formatter surface

* fix: use narrow byte formatter import

* refactor: remove byte formatter dependency changes

* fix: refresh rebased byte formatter baseline
2026-07-06 09:26:04 -07:00
Peter Steinberger
1f484a8dbd test: speed up and stabilize full suite 2026-07-05 08:00:23 -04:00
Masato Hoshino
cbb920c7d9
fix(qqbot): channel status keeps reporting connected after the gateway websocket dies (#100127)
* fix(qqbot): publish disconnected channel status when the gateway closes or gives up

The QQBot channel only ever set connected: true (onReady/onResumed);
no close path updated the status, so a fatal close (bot banned or
offline, 4914/4915) or reconnect exhaustion left channels.status
claiming a live connection forever, and the channel-health monitor
never saw connected=false for a dead gateway.

Thread an onDisconnected callback from GatewayConnection.handleClose
(fatal and pre-reconnect branches) and the reconnect-exhaustion path
through the engine/bridge layers into channel.ts, which now records
connected: false and, for fatal closes, the close reason as lastError.
The running flag stays owned by the gateway lifecycle store, matching
the sibling channel convention.

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

* fix(qqbot): import ChannelAccountSnapshot from the channel-contract subpath

The monolithic openclaw/plugin-sdk root entry is a legacy surface;
plugin-sdk contract guardrails and extension boundary checks require
focused subpath imports in bundled plugin sources.

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

* fix(qqbot): ignore stale socket closes from superseded gateway connections

A server-driven RECONNECT / INVALID_SESSION tears the old socket down
and brings up a replacement; the old socket's close event can arrive
after the replacement is live. Reacting to it again would tear down the
new socket and regress the connected status, so the close handler now
ignores closes from sockets that are no longer current.

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

* fix(qqbot): harden disconnect status recovery

* fix(qqbot): report opcode-driven reconnects

* fix(qqbot): keep fatal disconnect health visible

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-05 04:18:01 -07:00
Dallin Romney
bfffa950d7
refactor(plugin-sdk): consolidate tool result helpers (#99740)
* refactor(plugin-sdk): consolidate tool result helpers

* docs(plugin-sdk): tighten tool result guidance

* refactor(feishu): use tool results directly
2026-07-04 16:50:44 -07:00
Peter Steinberger
7a92c473c1
test(qqbot): fix macOS-only outbound media test failures
The scoped outbound media tests added in #92872 assert raw mkdtemp paths,
but prod resolvers canonicalize roots, so on macOS the /var -> /private/var
tmpdir symlink made three tests fail (green on Linux CI). Realpath the tmp
roots like sibling tests, and export the real UploadDailyLimitExceededError
from the outbound-dispatch sender.js mock so prod instanceof checks in error
paths hit real assertions instead of vitest's missing-export guard.

Also add AGENTS.md test guidance covering both patterns.
2026-07-04 19:20:22 +01:00
Dallin Romney
c7aca4f029
refactor: consolidate async timing helpers (#99721) 2026-07-03 17:58:15 -07:00
xingzhou
cd6b67533d
fix(qqbot): allow scoped sandbox media sends (#92872)
* fix(qqbot): allow scoped sandbox media sends

* fix(qqbot): thread scoped media access through all outbound paths

Cover the seven QQBot outbound media paths that dropped core scoped
sandbox media access: gateway block/tool/streaming deliver, direct
text qqmedia tags, structured QQBOT_PAYLOAD, and the scoped workspace
relative/missing-voice path resolution. The low-level sendPhoto /
sendVideoMsg / sendDocument now consume core mediaAccess.readFile via
the shared outbound loader with explicit localRoots guard, so
host-mediated workspace media can be sent without widening QQBot FS
access. Voice stays on the existing SILK transcode path to avoid
bypassing QQ-required format conversion.

* fix(qqbot): thread workspaceDir into gateway reply media context

Gateway dispatchOutbound previously built gatewayMediaContext with only
mediaLocalRoots, so a sandboxed block reply that carried a relative
media tag (e.g. <qqmedia>report.docx</qqmedia>) reached the downstream
sender without mediaAccess.workspaceDir and was rejected by containment
checks before the file could be resolved against the agent workspace.

Resolve the agent workspace dir via the public plugin-sdk agent-runtime
helper and pass it through mediaAccess.workspaceDir alongside the
existing mediaLocalRoots so the gateway block reply, tool forwarding,
QQBOT_PAYLOAD, and official C2C streaming paths all share the same
scoped workspace resolution as direct outbound sends. No public plugin
SDK boundary is widened; the helper is an existing export.

Add a regression test that exercises a relative <qqmedia> tag in a
gateway block reply and confirms it resolves against the configured
agent workspace.

* fix(qqbot): wrap host-read media sends with structured error handling

* fix(qqbot): preserve host-read media quota errors

* fix(qqbot): preserve host-read quota fallback path

* fix(qqbot): resolve host-read quota fallback path

* test(qqbot): satisfy host-read media lint

* fix(qqbot): cover host-read voice media

* fix(qqbot): satisfy host-read voice lint

* Drop incidental formatting from PR merge

Keep the conflict-resolution update scoped to the QQBot media send files.

* fix(qqbot): map sandbox workspace media paths

* fix(qqbot): preserve host-read media roots

* fix(qqbot): scope sandbox media delivery roots

* fix(qqbot): map virtual media roots before host read

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

* fix(qqbot): scope structured payload media roots

* fix(qqbot): keep auto media helper internal

* docs(changelog): note QQBot scoped sandbox media fix (#92872) (thanks @zhangguiping-xydt)

---------

Co-authored-by: sliverp <870080352@qq.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-03 15:02:58 +08:00
Dallin Romney
59b08b4693
refactor(shared): consolidate remaining channel lazy loaders (#99302) 2026-07-02 19:08:11 -07:00
Peter Steinberger
24d94a54a5
fix: migrate QQBot credential backups to SQLite KV (#89597)
* fix: migrate qqbot credential backups to sqlite kv

* fix: complete qqbot sqlite migration cleanup

* fix: harden qqbot doctor state migration
2026-07-01 20:03:59 +01:00
Vincent Koc
3e2646a786
fix(release): use workspace host deps in release lockfile
(cherry picked from commit d377c0f910)
2026-06-30 15:54:12 -07:00
Vincent Koc
66e676d29b
chore(release): close out 2026.6.11 on main 2026-06-30 11:31:08 -07:00
Patrick Erichsen
56c2d637d9
fix(qqbot): tighten bundled skill guardrails (#98032)
Some checks are pending
CI / check-additional-runtime-topology-architecture (push) Blocked by required conditions
CI / check-session-accessor-boundary (push) Blocked by required conditions
CI / check-session-transcript-reader-boundary (push) Blocked by required conditions
CI / check-docs (push) Blocked by required conditions
CI / skills-python (push) Blocked by required conditions
CI / -4 (push) Blocked by required conditions
CI / -5 (push) Blocked by required conditions
CI / macos-swift (push) Blocked by required conditions
CI / ios-build (push) Blocked by required conditions
CI / -6 (push) Blocked by required conditions
CI / ci-timings-summary (push) Blocked by required conditions
ClawSweeper Dispatch / dispatch (push) Waiting to run
CodeQL / Security High (actions) (push) Waiting to run
CodeQL / Security High (channel-runtime-boundary) (push) Waiting to run
CodeQL / Security High (core-auth-secrets) (push) Waiting to run
CodeQL / Security High (mcp-process-tool-boundary) (push) Waiting to run
CodeQL / Security High (network-ssrf-boundary) (push) Waiting to run
CodeQL / Security High (plugin-trust-boundary) (push) Waiting to run
Docs Sync Publish Repo / sync-publish-repo (push) Waiting to run
Docs / docs (push) Waiting to run
OpenClaw Stable Main Closeout / Resolve stable release closeout inputs (push) Waiting to run
OpenClaw Stable Main Closeout / Verify stable main closeout (push) Blocked by required conditions
Plugin Init Scaffold Validation / Validate provider scaffold (push) Waiting to run
Plugin NPM Release / preview_plugins_npm (push) Waiting to run
Plugin NPM Release / Validate release publish approval (push) Blocked by required conditions
Plugin NPM Release / preview_plugin_pack (push) Blocked by required conditions
Plugin NPM Release / publish_plugins_npm (push) Blocked by required conditions
Workflow Sanity / no-tabs (push) Waiting to run
Workflow Sanity / actionlint (push) Waiting to run
Workflow Sanity / generated-doc-baselines (push) Waiting to run
2026-06-29 22:48:52 -07:00
Vincent Koc
c0ee7a1d20
test: gate symlink assertions on file symlink capability (#95531)
Co-authored-by: ANIRUDDHA ADAK <127435065+aniruddhaadak80@users.noreply.github.com>
2026-06-28 22:50:37 -07:00
ly-wang19
3e64d84712
fix(qqbot): treat escaped pipes as literal content when splitting table cells (#97429)
splitTableCells/splitPartialTableCells split on every '|', including a GFM
backslash-escaped pipe ('\|'), which is literal cell content rather than a
column delimiter. A cell containing '\|' was therefore mis-counted as multiple
columns, so the oversized-row fallback (renderTableRowAsFields) rendered the
trailing content under the wrong header.

Split via an escape-aware scan that treats '\|' as a literal '|' (and '\\' as
a literal backslash so a following '|' still delimits). Behavior is byte-for-byte
unchanged for any row without an escaped pipe, so existing chunking is preserved.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:03:52 -07:00
llagy009
d693ed4af3
fix(qqbot): truncate reminder job name on code-point boundary (#96575)
generateJobName truncated reminder content with String.slice(0, 20) on
a UTF-16 code-unit index, so an astral character (e.g. an emoji) landing
on the boundary was cut into a lone surrogate, producing a malformed
cron job name.

Truncate with the shared truncateUtf16Safe helper so a surrogate pair is
never split, keeping the existing 20-unit budget and ellipsis suffix.

Adds a test asserting the truncated job name contains no lone surrogate.
2026-06-27 09:30:52 -07:00
mushuiyu886
6e8f30c0e2
fix(qqbot): bound STT transcription JSON response (#96968) 2026-06-27 07:25:29 -07:00
joshavant
0a14444924
Bound successful provider response reads 2026-06-24 19:08:22 -05:00
Vincent Koc
0671c08900
chore(release): close out 2026.6.10 on main (#96271)
* chore(release): close out 2026.6.10 on main

* chore(release): align native app metadata for 2026.6.10

* chore(release): sync Android 2026.6.10 notes

* docs(changelog): preserve 2026.6.9 history

* docs(changelog): preserve 2026.6.9 history
2026-06-24 11:51:14 +08:00
Patrick Erichsen
0feffda3fc
fix(plugins): remove simpleicons icon color paths (#95987) 2026-06-23 12:16:02 -07:00
Sliverp
9535b102d3
Gate private QQBot group commands (#92154)
* fix: gate private qqbot group commands

* fix(qqbot): keep authorized stop urgent in groups

* fix(qqbot): preserve omitted group command level

* fix(qqbot): preserve ignore-other-mentions gate

* test(qqbot): avoid unbound mention gate mock

* fix(qqbot): close strict command visibility gaps

* fix(qqbot): gate private group commands and close strict command visibility gaps (#92154) (thanks @sliverp)
2026-06-23 17:20:50 +08:00
Patrick Erichsen
0a97f73402
feat: add bundled plugin icon manifest URLs (#95845) 2026-06-22 22:14:18 -07:00
ANIRUDDHA ADAK
82316c2f45
test: make qqbot symlinked media helper test robust on Windows
Gate the QQ Bot symlink-media helper regression test on actual file-symlink capability, so environments that cannot create file symlinks skip that specific test while capable hosts still run it.

Validation:
- Windows Vitest proof in the PR body: `extensions/qqbot/src/engine/utils/file-utils.test.ts` passed with 4 tests passed and 1 symlink test skipped when file symlinks were unavailable.
- Current CI is clean at `cb7d5a162e`, including the refreshed Real behavior proof run `27992101343`.

Co-authored-by: Aniruddha Adak <aniruddhaadak80@users.noreply.github.com>
Co-authored-by: openclaw-clownfish[bot] <280122609+openclaw-clownfish[bot]@users.noreply.github.com>
2026-06-23 07:54:53 +08:00
ly-wang19
d63389ccf6
fix(qqbot): recognize GFM table separators with one or two dashes (#95637)
`isTableSeparatorLine` required 3+ dashes per cell (`/^:?-{3,}:?$/`), but a
GFM delimiter cell needs only one or more dashes. So a valid table whose
separator used 1 or 2 dashes (e.g. `|--|--|`) was not recognized: the header
stayed pending and was silently overwritten by each following row, so the
table's header, separator, and every row but the last vanished from the sent
message.

Accept `-+` so valid GFM separators are recognized, matching the spec and the
sibling LINE channel. Every existing test separator already uses 3+ dashes, so
they are byte-identical.

Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:27:01 +00:00
Vincent Koc
b039e949b6
chore(release): close out 2026.6.9 2026-06-21 12:24:15 +08:00
Vincent Koc
5c19699cb2
chore(deadcode): remove qqbot duplicate wrappers 2026-06-20 16:41:41 +08:00
Vincent Koc
9e5ac0cea4
refactor(extensions): drop stale internal declarations 2026-06-20 02:52:05 +08:00
Vincent Koc
37962aac95
test(qqbot): keep stt temp helper on sdk surface 2026-06-19 17:03:16 +02:00
Vincent Koc
a876f8d073
fix(qqbot): bound chunked upload error bodies 2026-06-19 17:03:16 +02:00
Vincent Koc
e0d58d994d
fix(qqbot): bound stt error bodies 2026-06-19 16:44:51 +02:00
Vincent Koc
b16fd6bee7
test(qqbot): fix channel api bounded body assertion 2026-06-19 16:35:55 +02:00
Vincent Koc
51ebe87a09
fix(qqbot): guard channel api fetches 2026-06-19 16:30:53 +02:00
Vincent Koc
ed8ab712dc
fix(qqbot): guard api client fetches 2026-06-19 16:14:19 +02:00
Vincent Koc
8594af21e9
fix(qqbot): bound token response bodies 2026-06-19 16:14:19 +02:00
Vincent Koc
772158c716
fix(qqbot): bound api error bodies 2026-06-19 15:30:29 +02:00
Vincent Koc
350aa7c3be
refactor(extensions): remove unused helper facades 2026-06-19 01:05:26 +08:00
Vincent Koc
58b77e787d
refactor(extensions): remove unused helper exports 2026-06-18 15:05:35 +08:00
Vincent Koc
0aa6b8ca0d
refactor(qqbot): dedupe approval capability state 2026-06-18 13:03:55 +08:00
Vincent Koc
4681a559c0
refactor(qqbot): hide response timeout default 2026-06-17 10:47:01 +08:00
Vincent Koc
00d2452fac
chore(release): refresh npm shrinkwrap versions 2026-06-17 07:32:37 +08:00
Vincent Koc
6774e7f259
chore(release): sync main to 2026.6.8 2026-06-17 07:25:30 +08:00
Shakker
920e6a8eec
chore: set version 2026.6.9 2026-06-16 19:54:07 +01:00
Vincent Koc
ff5d6571f2
test(qqbot): avoid bare temp dirs in media path coverage 2026-06-16 20:44:48 +02:00
ZengWen-DT
cfdcd5cdfd
fix(qqbot): deliver cron auto-TTS voice by trusting OpenClaw temp root (#92947)
QQBot is the only channel that root-sandboxes outbound local files. Its three
gate sites (resolveOutboundMediaPath, the voice send re-check, and
structured-payload validation) only trusted the QQ Bot media storage roots, so
framework-generated scratch media written under OpenClaw's hardened temp root
(e.g. cron auto-TTS voice files from speech-core) was rejected. The send then
returned a no-identity error, the message was silently lost, yet cron still
recorded it as delivered.

Add one shared resolver (resolveTrustedOutboundMediaPath) that also trusts the
preferred OpenClaw temp root — already a sanctioned media root in core
(buildMediaLocalRoots) — and route all three gates through it so the trust set
agrees everywhere. Fixes #92816.

Co-authored-by: zengwen <zeng_wen@foxmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 02:11:52 +08:00