Skip to content

Commit d0690f3

Browse files
committed
Auto-merge upstream openclaw/openclaw
2 parents b23ee7f + f17f319 commit d0690f3

34 files changed

Lines changed: 493 additions & 261 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Docs: https://docs.openclaw.ai
88

99
- Providers/Anthropic: skip `service_tier` injection for OAuth-authenticated stream wrapper requests so Claude OAuth requests stop failing with HTTP 401. (#60356) thanks @openperf.
1010
- Agents/exec: preserve explicit `host=node` routing under elevated defaults when `tools.exec.host=auto`, and fail loud on invalid elevated cross-host overrides. (#61739) Thanks @obviyus.
11+
- Docs/i18n: remove the zh-CN homepage redirect override so Mintlify can resolve the localized Chinese homepage without self-redirecting `/zh-CN/index`.
1112
## 2026.4.5
1213

1314
### Breaking

docs/channels/slack.md

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,13 @@ Status: production-ready for DMs + channels via Slack app integrations. Default
2626
<Tabs>
2727
<Tab title="Socket Mode (default)">
2828
<Steps>
29-
<Step title="Create Slack app and tokens">
30-
In Slack app settings:
29+
<Step title="Create a new Slack app">
30+
In Slack app settings press the **[Create New App](https://api.slack.com/apps/new)** button:
3131

32-
- enable **Socket Mode**
33-
- create **App Token** (`xapp-...`) with `connections:write`
34-
- install app and copy **Bot Token** (`xoxb-...`)
32+
- choose **from a manifest** and select a workspace for your app
33+
- paste the [example manifest](#manifest-and-scope-checklist) from below and continue to create
34+
- generate an **App-Level Token** (`xapp-...`) with `connections:write`
35+
- install app and copy the **Bot Token** (`xoxb-...`) shown
3536
</Step>
3637

3738
<Step title="Configure OpenClaw">
@@ -58,19 +59,6 @@ SLACK_BOT_TOKEN=xoxb-...
5859

5960
</Step>
6061

61-
<Step title="Subscribe app events">
62-
Subscribe bot events for:
63-
64-
- `app_mention`
65-
- `message.channels`, `message.groups`, `message.im`, `message.mpim`
66-
- `reaction_added`, `reaction_removed`
67-
- `member_joined_channel`, `member_left_channel`
68-
- `channel_rename`
69-
- `pin_added`, `pin_removed`
70-
71-
Also enable App Home **Messages Tab** for DMs.
72-
</Step>
73-
7462
<Step title="Start gateway">
7563

7664
```bash
@@ -84,15 +72,17 @@ openclaw gateway
8472

8573
<Tab title="HTTP Request URLs">
8674
<Steps>
87-
<Step title="Configure Slack app for HTTP">
75+
<Step title="Create a new Slack app">
76+
In Slack app settings press the **[Create New App](https://api.slack.com/apps/new)** button:
8877

89-
- set mode to HTTP (`channels.slack.mode="http"`)
90-
- copy Slack **Signing Secret**
91-
- set Event Subscriptions + Interactivity + Slash command Request URL to the same webhook path (default `/slack/events`)
78+
- choose **from a manifest** and select a workspace for your app
79+
- paste the [example manifest](#manifest-and-scope-checklist) and update the URLs before create
80+
- save the **Signing Secret** for request verification
81+
- install app and copy the **Bot Token** (`xoxb-...`) shown
9282

9383
</Step>
9484

95-
<Step title="Configure OpenClaw HTTP mode">
85+
<Step title="Configure OpenClaw">
9686

9787
```json5
9888
{
@@ -108,12 +98,20 @@ openclaw gateway
10898
}
10999
```
110100

101+
<Note>
102+
Use unique webhook paths for multi-account HTTP
103+
104+
Give each account a distinct `webhookPath` (default `/slack/events`) so registrations do not collide.
105+
</Note>
106+
111107
</Step>
112108

113-
<Step title="Use unique webhook paths for multi-account HTTP">
114-
Per-account HTTP mode is supported.
109+
<Step title="Start gateway">
110+
111+
```bash
112+
openclaw gateway
113+
```
115114

116-
Give each account a distinct `webhookPath` so registrations do not collide.
117115
</Step>
118116
</Steps>
119117

docs/docs.json

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,6 @@
124124
"source": "/context",
125125
"destination": "/concepts/context"
126126
},
127-
{
128-
"source": "/zh-CN",
129-
"destination": "/zh-CN/index"
130-
},
131-
{
132-
"source": "/zh-CN/",
133-
"destination": "/zh-CN/index"
134-
},
135127
{
136128
"source": "/compaction",
137129
"destination": "/concepts/compaction"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Keep bundled channel entry imports narrow so bootstrap/discovery paths do
2+
// not drag the broader Mattermost helper surfaces into lightweight plugin loads.
3+
export { mattermostPlugin } from "./src/channel.js";

extensions/mattermost/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default defineBundledChannelEntry({
1818
description: "Mattermost channel plugin",
1919
importMetaUrl: import.meta.url,
2020
plugin: {
21-
specifier: "./src/channel.js",
21+
specifier: "./channel-plugin-api.js",
2222
exportName: "mattermostPlugin",
2323
},
2424
runtime: {

extensions/mattermost/setup-entry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
33
export default defineBundledChannelSetupEntry({
44
importMetaUrl: import.meta.url,
55
plugin: {
6-
specifier: "./src/channel.js",
6+
specifier: "./channel-plugin-api.js",
77
exportName: "mattermostPlugin",
88
},
99
});

src/auto-reply/reply/commands-gating.test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ const getConfigOverridesMock = vi.hoisted(() => vi.fn(() => ({})));
2222
const getConfigValueAtPathMock = vi.hoisted(() => vi.fn());
2323
const parseConfigPathMock = vi.hoisted(() => vi.fn());
2424
const setConfigValueAtPathMock = vi.hoisted(() => vi.fn());
25-
const resolveConfigWriteDeniedTextMock = vi.hoisted(() => vi.fn(() => undefined));
25+
const resolveConfigWriteDeniedTextMock = vi.hoisted(() =>
26+
vi.fn<(...args: never[]) => string | null>(() => null),
27+
);
2628
const isInternalMessageChannelMock = vi.hoisted(() => vi.fn(() => false));
2729

2830
vi.mock("../../agents/agent-scope.js", () => ({
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export {
22
killAllControlledSubagentRuns,
33
killControlledSubagentRun,
4+
listControlledSubagentRuns,
45
sendControlledSubagentMessage,
56
steerControlledSubagentRun,
67
} from "../../agents/subagent-control.js";

src/auto-reply/reply/commands-subagents-focus.test.ts

Lines changed: 69 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
22
import type { OpenClawConfig } from "../../config/config.js";
3+
import type { SessionEntry } from "../../config/sessions/types.js";
34
import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js";
45
import { handleSubagentsFocusAction } from "./commands-subagents/action-focus.js";
56
import { handleSubagentsUnfocusAction } from "./commands-subagents/action-unfocus.js";
7+
import type { HandleCommandsParams } from "./commands-types.js";
8+
import type { InlineDirectives } from "./directive-handling.js";
69

710
const THREAD_CHANNEL = "thread-chat";
811
const ROOM_CHANNEL = "room-chat";
@@ -150,45 +153,91 @@ function createSessionBindingCapabilities() {
150153
};
151154
}
152155

156+
function buildCommandParams(params?: {
157+
cfg?: OpenClawConfig;
158+
chatType?: string;
159+
senderId?: string;
160+
sessionEntry?: SessionEntry;
161+
}): HandleCommandsParams {
162+
const directives: InlineDirectives = {
163+
cleaned: "",
164+
hasThinkDirective: false,
165+
hasVerboseDirective: false,
166+
hasFastDirective: false,
167+
hasReasoningDirective: false,
168+
hasElevatedDirective: false,
169+
hasExecDirective: false,
170+
hasExecOptions: false,
171+
invalidExecHost: false,
172+
invalidExecSecurity: false,
173+
invalidExecAsk: false,
174+
invalidExecNode: false,
175+
hasStatusDirective: false,
176+
hasModelDirective: false,
177+
hasQueueDirective: false,
178+
queueReset: false,
179+
hasQueueOptions: false,
180+
};
181+
return {
182+
cfg: params?.cfg ?? baseCfg,
183+
ctx: {
184+
ChatType: params?.chatType ?? "group",
185+
},
186+
command: {
187+
surface: "whatsapp",
188+
channel: "whatsapp",
189+
ownerList: [],
190+
senderIsOwner: true,
191+
isAuthorizedSender: true,
192+
senderId: params?.senderId ?? "user-1",
193+
rawBodyNormalized: "",
194+
commandBodyNormalized: "",
195+
},
196+
directives,
197+
elevated: { enabled: false, allowed: false, failures: [] },
198+
sessionEntry: params?.sessionEntry,
199+
sessionKey: "agent:main:main",
200+
workspaceDir: "/tmp/openclaw-subagents-focus",
201+
defaultGroupActivation: () => "mention",
202+
resolvedVerboseLevel: "off",
203+
resolvedReasoningLevel: "off",
204+
resolveDefaultThinkingLevel: async () => undefined,
205+
provider: "whatsapp",
206+
model: "test-model",
207+
contextTokens: 0,
208+
isGroup: true,
209+
};
210+
}
211+
153212
function buildFocusContext(params?: {
154213
cfg?: OpenClawConfig;
155214
chatType?: string;
156215
senderId?: string;
157216
token?: string;
158217
}) {
159218
return {
160-
params: {
161-
cfg: params?.cfg ?? baseCfg,
162-
ctx: {
163-
ChatType: params?.chatType ?? "group",
164-
},
165-
command: {
166-
senderId: params?.senderId ?? "user-1",
167-
},
168-
},
219+
params: buildCommandParams({
220+
cfg: params?.cfg,
221+
chatType: params?.chatType,
222+
senderId: params?.senderId,
223+
}),
169224
handledPrefix: "/focus",
170225
requesterKey: "agent:main:main",
171226
runs: [],
172227
restTokens: [params?.token ?? "codex-acp"],
173-
} as Parameters<typeof handleSubagentsFocusAction>[0];
228+
} satisfies Parameters<typeof handleSubagentsFocusAction>[0];
174229
}
175230

176231
function buildUnfocusContext(params?: { senderId?: string }) {
177232
return {
178-
params: {
179-
cfg: baseCfg,
180-
ctx: {
181-
ChatType: "group",
182-
},
183-
command: {
184-
senderId: params?.senderId ?? "user-1",
185-
},
186-
},
233+
params: buildCommandParams({
234+
senderId: params?.senderId,
235+
}),
187236
handledPrefix: "/unfocus",
188237
requesterKey: "agent:main:main",
189238
runs: [],
190239
restTokens: [],
191-
} as Parameters<typeof handleSubagentsUnfocusAction>[0];
240+
} satisfies Parameters<typeof handleSubagentsUnfocusAction>[0];
192241
}
193242

194243
describe("focus actions", () => {

src/auto-reply/reply/commands-subagents-spawn-action.test.ts

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { beforeEach, describe, expect, it, vi } from "vitest";
22
import type { SpawnSubagentResult } from "../../agents/subagent-spawn.js";
33
import type { OpenClawConfig } from "../../config/config.js";
4+
import type { SessionEntry } from "../../config/sessions/types.js";
45
import { handleSubagentsSpawnAction } from "./commands-subagents/action-spawn.js";
6+
import type { HandleCommandsParams } from "./commands-types.js";
7+
import type { InlineDirectives } from "./directive-handling.js";
58

69
const spawnSubagentDirectMock = vi.hoisted(() => vi.fn());
710

@@ -35,30 +38,68 @@ function buildContext(params?: {
3538
requesterKey?: string;
3639
restTokens?: string[];
3740
commandTo?: string | undefined;
38-
context?: Record<string, unknown>;
39-
sessionEntry?: Record<string, unknown> | undefined;
41+
context?: Partial<HandleCommandsParams["ctx"]>;
42+
sessionEntry?: SessionEntry | undefined;
4043
}) {
44+
const directives: InlineDirectives = {
45+
cleaned: "",
46+
hasThinkDirective: false,
47+
hasVerboseDirective: false,
48+
hasFastDirective: false,
49+
hasReasoningDirective: false,
50+
hasElevatedDirective: false,
51+
hasExecDirective: false,
52+
hasExecOptions: false,
53+
invalidExecHost: false,
54+
invalidExecSecurity: false,
55+
invalidExecAsk: false,
56+
invalidExecNode: false,
57+
hasStatusDirective: false,
58+
hasModelDirective: false,
59+
hasQueueDirective: false,
60+
queueReset: false,
61+
hasQueueOptions: false,
62+
};
63+
const ctx = {
64+
OriginatingChannel: "whatsapp",
65+
OriginatingTo: "channel:origin",
66+
AccountId: "default",
67+
MessageThreadId: "thread-1",
68+
...params?.context,
69+
};
4170
return {
4271
params: {
4372
cfg: params?.cfg ?? baseCfg,
73+
ctx,
4474
command: {
75+
surface: "whatsapp",
4576
channel: "whatsapp",
77+
ownerList: [],
78+
senderIsOwner: true,
79+
isAuthorizedSender: true,
80+
rawBodyNormalized: "",
81+
commandBodyNormalized: "",
4682
to: params?.commandTo ?? "channel:command",
4783
},
48-
ctx: {
49-
OriginatingChannel: "whatsapp",
50-
OriginatingTo: "channel:origin",
51-
AccountId: "default",
52-
MessageThreadId: "thread-1",
53-
...params?.context,
54-
},
84+
directives,
85+
elevated: { enabled: false, allowed: false, failures: [] },
86+
sessionKey: "agent:main:main",
87+
workspaceDir: "/tmp/openclaw-subagents-spawn",
88+
defaultGroupActivation: () => "mention",
89+
resolvedVerboseLevel: "off",
90+
resolvedReasoningLevel: "off",
91+
resolveDefaultThinkingLevel: async () => undefined,
92+
provider: "whatsapp",
93+
model: "test-model",
94+
contextTokens: 0,
95+
isGroup: true,
5596
...(params?.sessionEntry ? { sessionEntry: params.sessionEntry } : {}),
5697
},
5798
handledPrefix: "/subagents",
5899
requesterKey: params?.requesterKey ?? "agent:main:main",
59100
runs: [],
60101
restTokens: params?.restTokens ?? ["beta", "do", "the", "thing"],
61-
} as Parameters<typeof handleSubagentsSpawnAction>[0];
102+
} satisfies Parameters<typeof handleSubagentsSpawnAction>[0];
62103
}
63104

64105
describe("subagents spawn action", () => {
@@ -152,6 +193,8 @@ describe("subagents spawn action", () => {
152193
await handleSubagentsSpawnAction(
153194
buildContext({
154195
sessionEntry: {
196+
sessionId: "session-1",
197+
updatedAt: Date.now(),
155198
groupId: "group-1",
156199
groupChannel: "#group-channel",
157200
space: "workspace-1",

0 commit comments

Comments
 (0)