feat: add /reasoning reasoning visibility

This commit is contained in:
Peter Steinberger
2026-01-07 06:16:38 +01:00
parent cb2a72f8a9
commit 1673a221f8
32 changed files with 370 additions and 23 deletions

View File

@@ -221,6 +221,7 @@ export async function runReplyAgent(params: {
authProfileId: followupRun.run.authProfileId,
thinkLevel: followupRun.run.thinkLevel,
verboseLevel: followupRun.run.verboseLevel,
reasoningLevel: followupRun.run.reasoningLevel,
bashElevated: followupRun.run.bashElevated,
timeoutMs: followupRun.run.timeoutMs,
runId,

View File

@@ -41,7 +41,12 @@ import {
formatTokenCount,
} from "../status.js";
import type { MsgContext } from "../templating.js";
import type { ElevatedLevel, ThinkLevel, VerboseLevel } from "../thinking.js";
import type {
ElevatedLevel,
ReasoningLevel,
ThinkLevel,
VerboseLevel,
} from "../thinking.js";
import type { ReplyPayload } from "../types.js";
import { isAbortTrigger, setAbortMemory } from "./abort.js";
import type { InlineDirectives } from "./directive-handling.js";
@@ -202,6 +207,7 @@ export async function handleCommands(params: {
defaultGroupActivation: () => "always" | "mention";
resolvedThinkLevel?: ThinkLevel;
resolvedVerboseLevel: VerboseLevel;
resolvedReasoningLevel: ReasoningLevel;
resolvedElevatedLevel?: ElevatedLevel;
resolveDefaultThinkingLevel: () => Promise<ThinkLevel | undefined>;
provider: string;
@@ -226,6 +232,7 @@ export async function handleCommands(params: {
defaultGroupActivation,
resolvedThinkLevel,
resolvedVerboseLevel,
resolvedReasoningLevel,
resolvedElevatedLevel,
resolveDefaultThinkingLevel,
provider,
@@ -405,6 +412,7 @@ export async function handleCommands(params: {
resolvedThink:
resolvedThinkLevel ?? (await resolveDefaultThinkingLevel()),
resolvedVerbose: resolvedVerboseLevel,
resolvedReasoning: resolvedReasoningLevel,
resolvedElevated: resolvedElevatedLevel,
modelAuth: resolveModelAuthLabel(provider, cfg),
webLinked,

View File

@@ -32,9 +32,11 @@ import type { ReplyPayload } from "../types.js";
import {
type ElevatedLevel,
extractElevatedDirective,
extractReasoningDirective,
extractStatusDirective,
extractThinkDirective,
extractVerboseDirective,
type ReasoningLevel,
type ThinkLevel,
type VerboseLevel,
} from "./directives.js";
@@ -155,6 +157,9 @@ export type InlineDirectives = {
hasVerboseDirective: boolean;
verboseLevel?: VerboseLevel;
rawVerboseLevel?: string;
hasReasoningDirective: boolean;
reasoningLevel?: ReasoningLevel;
rawReasoningLevel?: string;
hasElevatedDirective: boolean;
elevatedLevel?: ElevatedLevel;
rawElevatedLevel?: string;
@@ -188,12 +193,18 @@ export function parseInlineDirectives(body: string): InlineDirectives {
rawLevel: rawVerboseLevel,
hasDirective: hasVerboseDirective,
} = extractVerboseDirective(thinkCleaned);
const {
cleaned: reasoningCleaned,
reasoningLevel,
rawLevel: rawReasoningLevel,
hasDirective: hasReasoningDirective,
} = extractReasoningDirective(verboseCleaned);
const {
cleaned: elevatedCleaned,
elevatedLevel,
rawLevel: rawElevatedLevel,
hasDirective: hasElevatedDirective,
} = extractElevatedDirective(verboseCleaned);
} = extractElevatedDirective(reasoningCleaned);
const { cleaned: statusCleaned, hasDirective: hasStatusDirective } =
extractStatusDirective(elevatedCleaned);
const {
@@ -225,6 +236,9 @@ export function parseInlineDirectives(body: string): InlineDirectives {
hasVerboseDirective,
verboseLevel,
rawVerboseLevel,
hasReasoningDirective,
reasoningLevel,
rawReasoningLevel,
hasElevatedDirective,
elevatedLevel,
rawElevatedLevel,
@@ -257,6 +271,7 @@ export function isDirectiveOnly(params: {
if (
!directives.hasThinkDirective &&
!directives.hasVerboseDirective &&
!directives.hasReasoningDirective &&
!directives.hasElevatedDirective &&
!directives.hasModelDirective &&
!directives.hasQueueDirective
@@ -367,6 +382,11 @@ export async function handleDirectiveOnly(params: {
text: `Unrecognized verbose level "${directives.rawVerboseLevel ?? ""}". Valid levels: off, on.`,
};
}
if (directives.hasReasoningDirective && !directives.reasoningLevel) {
return {
text: `Unrecognized reasoning level "${directives.rawReasoningLevel ?? ""}". Valid levels: on, off.`,
};
}
if (directives.hasElevatedDirective && !directives.elevatedLevel) {
return {
text: `Unrecognized elevated level "${directives.rawElevatedLevel ?? ""}". Valid levels: off, on.`,
@@ -476,6 +496,11 @@ export async function handleDirectiveOnly(params: {
if (directives.verboseLevel === "off") delete sessionEntry.verboseLevel;
else sessionEntry.verboseLevel = directives.verboseLevel;
}
if (directives.hasReasoningDirective && directives.reasoningLevel) {
if (directives.reasoningLevel === "off")
delete sessionEntry.reasoningLevel;
else sessionEntry.reasoningLevel = directives.reasoningLevel;
}
if (directives.hasElevatedDirective && directives.elevatedLevel) {
if (directives.elevatedLevel === "off") delete sessionEntry.elevatedLevel;
else sessionEntry.elevatedLevel = directives.elevatedLevel;
@@ -533,6 +558,13 @@ export async function handleDirectiveOnly(params: {
: `${SYSTEM_MARK} Verbose logging enabled.`,
);
}
if (directives.hasReasoningDirective && directives.reasoningLevel) {
parts.push(
directives.reasoningLevel === "off"
? `${SYSTEM_MARK} Reasoning visibility disabled.`
: `${SYSTEM_MARK} Reasoning visibility enabled.`,
);
}
if (directives.hasElevatedDirective && directives.elevatedLevel) {
parts.push(
directives.elevatedLevel === "off"
@@ -634,6 +666,14 @@ export async function persistInlineDirectives(params: {
}
updated = true;
}
if (directives.hasReasoningDirective && directives.reasoningLevel) {
if (directives.reasoningLevel === "off") {
delete sessionEntry.reasoningLevel;
} else {
sessionEntry.reasoningLevel = directives.reasoningLevel;
}
updated = true;
}
if (
directives.hasElevatedDirective &&
directives.elevatedLevel &&

View File

@@ -1,6 +1,8 @@
import type { ReasoningLevel } from "../thinking.js";
import {
type ElevatedLevel,
normalizeElevatedLevel,
normalizeReasoningLevel,
normalizeThinkLevel,
normalizeVerboseLevel,
type ThinkLevel,
@@ -74,6 +76,28 @@ export function extractElevatedDirective(body?: string): {
};
}
export function extractReasoningDirective(body?: string): {
cleaned: string;
reasoningLevel?: ReasoningLevel;
rawLevel?: string;
hasDirective: boolean;
} {
if (!body) return { cleaned: "", hasDirective: false };
const match = body.match(
/(?:^|\s)\/(?:reasoning|reason)(?=$|\s|:)\s*:?\s*([a-zA-Z-]+)\b/i,
);
const reasoningLevel = normalizeReasoningLevel(match?.[1]);
const cleaned = match
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
: body.trim();
return {
cleaned,
reasoningLevel,
rawLevel: match?.[1],
hasDirective: !!match,
};
}
export function extractStatusDirective(body?: string): {
cleaned: string;
hasDirective: boolean;
@@ -89,4 +113,4 @@ export function extractStatusDirective(body?: string): {
};
}
export type { ElevatedLevel, ThinkLevel, VerboseLevel };
export type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel };

View File

@@ -131,6 +131,7 @@ export function createFollowupRunner(params: {
authProfileId: queued.run.authProfileId,
thinkLevel: queued.run.thinkLevel,
verboseLevel: queued.run.verboseLevel,
reasoningLevel: queued.run.reasoningLevel,
bashElevated: queued.run.bashElevated,
timeoutMs: queued.run.timeoutMs,
runId,

View File

@@ -4,7 +4,12 @@ import type { ClawdbotConfig } from "../../config/config.js";
import type { SessionEntry } from "../../config/sessions.js";
import { defaultRuntime } from "../../runtime.js";
import type { OriginatingChannelType } from "../templating.js";
import type { ElevatedLevel, ThinkLevel, VerboseLevel } from "./directives.js";
import type {
ElevatedLevel,
ReasoningLevel,
ThinkLevel,
VerboseLevel,
} from "./directives.js";
import { isRoutableChannel } from "./route-reply.js";
export type QueueMode =
| "steer"
@@ -54,6 +59,7 @@ export type FollowupRun = {
authProfileId?: string;
thinkLevel?: ThinkLevel;
verboseLevel?: VerboseLevel;
reasoningLevel?: ReasoningLevel;
elevatedLevel?: ElevatedLevel;
bashElevated?: {
enabled: boolean;