Skip to content

Commit eefa61b

Browse files
committed
Auto-merge upstream openclaw/openclaw
2 parents 479fe14 + 8c7f17b commit eefa61b

30 files changed

Lines changed: 846 additions & 172 deletions

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ Docs: https://docs.openclaw.ai
2929
- Outbound/delivery-queue: persist the originating outbound `session` context on queued delivery entries and replay it during recovery, so write-ahead-queued sends keep their original outbound media policy context after restart instead of evaluating against a missing session. (#66025) Thanks @eleqtrizit.
3030
- Auto-reply/queue: split collect-mode followup drains into contiguous groups by per-message authorization context (sender id, owner status, exec/bash-elevated overrides), so queued items from different senders or exec configs no longer execute under the last queued run's owner-only and exec-approval context. (#66024) Thanks @eleqtrizit.
3131
- Dreaming/memory-core: require a live queued Dreaming cron event before the heartbeat hook runs the sweep, so managed Dreaming no longer replays on later heartbeats after the scheduled run was already consumed. (#66139) Thanks @mbelinky.
32+
- Control UI/Dreaming: stop Imported Insights and Memory Palace from calling optional `memory-wiki` gateway methods when the plugin is off, and refresh config before wiki reloads so the Dreaming tab stops showing misleading unknown-method failures. (#66140) Thanks @mbelinky.
33+
- Agents/tools: only mark streamed unknown-tool retries as counted when a streamed message actually classifies an unavailable tool, and keep incomplete streamed tool names from resetting the retry streak before the final assistant message arrives. (#66145) Thanks @dutifulbob.
3234

3335
## 2026.4.12
3436

@@ -270,6 +272,7 @@ Docs: https://docs.openclaw.ai
270272
- Daemon/launchd: keep `openclaw gateway stop` persistent without uninstalling the macOS LaunchAgent, re-enable it on explicit restart or repair, and harden launchd label handling. (#64447) Thanks @ngutman.
271273
- Plugins/context engines: preserve `plugins.slots.contextEngine` through normalization and keep explicitly selected workspace context-engine plugins enabled, so loader diagnostics and plugin activation stop dropping that slot selection. (#64192) Thanks @hclsys.
272274
- Heartbeat: stop top-level `interval:` and `prompt:` fields outside the `tasks:` block from bleeding into the last parsed heartbeat task. (#64488) Thanks @Rahulkumar070.
275+
- Slack/plugin commands: include plugin-registered slash commands in Slack native command registration when Slack native commands are enabled. (#64578) Thanks @rafaelreis-r.
273276
- Agents/OpenAI replay: preserve malformed function-call arguments in stored assistant history, avoid double-encoding preserved raw strings on replay, and coerce replayed string args back to objects at Anthropic and Google provider boundaries. (#61956) Thanks @100yenadmin.
274277
- Heartbeat/config: accept and honor `agents.defaults.heartbeat.timeoutSeconds` and per-agent heartbeat timeout overrides for heartbeat agent turns. (#64491) Thanks @cedillarack.
275278
- CLI/devices: make implicit `openclaw devices approve` selection preview-only and require approving the exact request ID, preventing latest-request races during device pairing. (#64160) Thanks @coygeek.
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
600f05b14825fa01eb9d63ab6cab5f33c74ff44a48cab5c65457ab08e5b0e91a plugin-sdk-api-baseline.json
2-
99d649a86a30756b18b91686f3683e6e829c5e316e1370266ec4fee344bc55cb plugin-sdk-api-baseline.jsonl
1+
42a93d8368fd40f6bbe3045ba89b84a28e1131c700d4e57580febd3e773b23a4 plugin-sdk-api-baseline.json
2+
515333c277b725abaccf4fd5ab8c5e58b2de39b26e1fe4738f31852fcf789c96 plugin-sdk-api-baseline.jsonl

extensions/slack/src/monitor/slash.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createChannelReplyPipeline } from "openclaw/plugin-sdk/channel-reply-pi
33
import {
44
resolveCommandAuthorizedFromAuthorizers,
55
resolveNativeCommandSessionTargets,
6+
listProviderPluginCommandSpecs,
67
} from "openclaw/plugin-sdk/command-auth";
78
import { type ChatCommandDefinition, type CommandArgs } from "openclaw/plugin-sdk/command-auth";
89
import {
@@ -670,6 +671,17 @@ export async function registerSlackMonitorSlashCommands(params: {
670671
skillCommands,
671672
provider: "slack",
672673
});
674+
const existingNativeNames = new Set(
675+
nativeCommands.map((c) => normalizeLowercaseStringOrEmpty(c.name)).filter(Boolean),
676+
);
677+
for (const pluginCommand of listProviderPluginCommandSpecs("slack")) {
678+
const normalizedName = normalizeLowercaseStringOrEmpty(pluginCommand.name);
679+
if (!normalizedName || existingNativeNames.has(normalizedName)) {
680+
continue;
681+
}
682+
existingNativeNames.add(normalizedName);
683+
nativeCommands.push(pluginCommand);
684+
}
673685
}
674686

675687
if (nativeCommands.length > 0) {

src/agents/agent-command.live-model-switch.test.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { LiveSessionModelSwitchError } from "./live-model-switch.js";
44
const state = vi.hoisted(() => ({
55
runWithModelFallbackMock: vi.fn(),
66
runAgentAttemptMock: vi.fn(),
7+
resolveEffectiveModelFallbacksMock: vi.fn().mockReturnValue(undefined),
78
emitAgentEventMock: vi.fn(),
89
registerAgentRunContextMock: vi.fn(),
910
clearAgentRunContextMock: vi.fn(),
@@ -15,7 +16,7 @@ vi.mock("./model-fallback.js", () => ({
1516
runWithModelFallback: (params: unknown) => state.runWithModelFallbackMock(params),
1617
}));
1718

18-
vi.mock("./command/attempt-execution.js", () => ({
19+
vi.mock("./command/attempt-execution.runtime.js", () => ({
1920
buildAcpResult: vi.fn(),
2021
createAcpVisibleTextAccumulator: vi.fn(),
2122
emitAcpAssistantDelta: vi.fn(),
@@ -29,7 +30,7 @@ vi.mock("./command/attempt-execution.js", () => ({
2930
sessionFileHasContent: vi.fn(async () => false),
3031
}));
3132

32-
vi.mock("./command/delivery.js", () => ({
33+
vi.mock("./command/delivery.runtime.js", () => ({
3334
deliverAgentCommandResult: (...args: unknown[]) => state.deliverAgentCommandResultMock(...args),
3435
}));
3536

@@ -47,7 +48,7 @@ vi.mock("./command/run-context.js", () => ({
4748
}),
4849
}));
4950

50-
vi.mock("./command/session-store.js", () => ({
51+
vi.mock("./command/session-store.runtime.js", () => ({
5152
updateSessionStoreAfterAgentRun: (...args: unknown[]) =>
5253
state.updateSessionStoreAfterAgentRunMock(...args),
5354
}));
@@ -107,7 +108,7 @@ vi.mock("../cli/deps.js", () => ({
107108
createDefaultDeps: () => ({}),
108109
}));
109110

110-
vi.mock("../config/config.js", () => ({
111+
vi.mock("../config/io.js", () => ({
111112
loadConfig: () => ({
112113
agents: {
113114
defaults: {
@@ -122,6 +123,9 @@ vi.mock("../config/config.js", () => ({
122123
readConfigFileSnapshotForWrite: async () => ({
123124
snapshot: { valid: false },
124125
}),
126+
}));
127+
128+
vi.mock("../config/runtime-snapshot.js", () => ({
125129
setRuntimeConfigSnapshot: vi.fn(),
126130
}));
127131

@@ -136,7 +140,7 @@ vi.mock("../config/sessions.js", () => ({
136140
),
137141
}));
138142

139-
vi.mock("../config/sessions/transcript.js", () => ({
143+
vi.mock("../config/sessions/transcript-resolve.runtime.js", () => ({
140144
resolveSessionTranscriptFile: async () => ({
141145
sessionFile: "/tmp/session.jsonl",
142146
sessionEntry: { sessionId: "session-1", updatedAt: Date.now() },
@@ -146,6 +150,7 @@ vi.mock("../config/sessions/transcript.js", () => ({
146150
vi.mock("../infra/agent-events.js", () => ({
147151
clearAgentRunContext: (...args: unknown[]) => state.clearAgentRunContextMock(...args),
148152
emitAgentEvent: (...args: unknown[]) => state.emitAgentEventMock(...args),
153+
onAgentEvent: vi.fn(),
149154
registerAgentRunContext: (...args: unknown[]) => state.registerAgentRunContextMock(...args),
150155
}));
151156

@@ -158,12 +163,18 @@ vi.mock("../infra/skills-remote.js", () => ({
158163
}));
159164

160165
vi.mock("../logging/subsystem.js", () => ({
161-
createSubsystemLogger: () => ({
162-
info: vi.fn(),
163-
warn: vi.fn(),
164-
error: vi.fn(),
165-
debug: vi.fn(),
166-
}),
166+
createSubsystemLogger: () => {
167+
const logger = {
168+
info: vi.fn(),
169+
warn: vi.fn(),
170+
error: vi.fn(),
171+
debug: vi.fn(),
172+
trace: vi.fn(),
173+
raw: vi.fn(),
174+
child: vi.fn(() => logger),
175+
};
176+
return logger;
177+
},
167178
}));
168179

169180
vi.mock("../routing/session-key.js", () => ({
@@ -198,12 +209,11 @@ vi.mock("../utils/message-channel.js", () => ({
198209
resolveMessageChannel: () => "test",
199210
}));
200211

201-
const resolveEffectiveModelFallbacksMock = vi.fn().mockReturnValue(undefined);
202212
vi.mock("./agent-scope.js", () => ({
203213
listAgentIds: () => ["default"],
204214
resolveAgentConfig: () => undefined,
205215
resolveAgentDir: () => "/tmp/agent",
206-
resolveEffectiveModelFallbacks: resolveEffectiveModelFallbacksMock,
216+
resolveEffectiveModelFallbacks: state.resolveEffectiveModelFallbacksMock,
207217
resolveSessionAgentId: () => "default",
208218
resolveAgentSkillsFilter: () => undefined,
209219
resolveAgentWorkspaceDir: () => "/tmp/workspace",
@@ -468,7 +478,7 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
468478
});
469479
state.runAgentAttemptMock.mockResolvedValue(makeSuccessResult("openai", "gpt-5.4"));
470480

471-
resolveEffectiveModelFallbacksMock.mockClear();
481+
state.resolveEffectiveModelFallbacksMock.mockClear();
472482

473483
const agentCommand = await getAgentCommand();
474484
await agentCommand({
@@ -477,11 +487,11 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
477487
senderIsOwner: true,
478488
});
479489

480-
expect(resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
481-
expect(resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
490+
expect(state.resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
491+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
482492
hasSessionModelOverride: false,
483493
});
484-
expect(resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
494+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
485495
hasSessionModelOverride: true,
486496
});
487497
});
@@ -508,7 +518,7 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
508518
});
509519
state.runAgentAttemptMock.mockResolvedValue(makeSuccessResult("anthropic", "claude"));
510520

511-
resolveEffectiveModelFallbacksMock.mockClear();
521+
state.resolveEffectiveModelFallbacksMock.mockClear();
512522

513523
const agentCommand = await getAgentCommand();
514524
await agentCommand({
@@ -517,11 +527,11 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
517527
senderIsOwner: true,
518528
});
519529

520-
expect(resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
521-
expect(resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
530+
expect(state.resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
531+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
522532
hasSessionModelOverride: false,
523533
});
524-
expect(resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
534+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
525535
hasSessionModelOverride: false,
526536
});
527537
});
@@ -546,7 +556,7 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
546556
});
547557
state.runAgentAttemptMock.mockResolvedValue(makeSuccessResult("openai", "claude"));
548558

549-
resolveEffectiveModelFallbacksMock.mockClear();
559+
state.resolveEffectiveModelFallbacksMock.mockClear();
550560

551561
const agentCommand = await getAgentCommand();
552562
await agentCommand({
@@ -555,11 +565,11 @@ describe("agentCommand – LiveSessionModelSwitchError retry", () => {
555565
senderIsOwner: true,
556566
});
557567

558-
expect(resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
559-
expect(resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
568+
expect(state.resolveEffectiveModelFallbacksMock).toHaveBeenCalledTimes(2);
569+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[0][0]).toMatchObject({
560570
hasSessionModelOverride: false,
561571
});
562-
expect(resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
572+
expect(state.resolveEffectiveModelFallbacksMock.mock.calls[1][0]).toMatchObject({
563573
hasSessionModelOverride: true,
564574
});
565575
});

0 commit comments

Comments
 (0)