Chore: format + lint fixes

This commit is contained in:
Peter Steinberger
2025-12-03 09:09:34 +00:00
parent 85917d4769
commit 53c1674382
4 changed files with 216 additions and 200 deletions

View File

@@ -76,7 +76,11 @@ function parsePiJson(raw: string): AgentParseResult {
} }
: undefined; : undefined;
return { texts, toolResults: toolResults.length ? toolResults : undefined, meta }; return {
texts,
toolResults: toolResults.length ? toolResults : undefined,
meta,
};
} }
export const piSpec: AgentSpec = { export const piSpec: AgentSpec = {

View File

@@ -37,69 +37,77 @@ type ThinkLevel = "off" | "minimal" | "low" | "medium" | "high";
type VerboseLevel = "off" | "on"; type VerboseLevel = "off" | "on";
function normalizeThinkLevel(raw?: string | null): ThinkLevel | undefined { function normalizeThinkLevel(raw?: string | null): ThinkLevel | undefined {
if (!raw) return undefined; if (!raw) return undefined;
const key = raw.toLowerCase(); const key = raw.toLowerCase();
if (["off"].includes(key)) return "off"; if (["off"].includes(key)) return "off";
if (["min", "minimal"].includes(key)) return "minimal"; if (["min", "minimal"].includes(key)) return "minimal";
if (["low", "thinkhard", "think-hard", "think_hard"].includes(key)) if (["low", "thinkhard", "think-hard", "think_hard"].includes(key))
return "low"; return "low";
if (["med", "medium", "thinkharder", "think-harder", "harder"].includes(key)) if (["med", "medium", "thinkharder", "think-harder", "harder"].includes(key))
return "medium"; return "medium";
if (["high", "ultra", "ultrathink", "think-hard", "thinkhardest", "highest", "max"].includes(key)) if (
return "high"; [
if (["think"].includes(key)) return "minimal"; "high",
return undefined; "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 { function normalizeVerboseLevel(raw?: string | null): VerboseLevel | undefined {
if (!raw) return undefined; if (!raw) return undefined;
const key = raw.toLowerCase(); const key = raw.toLowerCase();
if (["off", "false", "no", "0"].includes(key)) return "off"; if (["off", "false", "no", "0"].includes(key)) return "off";
if (["on", "full", "true", "yes", "1"].includes(key)) return "on"; if (["on", "full", "true", "yes", "1"].includes(key)) return "on";
return undefined; return undefined;
} }
function extractThinkDirective(body?: string): { function extractThinkDirective(body?: string): {
cleaned: string; cleaned: string;
thinkLevel?: ThinkLevel; thinkLevel?: ThinkLevel;
rawLevel?: string; rawLevel?: string;
hasDirective: boolean; hasDirective: boolean;
} { } {
if (!body) return { cleaned: "", hasDirective: false }; if (!body) return { cleaned: "", hasDirective: false };
// Match the longest keyword first to avoid partial captures (e.g. "/think:high") // Match the longest keyword first to avoid partial captures (e.g. "/think:high")
const match = body.match( const match = body.match(/\/(?:thinking|think|t)\s*:?\s*([a-zA-Z-]+)\b/i);
/\/(?:thinking|think|t)\s*:?\s*([a-zA-Z-]+)\b/i, const thinkLevel = normalizeThinkLevel(match?.[1]);
); const cleaned = match
const thinkLevel = normalizeThinkLevel(match?.[1]); ? body.replace(match[0], "").replace(/\s+/g, " ").trim()
const cleaned = match : body.trim();
? body.replace(match[0], "").replace(/\s+/g, " ").trim() return {
: body.trim(); cleaned,
return { thinkLevel,
cleaned, rawLevel: match?.[1],
thinkLevel, hasDirective: !!match,
rawLevel: match?.[1], };
hasDirective: !!match,
};
} }
function extractVerboseDirective(body?: string): { function extractVerboseDirective(body?: string): {
cleaned: string; cleaned: string;
verboseLevel?: VerboseLevel; verboseLevel?: VerboseLevel;
rawLevel?: string; rawLevel?: string;
hasDirective: boolean; hasDirective: boolean;
} { } {
if (!body) return { cleaned: "", hasDirective: false }; if (!body) return { cleaned: "", hasDirective: false };
const match = body.match(/\/(?:verbose|v)\s*:?\s*([a-zA-Z-]+)\b/i); const match = body.match(/\/(?:verbose|v)\s*:?\s*([a-zA-Z-]+)\b/i);
const verboseLevel = normalizeVerboseLevel(match?.[1]); const verboseLevel = normalizeVerboseLevel(match?.[1]);
const cleaned = match const cleaned = match
? body.replace(match[0], "").replace(/\s+/g, " ").trim() ? body.replace(match[0], "").replace(/\s+/g, " ").trim()
: body.trim(); : body.trim();
return { return {
cleaned, cleaned,
verboseLevel, verboseLevel,
rawLevel: match?.[1], rawLevel: match?.[1],
hasDirective: !!match, hasDirective: !!match,
}; };
} }
function isAbortTrigger(text?: string): boolean { function isAbortTrigger(text?: string): boolean {
@@ -242,96 +250,96 @@ export async function getReplyFromConfig(
IsNewSession: isNewSession ? "true" : "false", IsNewSession: isNewSession ? "true" : "false",
}; };
const { const {
cleaned: thinkCleaned, cleaned: thinkCleaned,
thinkLevel: inlineThink, thinkLevel: inlineThink,
rawLevel: rawThinkLevel, rawLevel: rawThinkLevel,
hasDirective: hasThinkDirective, hasDirective: hasThinkDirective,
} = extractThinkDirective(sessionCtx.BodyStripped ?? sessionCtx.Body ?? ""); } = extractThinkDirective(sessionCtx.BodyStripped ?? sessionCtx.Body ?? "");
const { const {
cleaned: verboseCleaned, cleaned: verboseCleaned,
verboseLevel: inlineVerbose, verboseLevel: inlineVerbose,
rawLevel: rawVerboseLevel, rawLevel: rawVerboseLevel,
hasDirective: hasVerboseDirective, hasDirective: hasVerboseDirective,
} = extractVerboseDirective(thinkCleaned); } = extractVerboseDirective(thinkCleaned);
sessionCtx.Body = verboseCleaned; sessionCtx.Body = verboseCleaned;
sessionCtx.BodyStripped = verboseCleaned; sessionCtx.BodyStripped = verboseCleaned;
let resolvedThinkLevel = let resolvedThinkLevel =
inlineThink ?? inlineThink ??
(sessionEntry?.thinkingLevel as ThinkLevel | undefined) ?? (sessionEntry?.thinkingLevel as ThinkLevel | undefined) ??
(reply?.thinkingDefault as ThinkLevel | undefined); (reply?.thinkingDefault as ThinkLevel | undefined);
let resolvedVerboseLevel = const resolvedVerboseLevel =
inlineVerbose ?? inlineVerbose ??
(sessionEntry?.verboseLevel as VerboseLevel | undefined) ?? (sessionEntry?.verboseLevel as VerboseLevel | undefined) ??
(reply?.verboseDefault as VerboseLevel | undefined); (reply?.verboseDefault as VerboseLevel | undefined);
const directiveOnly = (() => { const directiveOnly = (() => {
if (!hasThinkDirective) return false; if (!hasThinkDirective) return false;
if (!thinkCleaned) return true; if (!thinkCleaned) return true;
// Ignore bracketed prefixes (timestamps, same-phone markers, etc.) // Ignore bracketed prefixes (timestamps, same-phone markers, etc.)
const stripped = thinkCleaned.replace(/\[[^\]]+\]\s*/g, "").trim(); const stripped = thinkCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
return stripped.length === 0; return stripped.length === 0;
})(); })();
// Directive-only message => persist session thinking level and return ack // Directive-only message => persist session thinking level and return ack
if (directiveOnly) { if (directiveOnly) {
if (!inlineThink) { if (!inlineThink) {
cleanupTyping(); cleanupTyping();
return { return {
text: `Unrecognized thinking level "${rawThinkLevel ?? ""}". Valid levels: off, minimal, low, medium, high.`, text: `Unrecognized thinking level "${rawThinkLevel ?? ""}". Valid levels: off, minimal, low, medium, high.`,
}; };
} }
if (sessionEntry && sessionStore && sessionKey) { if (sessionEntry && sessionStore && sessionKey) {
if (inlineThink === "off") { if (inlineThink === "off") {
delete sessionEntry.thinkingLevel; delete sessionEntry.thinkingLevel;
} else { } else {
sessionEntry.thinkingLevel = inlineThink; sessionEntry.thinkingLevel = inlineThink;
} }
sessionEntry.updatedAt = Date.now(); sessionEntry.updatedAt = Date.now();
sessionStore[sessionKey] = sessionEntry; sessionStore[sessionKey] = sessionEntry;
await saveSessionStore(storePath, sessionStore); await saveSessionStore(storePath, sessionStore);
} }
const ack = const ack =
inlineThink === "off" inlineThink === "off"
? "Thinking disabled." ? "Thinking disabled."
: `Thinking level set to ${inlineThink}.`; : `Thinking level set to ${inlineThink}.`;
cleanupTyping(); cleanupTyping();
return { text: ack }; return { text: ack };
} }
const verboseDirectiveOnly = (() => { const verboseDirectiveOnly = (() => {
if (!hasVerboseDirective) return false; if (!hasVerboseDirective) return false;
if (!verboseCleaned) return true; if (!verboseCleaned) return true;
const stripped = verboseCleaned.replace(/\[[^\]]+\]\s*/g, "").trim(); const stripped = verboseCleaned.replace(/\[[^\]]+\]\s*/g, "").trim();
return stripped.length === 0; return stripped.length === 0;
})(); })();
if (verboseDirectiveOnly) { if (verboseDirectiveOnly) {
if (!inlineVerbose) { if (!inlineVerbose) {
cleanupTyping(); cleanupTyping();
return { return {
text: `Unrecognized verbose level "${rawVerboseLevel ?? ""}". Valid levels: off, on.`, text: `Unrecognized verbose level "${rawVerboseLevel ?? ""}". Valid levels: off, on.`,
}; };
} }
if (sessionEntry && sessionStore && sessionKey) { if (sessionEntry && sessionStore && sessionKey) {
if (inlineVerbose === "off") { if (inlineVerbose === "off") {
delete sessionEntry.verboseLevel; delete sessionEntry.verboseLevel;
} else { } else {
sessionEntry.verboseLevel = inlineVerbose; sessionEntry.verboseLevel = inlineVerbose;
} }
sessionEntry.updatedAt = Date.now(); sessionEntry.updatedAt = Date.now();
sessionStore[sessionKey] = sessionEntry; sessionStore[sessionKey] = sessionEntry;
await saveSessionStore(storePath, sessionStore); await saveSessionStore(storePath, sessionStore);
} }
const ack = const ack =
inlineVerbose === "off" inlineVerbose === "off"
? "Verbose logging disabled." ? "Verbose logging disabled."
: "Verbose logging enabled."; : "Verbose logging enabled.";
cleanupTyping(); cleanupTyping();
return { text: ack }; return { text: ack };
} }
// Optional allowlist by origin number (E.164 without whatsapp: prefix) // Optional allowlist by origin number (E.164 without whatsapp: prefix)
const allowFrom = cfg.inbound?.allowFrom; const allowFrom = cfg.inbound?.allowFrom;
@@ -489,11 +497,11 @@ export async function getReplyFromConfig(
const isHeartbeat = opts?.isHeartbeat === true; const isHeartbeat = opts?.isHeartbeat === true;
if (reply && reply.mode === "command") { if (reply && reply.mode === "command") {
const heartbeatCommand = isHeartbeat const heartbeatCommand = isHeartbeat
? (reply as { heartbeatCommand?: string[] }).heartbeatCommand ? (reply as { heartbeatCommand?: string[] }).heartbeatCommand
: undefined; : undefined;
const commandArgs = heartbeatCommand?.length const commandArgs = heartbeatCommand?.length
? heartbeatCommand ? heartbeatCommand
: reply.command; : reply.command;
@@ -502,34 +510,34 @@ export async function getReplyFromConfig(
return undefined; return undefined;
} }
await onReplyStart(); await onReplyStart();
const commandReply = { const commandReply = {
...reply, ...reply,
command: commandArgs, command: commandArgs,
mode: "command" as const, mode: "command" as const,
}; };
try { try {
const runResult = await runCommandReply({ const runResult = await runCommandReply({
reply: commandReply, reply: commandReply,
templatingCtx, templatingCtx,
sendSystemOnce, sendSystemOnce,
isNewSession, isNewSession,
isFirstTurnInSession, isFirstTurnInSession,
systemSent, systemSent,
timeoutMs, timeoutMs,
timeoutSeconds, timeoutSeconds,
commandRunner, commandRunner,
thinkLevel: resolvedThinkLevel, thinkLevel: resolvedThinkLevel,
verboseLevel: resolvedVerboseLevel, verboseLevel: resolvedVerboseLevel,
}); });
const payloadArray = runResult.payloads ?? []; const payloadArray = runResult.payloads ?? [];
const meta = runResult.meta; const meta = runResult.meta;
let finalPayloads = payloadArray; let finalPayloads = payloadArray;
if (!finalPayloads || finalPayloads.length === 0) { if (!finalPayloads || finalPayloads.length === 0) {
return undefined; return undefined;
} }
if (sessionCfg && sessionStore && sessionKey) { if (sessionCfg && sessionStore && sessionKey) {
const returnedSessionId = meta.agentMeta?.sessionId; const returnedSessionId = meta.agentMeta?.sessionId;
if (returnedSessionId && returnedSessionId !== sessionId) { if (returnedSessionId && returnedSessionId !== sessionId) {
const entry = sessionEntry ?? const entry = sessionEntry ??
sessionStore[sessionKey] ?? { sessionStore[sessionKey] ?? {
@@ -553,28 +561,28 @@ export async function getReplyFromConfig(
} }
} }
} }
if (meta.agentMeta && isVerbose()) { if (meta.agentMeta && isVerbose()) {
logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`); logVerbose(`Agent meta: ${JSON.stringify(meta.agentMeta)}`);
} }
// If verbose is enabled and this is a new session, prepend a session hint. // If verbose is enabled and this is a new session, prepend a session hint.
const sessionIdHint = const sessionIdHint =
resolvedVerboseLevel === "on" && isNewSession resolvedVerboseLevel === "on" && isNewSession
? sessionId ?? ? (sessionId ??
meta.agentMeta?.sessionId ?? meta.agentMeta?.sessionId ??
templatingCtx.SessionId ?? templatingCtx.SessionId ??
"unknown" "unknown")
: undefined; : undefined;
if (sessionIdHint) { if (sessionIdHint) {
finalPayloads = [ finalPayloads = [
{ text: `🧭 New session: ${sessionIdHint}` }, { text: `🧭 New session: ${sessionIdHint}` },
...payloadArray, ...payloadArray,
]; ];
} }
return finalPayloads.length === 1 ? finalPayloads[0] : finalPayloads; return finalPayloads.length === 1 ? finalPayloads[0] : finalPayloads;
} finally { } finally {
cleanupTyping(); cleanupTyping();
} }
} }
cleanupTyping(); cleanupTyping();
return undefined; return undefined;

View File

@@ -9,12 +9,12 @@ import { CONFIG_DIR, normalizeE164 } from "../utils.js";
export type SessionScope = "per-sender" | "global"; export type SessionScope = "per-sender" | "global";
export type SessionEntry = { export type SessionEntry = {
sessionId: string; sessionId: string;
updatedAt: number; updatedAt: number;
systemSent?: boolean; systemSent?: boolean;
abortedLastRun?: boolean; abortedLastRun?: boolean;
thinkingLevel?: string; thinkingLevel?: string;
verboseLevel?: string; verboseLevel?: string;
}; };
export const SESSION_STORE_DEFAULT = path.join(CONFIG_DIR, "sessions.json"); export const SESSION_STORE_DEFAULT = path.join(CONFIG_DIR, "sessions.json");

View File

@@ -810,7 +810,11 @@ describe("config and templating", () => {
}; };
const ack = await index.getReplyFromConfig( 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, undefined,
cfg, cfg,
runSpy, runSpy,
@@ -863,7 +867,7 @@ describe("config and templating", () => {
); );
expect(runSpy).not.toHaveBeenCalled(); 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. // Send another message; state should not carry any level.
const second = await index.getReplyFromConfig( const second = await index.getReplyFromConfig(