Skip to content

Commit ce282a7

Browse files
committed
chat: clear thinking and fast defaults
1 parent e8d63b8 commit ce282a7

19 files changed

Lines changed: 209 additions & 46 deletions

CHANGELOG.md

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

77
### Changes
88

9+
- Chat commands: add `/think default` and `/fast default` to clear session overrides and inherit configured/provider defaults.
910
- Docker: run the runtime image under `tini` so long-lived containers reap orphaned child processes and forward signals correctly. (#77885) Thanks @VintageAyu.
1011
- Google/Gemini: normalize retired `google/gemini-3-pro-preview` and `google-gemini-cli/gemini-3-pro-preview` selections to `google/gemini-3.1-pro-preview` before they are written to model config.
1112
- Amazon Bedrock: support `serviceTier` parameter for Bedrock models, configurable via `agents.defaults.params.serviceTier` or per-model in `agents.defaults.models`. Valid values: `default`, `flex`, `priority`, `reserved`. (#64512) Thanks @mobilinkd.

docs/tools/slash-commands.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ Current source-of-truth:
135135

136136
</Accordion>
137137
<Accordion title="Model and run controls">
138-
- `/think <level>` sets the thinking level. Options come from the active model's provider profile; common levels are `off`, `minimal`, `low`, `medium`, and `high`, with custom levels such as `xhigh`, `adaptive`, `max`, or binary `on` only where supported. Aliases: `/thinking`, `/t`.
138+
- `/think <level|default>` sets the thinking level or clears the session override. Options come from the active model's provider profile; common levels are `off`, `minimal`, `low`, `medium`, and `high`, with custom levels such as `xhigh`, `adaptive`, `max`, or binary `on` only where supported. Aliases: `/thinking`, `/t`.
139139
- `/verbose on|off|full` toggles verbose output. Alias: `/v`.
140140
- `/trace on|off` toggles plugin trace output for the current session.
141-
- `/fast [status|on|off]` shows or sets fast mode.
141+
- `/fast [status|on|off|default]` shows, sets, or clears fast mode.
142142
- `/reasoning [on|off|stream]` toggles reasoning visibility. Alias: `/reason`.
143143
- `/elevated [on|off|ask|full]` toggles elevated mode. Alias: `/elev`.
144144
- `/exec host=<auto|sandbox|gateway|node> security=<deny|allowlist|full> ask=<off|on-miss|always> node=<id>` shows or sets exec defaults.

docs/tools/thinking.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ title: "Thinking levels"
4848
## Setting a session default
4949

5050
- Send a message that is **only** the directive (whitespace allowed), e.g. `/think:medium` or `/t high`.
51-
- That sticks for the current session (per-sender by default); cleared by `/think:off` or session idle reset.
51+
- That sticks for the current session (per-sender by default). Use `/think default` to clear the session override and inherit the configured/provider default; aliases include `inherit`, `clear`, `reset`, and `unpin`.
52+
- `/think off` stores an explicit off override. It disables thinking until you change or clear the session override.
5253
- Confirmation reply is sent (`Thinking level set to high.` / `Thinking disabled.`). If the level is invalid (e.g. `/thinking big`), the command is rejected with a hint and the session state is left unchanged.
5354
- Send `/think` (or `/think:`) with no argument to see the current thinking level.
5455

@@ -59,11 +60,11 @@ title: "Thinking levels"
5960

6061
## Fast mode (/fast)
6162

62-
- Levels: `on|off`.
63-
- Directive-only message toggles a session fast-mode override and replies `Fast mode enabled.` / `Fast mode disabled.`.
63+
- Levels: `on|off|default`.
64+
- Directive-only message toggles a session fast-mode override and replies `Fast mode enabled.` / `Fast mode disabled.`. Use `/fast default` to clear the session override and inherit the configured default; aliases include `inherit`, `clear`, `reset`, and `unpin`.
6465
- Send `/fast` (or `/fast status`) with no mode to see the current effective fast-mode state.
6566
- OpenClaw resolves fast mode in this order:
66-
1. Inline/directive-only `/fast on|off`
67+
1. Inline/directive-only `/fast on|off` override (`/fast default` clears this layer)
6768
2. Session override
6869
3. Per-agent default (`agents.list[].fastModeDefault`)
6970
4. Per-model config: `agents.defaults.models["<provider>/<model>"].params.fastMode`

src/auto-reply/command-status-builders.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ export function buildHelpMessage(cfg?: OpenClawConfig): string {
5959
lines.push("");
6060

6161
const optionParts = [
62-
"/think <level>",
62+
"/think <level|default>",
6363
"/model <id>",
64-
"/fast status|on|off",
64+
"/fast status|on|off|default",
6565
"/verbose on|off|full",
6666
"/trace on|off|raw",
6767
];

src/auto-reply/commands-registry.shared.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ type ListThinkingLevels = (
1313
provider?: string | null,
1414
model?: string | null,
1515
catalog?: CommandArgChoiceContext["catalog"],
16-
) => ThinkLevel[];
16+
) => string[];
1717

1818
const BROWSER_SAFE_THINKING_LEVELS: ThinkLevel[] = [
1919
...BASE_THINKING_LEVELS,
@@ -147,8 +147,12 @@ export function assertCommandRegistry(commands: ChatCommandDefinition[]): void {
147147
export function buildBuiltinChatCommands(
148148
params: { listThinkingLevels?: ListThinkingLevels } = {},
149149
): ChatCommandDefinition[] {
150-
const listThinkingLevelChoices =
150+
const configuredThinkingLevels =
151151
params.listThinkingLevels ?? (() => BROWSER_SAFE_THINKING_LEVELS);
152+
const listThinkingLevelChoices: ListThinkingLevels = (provider, model, catalog) => {
153+
const levels = configuredThinkingLevels(provider, model, catalog);
154+
return ["default", ...levels.filter((level) => level !== "default")];
155+
};
152156
const commands: ChatCommandDefinition[] = [
153157
defineChatCommand({
154158
key: "help",
@@ -799,9 +803,9 @@ export function buildBuiltinChatCommands(
799803
args: [
800804
{
801805
name: "mode",
802-
description: "status, on, or off",
806+
description: "status, on, off, or default",
803807
type: "string",
804-
choices: ["status", "on", "off"],
808+
choices: ["status", "on", "off", "default"],
805809
},
806810
],
807811
argsMenu: "auto",

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ describe("commands registry", () => {
331331
category: "options",
332332
});
333333
const modeArg = fast?.args?.find((arg) => arg.name === "mode");
334-
expect(modeArg?.choices).toEqual(["status", "on", "off"]);
334+
expect(modeArg?.choices).toEqual(["status", "on", "off", "default"]);
335335
});
336336

337337
it("detects known text commands", () => {
@@ -592,14 +592,15 @@ describe("commands registry args", () => {
592592

593593
expect(menu?.arg.name).toBe("level");
594594
expect(menu?.choices.map((choice) => choice.value)).toEqual([
595+
"default",
595596
"off",
596597
"low",
597598
"medium",
598599
"high",
599600
"max",
600601
]);
601602
expect(formatCommandArgMenuTitle({ command, menu: menu! })).toBe(
602-
"Choose level for /think.\nOptions: off, low, medium, high, max.",
603+
"Choose level for /think.\nOptions: default, off, low, medium, high, max.",
603604
);
604605
});
605606

src/auto-reply/reply.directive.parse.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,21 @@ describe("directive parsing", () => {
6969
expect(res.fastMode).toBe(true);
7070
});
7171

72+
it("parses default thinking and fast directives as override clears", () => {
73+
expect(parseInlineDirectives("/think default")).toMatchObject({
74+
hasThinkDirective: true,
75+
thinkLevel: undefined,
76+
rawThinkLevel: "default",
77+
clearThinkLevel: true,
78+
});
79+
expect(parseInlineDirectives("/fast inherit")).toMatchObject({
80+
hasFastDirective: true,
81+
fastMode: undefined,
82+
rawFastMode: "inherit",
83+
clearFastMode: true,
84+
});
85+
});
86+
7287
it("matches elevated with leading space", () => {
7388
const res = extractElevatedDirective(" please /elevated on now");
7489
expect(res.hasDirective).toBe(true);

src/auto-reply/reply/commands-session-usage.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,45 @@ describe("handleFastCommand", () => {
241241
}),
242242
);
243243
});
244+
245+
it("clears fast mode for /fast default", async () => {
246+
const params = buildUsageParams();
247+
params.command.commandBodyNormalized = "/fast default";
248+
params.sessionEntry = {
249+
sessionId: "target-session",
250+
updatedAt: Date.now(),
251+
fastMode: true,
252+
};
253+
params.sessionStore = { [params.sessionKey]: params.sessionEntry };
254+
255+
const result = await handleFastCommand(params, true);
256+
257+
expect(result?.shouldContinue).toBe(false);
258+
expect(result?.reply?.text).toBe("⚙️ Fast mode reset to default.");
259+
expect(params.sessionEntry.fastMode).toBeUndefined();
260+
expect(params.sessionStore[params.sessionKey]?.fastMode).toBeUndefined();
261+
});
262+
263+
it("clears fast mode on the target store entry for /fast default", async () => {
264+
const params = buildUsageParams();
265+
params.command.commandBodyNormalized = "/fast default";
266+
params.sessionEntry = {
267+
sessionId: "wrapper-session",
268+
updatedAt: Date.now(),
269+
fastMode: false,
270+
};
271+
params.sessionStore = {
272+
[params.sessionKey]: {
273+
sessionId: "target-session",
274+
updatedAt: Date.now(),
275+
fastMode: true,
276+
},
277+
};
278+
279+
const result = await handleFastCommand(params, true);
280+
281+
expect(result?.reply?.text).toBe("⚙️ Fast mode reset to default.");
282+
expect(params.sessionEntry.fastMode).toBe(false);
283+
expect(params.sessionStore[params.sessionKey]?.fastMode).toBeUndefined();
284+
});
244285
});

src/auto-reply/reply/commands-session.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ import {
2929
import { formatTokenCount, formatUsd } from "../../utils/usage-format.js";
3030
import { parseActivationCommand } from "../group-activation.js";
3131
import { parseSendPolicyCommand } from "../send-policy.js";
32-
import { normalizeFastMode, normalizeUsageDisplay, resolveResponseUsageMode } from "../thinking.js";
32+
import {
33+
isSessionDefaultDirectiveValue,
34+
normalizeFastMode,
35+
normalizeUsageDisplay,
36+
resolveResponseUsageMode,
37+
} from "../thinking.js";
3338
import { resolveCommandSurfaceChannel } from "./channel-context.js";
3439
import { rejectNonOwnerCommand, rejectUnauthorizedCommand } from "./command-gates.js";
3540
import { handleAbortTrigger, handleStopCommand } from "./commands-session-abort.js";
@@ -412,17 +417,29 @@ export const handleFastCommand: CommandHandler = async (params, allowTextCommand
412417
};
413418
}
414419

415-
const nextMode = normalizeFastMode(rawMode);
420+
const targetSessionEntry = params.sessionStore?.[params.sessionKey] ?? params.sessionEntry;
421+
const resetsToDefault = isSessionDefaultDirectiveValue(rawMode);
422+
const nextMode = resetsToDefault ? undefined : normalizeFastMode(rawMode);
416423
if (nextMode === undefined) {
424+
if (resetsToDefault) {
425+
if (targetSessionEntry && params.sessionStore && params.sessionKey) {
426+
delete targetSessionEntry.fastMode;
427+
await persistSessionEntry({ ...params, sessionEntry: targetSessionEntry });
428+
}
429+
return {
430+
shouldContinue: false,
431+
reply: { text: "⚙️ Fast mode reset to default." },
432+
};
433+
}
417434
return {
418435
shouldContinue: false,
419-
reply: { text: "⚙️ Usage: /fast status|on|off" },
436+
reply: { text: "⚙️ Usage: /fast status|on|off|default" },
420437
};
421438
}
422439

423-
if (params.sessionEntry && params.sessionStore && params.sessionKey) {
424-
params.sessionEntry.fastMode = nextMode;
425-
await persistSessionEntry(params);
440+
if (targetSessionEntry && params.sessionStore && params.sessionKey) {
441+
targetSessionEntry.fastMode = nextMode;
442+
await persistSessionEntry({ ...params, sessionEntry: targetSessionEntry });
426443
}
427444

428445
return {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ export function createEmptyInlineDirectives(): InlineDirectives {
4848
return {
4949
cleaned: "",
5050
hasThinkDirective: false,
51+
clearThinkLevel: false,
5152
hasVerboseDirective: false,
5253
hasFastDirective: false,
54+
clearFastMode: false,
5355
hasReasoningDirective: false,
5456
hasTraceDirective: false,
5557
hasElevatedDirective: false,

0 commit comments

Comments
 (0)