mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-19 18:39:20 -05:00
Chore: format + lint fixes
This commit is contained in:
@@ -76,7 +76,11 @@ function parsePiJson(raw: string): AgentParseResult {
|
||||
}
|
||||
: undefined;
|
||||
|
||||
return { texts, toolResults: toolResults.length ? toolResults : undefined, meta };
|
||||
return {
|
||||
texts,
|
||||
toolResults: toolResults.length ? toolResults : undefined,
|
||||
meta,
|
||||
};
|
||||
}
|
||||
|
||||
export const piSpec: AgentSpec = {
|
||||
|
||||
@@ -37,69 +37,77 @@ type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high";
|
||||
type VerboseLevel = "off" | "on";
|
||||
|
||||
function normalizeThinkLevel(raw?: string | null): ThinkLevel | undefined {
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off"].includes(key)) return "off";
|
||||
if (["min", "minimal"].includes(key)) return "minimal";
|
||||
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key))
|
||||
return "low";
|
||||
if (["med", "medium", "thinkharder", "think-harder", "harder"].includes(key))
|
||||
return "medium";
|
||||
if (["high", "ultra", "ultrathink", "think-hard", "thinkhardest", "highest", "max"].includes(key))
|
||||
return "high";
|
||||
if (["think"].includes(key)) return "minimal";
|
||||
return undefined;
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off"].includes(key)) return "off";
|
||||
if (["min", "minimal"].includes(key)) return "minimal";
|
||||
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key))
|
||||
return "low";
|
||||
if (["med", "medium", "thinkharder", "think-harder", "harder"].includes(key))
|
||||
return "medium";
|
||||
if (
|
||||
[
|
||||
"high",
|
||||
"ultra",
|
||||
"ultrathink",
|
||||
"think-hard",
|
||||
"thinkhardest",
|
||||
"highest",
|
||||
"max",
|
||||
].includes(key)
|
||||
)
|
||||
return "high";
|
||||
if (["think"].includes(key)) return "minimal";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function normalizeVerboseLevel(raw?: string | null): VerboseLevel | undefined {
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off", "false", "no", "0"].includes(key)) return "off";
|
||||
if (["on", "full", "true", "yes", "1"].includes(key)) return "on";
|
||||
return undefined;
|
||||
if (!raw) return undefined;
|
||||
const key = raw.toLowerCase();
|
||||
if (["off", "false", "no", "0"].includes(key)) return "off";
|
||||
if (["on", "full", "true", "yes", "1"].includes(key)) return "on";
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractThinkDirective(body?: string): {
|
||||
cleaned: string;
|
||||
thinkLevel?: ThinkLevel;
|
||||
rawLevel?: string;
|
||||
hasDirective: boolean;
|
||||
cleaned: string;
|
||||
thinkLevel?: ThinkLevel;
|
||||
rawLevel?: string;
|
||||
hasDirective: boolean;
|
||||
} {
|
||||
if (!body) return { cleaned: "", hasDirective: false };
|
||||
// Match the longest keyword first to avoid partial captures (e.g. "/think:high")
|
||||
const match = body.match(
|
||||
/\/(?:thinking|think|t)\s*:?\s*([a-zA-Z-]+)\b/i,
|
||||
);
|
||||
const thinkLevel = normalizeThinkLevel(match?.[1]);
|
||||
const cleaned = match
|
||||
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
||||
: body.trim();
|
||||
return {
|
||||
cleaned,
|
||||
thinkLevel,
|
||||
rawLevel: match?.[1],
|
||||
hasDirective: !!match,
|
||||
};
|
||||
if (!body) return { cleaned: "", hasDirective: false };
|
||||
// Match the longest keyword first to avoid partial captures (e.g. "/think:high")
|
||||
const match = body.match(/\/(?:thinking|think|t)\s*:?\s*([a-zA-Z-]+)\b/i);
|
||||
const thinkLevel = normalizeThinkLevel(match?.[1]);
|
||||
const cleaned = match
|
||||
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
||||
: body.trim();
|
||||
return {
|
||||
cleaned,
|
||||
thinkLevel,
|
||||
rawLevel: match?.[1],
|
||||
hasDirective: !!match,
|
||||
};
|
||||
}
|
||||
|
||||
function extractVerboseDirective(body?: string): {
|
||||
cleaned: string;
|
||||
verboseLevel?: VerboseLevel;
|
||||
rawLevel?: string;
|
||||
hasDirective: boolean;
|
||||
cleaned: string;
|
||||
verboseLevel?: VerboseLevel;
|
||||
rawLevel?: string;
|
||||
hasDirective: boolean;
|
||||
} {
|
||||
if (!body) return { cleaned: "", hasDirective: false };
|
||||
const match = body.match(/\/(?:verbose|v)\s*:?\s*([a-zA-Z-]+)\b/i);
|
||||
const verboseLevel = normalizeVerboseLevel(match?.[1]);
|
||||
const cleaned = match
|
||||
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
||||
: body.trim();
|
||||
return {
|
||||
cleaned,
|
||||
verboseLevel,
|
||||
rawLevel: match?.[1],
|
||||
hasDirective: !!match,
|
||||
};
|
||||
if (!body) return { cleaned: "", hasDirective: false };
|
||||
const match = body.match(/\/(?:verbose|v)\s*:?\s*([a-zA-Z-]+)\b/i);
|
||||
const verboseLevel = normalizeVerboseLevel(match?.[1]);
|
||||
const cleaned = match
|
||||
? body.replace(match[0], "").replace(/\s+/g, " ").trim()
|
||||
: body.trim();
|
||||
return {
|
||||
cleaned,
|
||||
verboseLevel,
|
||||
rawLevel: match?.[1],
|
||||
hasDirective: !!match,
|
||||
};
|
||||
}
|
||||
|
||||
function isAbortTrigger(text?: string): boolean {
|
||||
@@ -242,96 +250,96 @@ export async function getReplyFromConfig(
|
||||
IsNewSession: isNewSession ? "true" : "false",
|
||||
};
|
||||
|
||||
const {
|
||||
cleaned: thinkCleaned,
|
||||
thinkLevel: inlineThink,
|
||||
rawLevel: rawThinkLevel,
|
||||
hasDirective: hasThinkDirective,
|
||||
} = extractThinkDirective(sessionCtx.BodyStripped ?? sessionCtx.Body ?? "");
|
||||
const {
|
||||
cleaned: verboseCleaned,
|
||||
verboseLevel: inlineVerbose,
|
||||
rawLevel: rawVerboseLevel,
|
||||
hasDirective: hasVerboseDirective,
|
||||
} = extractVerboseDirective(thinkCleaned);
|
||||
sessionCtx.Body = verboseCleaned;
|
||||
sessionCtx.BodyStripped = verboseCleaned;
|
||||
const {
|
||||
cleaned: thinkCleaned,
|
||||
thinkLevel: inlineThink,
|
||||
rawLevel: rawThinkLevel,
|
||||
hasDirective: hasThinkDirective,
|
||||
} = extractThinkDirective(sessionCtx.BodyStripped ?? sessionCtx.Body ?? "");
|
||||
const {
|
||||
cleaned: verboseCleaned,
|
||||
verboseLevel: inlineVerbose,
|
||||
rawLevel: rawVerboseLevel,
|
||||
hasDirective: hasVerboseDirective,
|
||||
} = extractVerboseDirective(thinkCleaned);
|
||||
sessionCtx.Body = verboseCleaned;
|
||||
sessionCtx.BodyStripped = verboseCleaned;
|
||||
|
||||
let resolvedThinkLevel =
|
||||
inlineThink ??
|
||||
(sessionEntry?.thinkingLevel as ThinkLevel | undefined) ??
|
||||
(reply?.thinkingDefault as ThinkLevel | undefined);
|
||||
let resolvedThinkLevel =
|
||||
inlineThink ??
|
||||
(sessionEntry?.thinkingLevel as ThinkLevel | undefined) ??
|
||||
(reply?.thinkingDefault as ThinkLevel | undefined);
|
||||
|
||||
let resolvedVerboseLevel =
|
||||
inlineVerbose ??
|
||||
(sessionEntry?.verboseLevel as VerboseLevel | undefined) ??
|
||||
(reply?.verboseDefault as VerboseLevel | undefined);
|
||||
const resolvedVerboseLevel =
|
||||
inlineVerbose ??
|
||||
(sessionEntry?.verboseLevel as VerboseLevel | undefined) ??
|
||||
(reply?.verboseDefault as VerboseLevel | undefined);
|
||||
|
||||
const directiveOnly = (() => {
|
||||
if (!hasThinkDirective) return false;
|
||||
if (!thinkCleaned) return true;
|
||||
// Ignore bracketed prefixes (timestamps, same-phone markers, etc.)
|
||||
const stripped = thinkCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
|
||||
return stripped.length === 0;
|
||||
})();
|
||||
const directiveOnly = (() => {
|
||||
if (!hasThinkDirective) return false;
|
||||
if (!thinkCleaned) return true;
|
||||
// Ignore bracketed prefixes (timestamps, same-phone markers, etc.)
|
||||
const stripped = thinkCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
|
||||
return stripped.length === 0;
|
||||
})();
|
||||
|
||||
// Directive-only message => persist session thinking level and return ack
|
||||
if (directiveOnly) {
|
||||
if (!inlineThink) {
|
||||
cleanupTyping();
|
||||
return {
|
||||
text: `Unrecognized thinking level "${rawThinkLevel ?? ""}". Valid levels: off, minimal, low, medium, high.`,
|
||||
};
|
||||
}
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
if (inlineThink === "off") {
|
||||
delete sessionEntry.thinkingLevel;
|
||||
} else {
|
||||
sessionEntry.thinkingLevel = inlineThink;
|
||||
}
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
const ack =
|
||||
inlineThink === "off"
|
||||
? "Thinking disabled."
|
||||
: `Thinking level set to ${inlineThink}.`;
|
||||
cleanupTyping();
|
||||
return { text: ack };
|
||||
}
|
||||
// Directive-only message => persist session thinking level and return ack
|
||||
if (directiveOnly) {
|
||||
if (!inlineThink) {
|
||||
cleanupTyping();
|
||||
return {
|
||||
text: `Unrecognized thinking level "${rawThinkLevel ?? ""}". Valid levels: off, minimal, low, medium, high.`,
|
||||
};
|
||||
}
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
if (inlineThink === "off") {
|
||||
delete sessionEntry.thinkingLevel;
|
||||
} else {
|
||||
sessionEntry.thinkingLevel = inlineThink;
|
||||
}
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
const ack =
|
||||
inlineThink === "off"
|
||||
? "Thinking disabled."
|
||||
: `Thinking level set to ${inlineThink}.`;
|
||||
cleanupTyping();
|
||||
return { text: ack };
|
||||
}
|
||||
|
||||
const verboseDirectiveOnly = (() => {
|
||||
if (!hasVerboseDirective) return false;
|
||||
if (!verboseCleaned) return true;
|
||||
const stripped = verboseCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
|
||||
return stripped.length === 0;
|
||||
})();
|
||||
const verboseDirectiveOnly = (() => {
|
||||
if (!hasVerboseDirective) return false;
|
||||
if (!verboseCleaned) return true;
|
||||
const stripped = verboseCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
|
||||
return stripped.length === 0;
|
||||
})();
|
||||
|
||||
if (verboseDirectiveOnly) {
|
||||
if (!inlineVerbose) {
|
||||
cleanupTyping();
|
||||
return {
|
||||
text: `Unrecognized verbose level "${rawVerboseLevel ?? ""}". Valid levels: off, on.`,
|
||||
};
|
||||
}
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
if (inlineVerbose === "off") {
|
||||
delete sessionEntry.verboseLevel;
|
||||
} else {
|
||||
sessionEntry.verboseLevel = inlineVerbose;
|
||||
}
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
const ack =
|
||||
inlineVerbose === "off"
|
||||
? "Verbose logging disabled."
|
||||
: "Verbose logging enabled.";
|
||||
cleanupTyping();
|
||||
return { text: ack };
|
||||
}
|
||||
if (verboseDirectiveOnly) {
|
||||
if (!inlineVerbose) {
|
||||
cleanupTyping();
|
||||
return {
|
||||
text: `Unrecognized verbose level "${rawVerboseLevel ?? ""}". Valid levels: off, on.`,
|
||||
};
|
||||
}
|
||||
if (sessionEntry && sessionStore && sessionKey) {
|
||||
if (inlineVerbose === "off") {
|
||||
delete sessionEntry.verboseLevel;
|
||||
} else {
|
||||
sessionEntry.verboseLevel = inlineVerbose;
|
||||
}
|
||||
sessionEntry.updatedAt = Date.now();
|
||||
sessionStore[sessionKey] = sessionEntry;
|
||||
await saveSessionStore(storePath, sessionStore);
|
||||
}
|
||||
const ack =
|
||||
inlineVerbose === "off"
|
||||
? "Verbose logging disabled."
|
||||
: "Verbose logging enabled.";
|
||||
cleanupTyping();
|
||||
return { text: ack };
|
||||
}
|
||||
|
||||
// Optional allowlist by origin number (E.164 without whatsapp: prefix)
|
||||
const allowFrom = cfg.inbound?.allowFrom;
|
||||
@@ -489,11 +497,11 @@ export async function getReplyFromConfig(
|
||||
|
||||
const isHeartbeat = opts?.isHeartbeat === true;
|
||||
|
||||
if (reply && reply.mode === "command") {
|
||||
const heartbeatCommand = isHeartbeat
|
||||
? (reply as { heartbeatCommand?: string[] }).heartbeatCommand
|
||||
: undefined;
|
||||
const commandArgs = heartbeatCommand?.length
|
||||
if (reply && reply.mode === "command") {
|
||||
const heartbeatCommand = isHeartbeat
|
||||
? (reply as { heartbeatCommand?: string[] }).heartbeatCommand
|
||||
: undefined;
|
||||
const commandArgs = heartbeatCommand?.length
|
||||
? heartbeatCommand
|
||||
: reply.command;
|
||||
|
||||
@@ -502,34 +510,34 @@ export async function getReplyFromConfig(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
await onReplyStart();
|
||||
const commandReply = {
|
||||
...reply,
|
||||
command: commandArgs,
|
||||
mode: "command" as const,
|
||||
};
|
||||
try {
|
||||
const runResult = await runCommandReply({
|
||||
reply: commandReply,
|
||||
templatingCtx,
|
||||
sendSystemOnce,
|
||||
isNewSession,
|
||||
isFirstTurnInSession,
|
||||
systemSent,
|
||||
timeoutMs,
|
||||
timeoutSeconds,
|
||||
commandRunner,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
verboseLevel: resolvedVerboseLevel,
|
||||
});
|
||||
const payloadArray = runResult.payloads ?? [];
|
||||
const meta = runResult.meta;
|
||||
let finalPayloads = payloadArray;
|
||||
if (!finalPayloads || finalPayloads.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (sessionCfg && sessionStore && sessionKey) {
|
||||
const returnedSessionId = meta.agentMeta?.sessionId;
|
||||
await onReplyStart();
|
||||
const commandReply = {
|
||||
...reply,
|
||||
command: commandArgs,
|
||||
mode: "command" as const,
|
||||
};
|
||||
try {
|
||||
const runResult = await runCommandReply({
|
||||
reply: commandReply,
|
||||
templatingCtx,
|
||||
sendSystemOnce,
|
||||
isNewSession,
|
||||
isFirstTurnInSession,
|
||||
systemSent,
|
||||
timeoutMs,
|
||||
timeoutSeconds,
|
||||
commandRunner,
|
||||
thinkLevel: resolvedThinkLevel,
|
||||
verboseLevel: resolvedVerboseLevel,
|
||||
});
|
||||
const payloadArray = runResult.payloads ?? [];
|
||||
const meta = runResult.meta;
|
||||
let finalPayloads = payloadArray;
|
||||
if (!finalPayloads || finalPayloads.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (sessionCfg && sessionStore && sessionKey) {
|
||||
const returnedSessionId = meta.agentMeta?.sessionId;
|
||||
if (returnedSessionId && returnedSessionId !== sessionId) {
|
||||
const entry = sessionEntry ??
|
||||
sessionStore[sessionKey] ?? {
|
||||
@@ -553,28 +561,28 @@ export async function getReplyFromConfig(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (meta.agentMeta && isVerbose()) {
|
||||
logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`);
|
||||
}
|
||||
// If verbose is enabled and this is a new session, prepend a session hint.
|
||||
const sessionIdHint =
|
||||
resolvedVerboseLevel === "on" && isNewSession
|
||||
? sessionId ??
|
||||
meta.agentMeta?.sessionId ??
|
||||
templatingCtx.SessionId ??
|
||||
"unknown"
|
||||
: undefined;
|
||||
if (sessionIdHint) {
|
||||
finalPayloads = [
|
||||
{ text: `🧭 New session: ${sessionIdHint}` },
|
||||
...payloadArray,
|
||||
];
|
||||
}
|
||||
return finalPayloads.length === 1 ? finalPayloads[0] : finalPayloads;
|
||||
} finally {
|
||||
cleanupTyping();
|
||||
}
|
||||
}
|
||||
if (meta.agentMeta && isVerbose()) {
|
||||
logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`);
|
||||
}
|
||||
// If verbose is enabled and this is a new session, prepend a session hint.
|
||||
const sessionIdHint =
|
||||
resolvedVerboseLevel === "on" && isNewSession
|
||||
? (sessionId ??
|
||||
meta.agentMeta?.sessionId ??
|
||||
templatingCtx.SessionId ??
|
||||
"unknown")
|
||||
: undefined;
|
||||
if (sessionIdHint) {
|
||||
finalPayloads = [
|
||||
{ text: `🧭 New session: ${sessionIdHint}` },
|
||||
...payloadArray,
|
||||
];
|
||||
}
|
||||
return finalPayloads.length === 1 ? finalPayloads[0] : finalPayloads;
|
||||
} finally {
|
||||
cleanupTyping();
|
||||
}
|
||||
}
|
||||
|
||||
cleanupTyping();
|
||||
return undefined;
|
||||
|
||||
@@ -9,12 +9,12 @@ import { CONFIG_DIR, normalizeE164 } from "../utils.js";
|
||||
export type SessionScope = "per-sender" | "global";
|
||||
|
||||
export type SessionEntry = {
|
||||
sessionId: string;
|
||||
updatedAt: number;
|
||||
systemSent?: boolean;
|
||||
abortedLastRun?: boolean;
|
||||
thinkingLevel?: string;
|
||||
verboseLevel?: string;
|
||||
sessionId: string;
|
||||
updatedAt: number;
|
||||
systemSent?: boolean;
|
||||
abortedLastRun?: boolean;
|
||||
thinkingLevel?: string;
|
||||
verboseLevel?: string;
|
||||
};
|
||||
|
||||
export const SESSION_STORE_DEFAULT = path.join(CONFIG_DIR, "sessions.json");
|
||||
|
||||
@@ -810,7 +810,11 @@ describe("config and templating", () => {
|
||||
};
|
||||
|
||||
const ack = await index.getReplyFromConfig(
|
||||
{ Body: "[Dec 1 00:00] [🦞 same-phone] /think:high", From: "+1", To: "+2" },
|
||||
{
|
||||
Body: "[Dec 1 00:00] [🦞 same-phone] /think:high",
|
||||
From: "+1",
|
||||
To: "+2",
|
||||
},
|
||||
undefined,
|
||||
cfg,
|
||||
runSpy,
|
||||
@@ -863,7 +867,7 @@ describe("config and templating", () => {
|
||||
);
|
||||
|
||||
expect(runSpy).not.toHaveBeenCalled();
|
||||
expect(ack?.text).toContain("Unrecognized thinking level \"big\"");
|
||||
expect(ack?.text).toContain('Unrecognized thinking level "big"');
|
||||
|
||||
// Send another message; state should not carry any level.
|
||||
const second = await index.getReplyFromConfig(
|
||||
|
||||
Reference in New Issue
Block a user