Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c1bb07b
fix(slack): route system events to bound agent sessions (#34045)
vincentkoc Mar 4, 2026
88ee571
Delete changelog/fragments directory
vincentkoc Mar 4, 2026
dc8253a
fix(memory): serialize local embedding initialization to avoid duplic…
SubtleSpark Mar 4, 2026
3fa43ec
fix(model): propagate custom provider headers to model objects (#27490)
Sid-Qin Mar 4, 2026
4fb4049
fix(daemon): handle systemctl is-enabled exit 4 (not-found) on Ubuntu…
Yuandiaodiaodiao Mar 4, 2026
c8ebd48
fix(node-host): sync rawCommand with hardened argv after executable p…
Sid-Qin Mar 4, 2026
76bfd9b
Agents: add generic poll-vote action support
gumadeiras Mar 4, 2026
7597fc5
fix(ollama): pass provider headers to Ollama stream function (#24285)
Feb 23, 2026
7531a3e
test(ollama): add default header precedence coverage
shakkernerd Mar 4, 2026
e6f0203
chore(changelog): add PR entry openclaw#24337 thanks @echoVic
shakkernerd Mar 4, 2026
efdf2ca
Outbound: allow text-only plugin adapters
liuxiaopai-ai Mar 3, 2026
bb07b2b
Outbound: avoid empty multi-media fallback sends
liuxiaopai-ai Mar 3, 2026
a970cae
chore(changelog): align outbound adapter entry openclaw#32788 thanks …
shakkernerd Mar 4, 2026
698c200
fix(outbound): fail media-only text-only adapter fallback
shakkernerd Mar 4, 2026
2123265
chore(changelog): clarify outbound media-only fallback openclaw#32788…
shakkernerd Mar 4, 2026
4cc293d
fix(review): enforce behavioral sweep validation
shakkernerd Mar 4, 2026
2b98cb6
Fix gateway restart false timeouts on Debian/systemd (#34874)
vincentkoc Mar 4, 2026
df0f2e3
Compaction/Safeguard: require structured summary headings (#25555)
rodrigouroz Mar 4, 2026
53b2479
Fix Linux daemon install checks when systemd user bus env is missing …
vincentkoc Mar 4, 2026
4242c51
agents: preserve totalTokens on request failure instead of using cont…
RealKai42 Mar 4, 2026
96021a2
fix: align AGENTS.md template section names with post-compaction extr…
echoVic Mar 4, 2026
8c5692a
Changelog: add daemon systemd user-bus fallback entry (#34884)
vincentkoc Mar 4, 2026
9c68470
Changelog: add gateway restart health entry (#34874)
vincentkoc Mar 4, 2026
b3fb881
fix: finalize spanish locale support
dvrshil Mar 4, 2026
ed05810
fix: add spanish locale support (#35038) (thanks @DaoPromociones)
dvrshil Mar 4, 2026
809f951
fix(deps): patch hono transitive audit vulnerabilities
shakkernerd Mar 4, 2026
da0e245
fix(security): avoid prototype-chain account path checks (#34982)
HOYALIM Mar 5, 2026
4d06c90
fix(deps): bump tar to 7.5.10
shakkernerd Mar 5, 2026
4989485
docs(changelog): document dependency security fixes
shakkernerd Mar 5, 2026
432e022
fix: restore auto-reply system events timeline (#34794) (thanks @anis…
anisoptera Mar 5, 2026
63ce7c7
fix(feishu): comprehensive reply mechanism — outbound replyToId forwa…
guoqunabc Mar 5, 2026
68e68bf
fix(feishu): use msg_type media for mp4 video (fixes #33674) (#33720)
polooooo Mar 5, 2026
8b8167d
fix(agents): bypass pendingDescendantRuns guard for cron announce del…
Sid-Qin Mar 5, 2026
3bf6ed1
Feishu: harden streaming merge semantics and final reply dedupe (#33245)
rexl2018 Mar 5, 2026
1059b40
fix: cron backup should preserve pre-edit snapshot (#35195) (#35234)
0xsline Mar 5, 2026
79d00ae
fix(cron): stabilize restart catch-up replay semantics (#35351)
Takhoffman Mar 5, 2026
28dc2e8
cron: narrow startup replay backoff guard (#35391)
Takhoffman Mar 5, 2026
cc5dad8
cron: unify stale-run recovery and preserve manual-run every anchors …
Takhoffman Mar 5, 2026
4bd3469
refactor(telegram): remove unused webhook callback helper (#27816)
huntharo Mar 5, 2026
5d5fa0d
fix(pr): make review claim step required
shakkernerd Mar 5, 2026
48decef
fix(skills): deduplicate slash commands by skillName across all inter…
Feb 26, 2026
fb4f52b
style: fix formatting in skill-commands.test.ts and provider.ts
Feb 26, 2026
b5a94d2
style(skills): align formatting cleanup for dedupe changes
shakkernerd Mar 5, 2026
1805735
chore(changelog): add dedupe note openclaw#27521 thanks @shivama205
shakkernerd Mar 5, 2026
987e473
fix(agents): detect Venice provider proxying xAI/Grok models for sche…
Sid-Qin Mar 5, 2026
ce0c131
fix(agents): decode HTML entities in xAI/Grok tool call arguments (#3…
Sid-Qin Mar 5, 2026
d9b69a6
fix(agents): guard promoteThinkingTagsToBlocks against malformed cont…
Sid-Qin Mar 5, 2026
8891e1e
fix(web-ui): render Accounts schema node properly (#35380)
stakeswky Mar 5, 2026
463fd47
fix(agents): guard context pruning against malformed thinking blocks …
Sid-Qin Mar 5, 2026
c4dab17
fix(gateway): prevent internal route leakage in chat.send
alexyyyander Mar 5, 2026
3a6b412
fix(gateway): pass actual version to Control UI client instead of dev…
Sid-Qin Mar 5, 2026
60849f3
chore(pr): enforce changelog placement and reduce merge sync churn
shakkernerd Mar 5, 2026
2c8ee59
TTS: add baseUrl support to OpenAI TTS config (#34321)
RealKai42 Mar 5, 2026
3a2bb80
daemon: relax systemd user availability and enabled detection
Octane0411 Mar 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup-node-env/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ runs:
if: inputs.install-bun == 'true'
uses: oven-sh/setup-bun@v2
with:
bun-version: "1.3.9+cf6cdbbba"
bun-version: "1.3.9"

- name: Runtime versions
shell: bash
Expand Down
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
- 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 + what’s covered: `docs/testing.md`.
- Changelog: user-facing changes only; no internal/meta notes (version alignment, appcast reminders, release process).
- Changelog placement: in the active version block, append new entries to the end of the target section (`### Changes` or `### Fixes`); do not insert new entries at the top of a section.
- 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.

Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion changelog/fragments/ios-live-activity-status-cleanup.md

This file was deleted.

1 change: 0 additions & 1 deletion changelog/fragments/pr-30356.md

This file was deleted.

1 change: 1 addition & 0 deletions changelog/fragments/pr-feishu-reply-mechanism.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Feishu reply routing now uses one canonical reply-target path across inbound and outbound flows: normal groups reply to the triggering message while topic-mode groups stay on topic roots, outbound sends preserve `replyToId`/`threadId`, withdrawn reply targets fall back to direct sends, and cron duplicate suppression normalizes Feishu/Lark target IDs consistently (#32980, #32958, #33572, #33526; #33789, #33575, #33515, #33161). Thanks @guoqunabc, @bmendonca3, @MunemHashmi, and @Jimmy-xuzimo.
4 changes: 2 additions & 2 deletions docs/reference/templates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ This folder is home. Treat it that way.

If `BOOTSTRAP.md` exists, that's your birth certificate. Follow it, figure out who you are, then delete it. You won't need it again.

## Every Session
## Session Startup

Before doing anything else:

Expand Down Expand Up @@ -52,7 +52,7 @@ Capture what matters. Decisions, context, things to remember. Skip the secrets u
- When you make a mistake → document it so future-you doesn't repeat it
- **Text > Brain** 📝

## Safety
## Red Lines

- Don't exfiltrate private data. Ever.
- Don't run destructive commands without asking.
Expand Down
4 changes: 2 additions & 2 deletions docs/zh-CN/reference/templates/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ x-i18n:

如果 `BOOTSTRAP.md` 存在,那就是你的"出生证明"。按照它的指引,弄清楚你是谁,然后删除它。你不会再需要它了。

## 每次会话
## 会话启动

在做任何事情之前:

Expand Down Expand Up @@ -58,7 +58,7 @@ x-i18n:
- 当你犯了错误 → 记录下来,这样未来的你不会重蹈覆辙
- **文件 > 大脑** 📝

## 安全
## 红线

- 不要泄露隐私数据。绝对不要。
- 不要在未询问的情况下执行破坏性命令。
Expand Down
114 changes: 114 additions & 0 deletions extensions/feishu/src/bot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1517,6 +1517,120 @@ describe("handleFeishuMessage command authorization", () => {
);
});

it("replies to triggering message in normal group even when root_id is present (#32980)", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);

const cfg: ClawdbotConfig = {
channels: {
feishu: {
groups: {
"oc-group": {
requireMention: false,
groupSessionScope: "group",
},
},
},
},
} as ClawdbotConfig;

const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-normal-user" } },
message: {
message_id: "om_quote_reply",
root_id: "om_original_msg",
chat_id: "oc-group",
chat_type: "group",
message_type: "text",
content: JSON.stringify({ text: "hello in normal group" }),
},
};

await dispatchMessage({ cfg, event });

expect(mockCreateFeishuReplyDispatcher).toHaveBeenCalledWith(
expect.objectContaining({
replyToMessageId: "om_quote_reply",
rootId: "om_original_msg",
}),
);
});

it("replies to topic root in topic-mode group with root_id", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);

const cfg: ClawdbotConfig = {
channels: {
feishu: {
groups: {
"oc-group": {
requireMention: false,
groupSessionScope: "group_topic",
},
},
},
},
} as ClawdbotConfig;

const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-topic-user" } },
message: {
message_id: "om_topic_reply",
root_id: "om_topic_root",
chat_id: "oc-group",
chat_type: "group",
message_type: "text",
content: JSON.stringify({ text: "hello in topic group" }),
},
};

await dispatchMessage({ cfg, event });

expect(mockCreateFeishuReplyDispatcher).toHaveBeenCalledWith(
expect.objectContaining({
replyToMessageId: "om_topic_root",
rootId: "om_topic_root",
}),
);
});

it("replies to topic root in topic-sender group with root_id", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);

const cfg: ClawdbotConfig = {
channels: {
feishu: {
groups: {
"oc-group": {
requireMention: false,
groupSessionScope: "group_topic_sender",
},
},
},
},
} as ClawdbotConfig;

const event: FeishuMessageEvent = {
sender: { sender_id: { open_id: "ou-topic-sender-user" } },
message: {
message_id: "om_topic_sender_reply",
root_id: "om_topic_sender_root",
chat_id: "oc-group",
chat_type: "group",
message_type: "text",
content: JSON.stringify({ text: "hello in topic sender group" }),
},
};

await dispatchMessage({ cfg, event });

expect(mockCreateFeishuReplyDispatcher).toHaveBeenCalledWith(
expect.objectContaining({
replyToMessageId: "om_topic_sender_root",
rootId: "om_topic_sender_root",
}),
);
});

it("forces thread replies when inbound message contains thread_id", async () => {
mockShouldComputeCommandAuthorized.mockReturnValue(false);

Expand Down
18 changes: 17 additions & 1 deletion extensions/feishu/src/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1337,7 +1337,23 @@ export async function handleFeishuMessage(params: {
const messageCreateTimeMs = event.message.create_time
? parseInt(event.message.create_time, 10)
: undefined;
const replyTargetMessageId = ctx.rootId ?? ctx.messageId;
// Determine reply target based on group session mode:
// - Topic-mode groups (group_topic / group_topic_sender): reply to the topic
// root so the bot stays in the same thread.
// - Groups with explicit replyInThread config: reply to the root so the bot
// stays in the thread the user expects.
// - Normal groups (auto-detected threadReply from root_id): reply to the
// triggering message itself. Using rootId here would silently push the
// reply into a topic thread invisible in the main chat view (#32980).
const isTopicSession =
isGroup &&
(groupSession?.groupSessionScope === "group_topic" ||
groupSession?.groupSessionScope === "group_topic_sender");
const configReplyInThread =
isGroup &&
(groupConfig?.replyInThread ?? feishuCfg?.replyInThread ?? "disabled") === "enabled";
const replyTargetMessageId =
isTopicSession || configReplyInThread ? (ctx.rootId ?? ctx.messageId) : ctx.messageId;
const threadReply = isGroup ? (groupSession?.threadReply ?? false) : false;

if (broadcastAgents) {
Expand Down
13 changes: 8 additions & 5 deletions extensions/feishu/src/media.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ describe("sendMediaFeishu msg_type routing", () => {
messageResourceGetMock.mockResolvedValue(Buffer.from("resource-bytes"));
});

it("uses msg_type=file for mp4", async () => {
it("uses msg_type=media for mp4 video", async () => {
await sendMediaFeishu({
cfg: {} as any,
to: "user:ou_target",
Expand All @@ -129,7 +129,7 @@ describe("sendMediaFeishu msg_type routing", () => {

expect(messageCreateMock).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ msg_type: "file" }),
data: expect.objectContaining({ msg_type: "media" }),
}),
);
});
Expand Down Expand Up @@ -176,7 +176,7 @@ describe("sendMediaFeishu msg_type routing", () => {
);
});

it("uses msg_type=file when replying with mp4", async () => {
it("uses msg_type=media when replying with mp4", async () => {
await sendMediaFeishu({
cfg: {} as any,
to: "user:ou_target",
Expand All @@ -188,7 +188,7 @@ describe("sendMediaFeishu msg_type routing", () => {
expect(messageReplyMock).toHaveBeenCalledWith(
expect.objectContaining({
path: { message_id: "om_parent" },
data: expect.objectContaining({ msg_type: "file" }),
data: expect.objectContaining({ msg_type: "media" }),
}),
);

Expand All @@ -208,7 +208,10 @@ describe("sendMediaFeishu msg_type routing", () => {
expect(messageReplyMock).toHaveBeenCalledWith(
expect.objectContaining({
path: { message_id: "om_parent" },
data: expect.objectContaining({ msg_type: "file", reply_in_thread: true }),
data: expect.objectContaining({
msg_type: "media",
reply_in_thread: true,
}),
}),
);
});
Expand Down
8 changes: 4 additions & 4 deletions extensions/feishu/src/media.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,8 +328,8 @@ export async function sendFileFeishu(params: {
cfg: ClawdbotConfig;
to: string;
fileKey: string;
/** Use "audio" for audio files, "file" for documents and video */
msgType?: "file" | "audio";
/** Use "audio" for audio, "media" for video (mp4), "file" for documents */
msgType?: "file" | "audio" | "media";
replyToMessageId?: string;
replyInThread?: boolean;
accountId?: string;
Expand Down Expand Up @@ -467,8 +467,8 @@ export async function sendMediaFeishu(params: {
fileType,
accountId,
});
// Feishu API: opus -> "audio", everything else (including video) -> "file"
const msgType = fileType === "opus" ? "audio" : "file";
// Feishu API: opus -> "audio", mp4/video -> "media" (playable), others -> "file"
const msgType = fileType === "opus" ? "audio" : fileType === "mp4" ? "media" : "file";
return sendFileFeishu({
cfg,
to,
Expand Down
Loading