diff --git a/src/agents/model-catalog.ts b/src/agents/model-catalog.ts index f4bbc7268..b28e87131 100644 --- a/src/agents/model-catalog.ts +++ b/src/agents/model-catalog.ts @@ -1,5 +1,5 @@ -import { resolveClawdisAgentDir } from "./agent-paths.js"; import { type ClawdisConfig, loadConfig } from "../config/config.js"; +import { resolveClawdisAgentDir } from "./agent-paths.js"; import { ensureClawdisModelsJson } from "./models-config.js"; export type ModelCatalogEntry = { diff --git a/src/auto-reply/reply.directive.test.ts b/src/auto-reply/reply.directive.test.ts index bded70828..faf1a0493 100644 --- a/src/auto-reply/reply.directive.test.ts +++ b/src/auto-reply/reply.directive.test.ts @@ -12,8 +12,8 @@ vi.mock("../agents/model-catalog.js", () => ({ loadModelCatalog: vi.fn(), })); -import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { loadModelCatalog } from "../agents/model-catalog.js"; +import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { loadSessionStore, resolveSessionKey, @@ -277,10 +277,7 @@ describe("directive parsing", () => { provider: "anthropic", model: "claude-opus-4-5", workspace: path.join(home, "clawd"), - allowedModels: [ - "anthropic/claude-opus-4-5", - "openai/gpt-4.1-mini", - ], + allowedModels: ["anthropic/claude-opus-4-5", "openai/gpt-4.1-mini"], }, session: { store: storePath }, }, @@ -316,7 +313,7 @@ describe("directive parsing", () => { const text = Array.isArray(res) ? res[0]?.text : res?.text; expect(text).toContain("Model set to openai/gpt-4.1-mini"); const store = loadSessionStore(storePath); - const entry = store["main"]; + const entry = store.main; expect(entry.modelOverride).toBe("gpt-4.1-mini"); expect(entry.providerOverride).toBe("openai"); expect(runEmbeddedPiAgent).not.toHaveBeenCalled(); diff --git a/src/auto-reply/reply.ts b/src/auto-reply/reply.ts index 5ddff6af9..d170f011b 100644 --- a/src/auto-reply/reply.ts +++ b/src/auto-reply/reply.ts @@ -44,6 +44,7 @@ import { normalizeGroupActivation, parseActivationCommand, } from "./group-activation.js"; +import { extractModelDirective } from "./model.js"; import { buildStatusMessage } from "./status.js"; import type { MsgContext, TemplateContext } from "./templating.js"; import { @@ -52,7 +53,6 @@ import { type ThinkLevel, type VerboseLevel, } from "./thinking.js"; -import { extractModelDirective } from "./model.js"; import { SILENT_REPLY_TOKEN } from "./tokens.js"; import { isAudio, transcribeInboundAudio } from "./transcription.js"; import type { GetReplyOptions, ReplyPayload } from "./types.js"; @@ -199,9 +199,7 @@ export async function getReplyFromConfig( const configuredTypingSeconds = agentCfg?.typingIntervalSeconds ?? sessionCfg?.typingIntervalSeconds; const typingIntervalSeconds = - typeof configuredTypingSeconds === "number" - ? configuredTypingSeconds - : 6; + typeof configuredTypingSeconds === "number" ? configuredTypingSeconds : 6; const typingIntervalMs = typingIntervalSeconds * 1000; const cleanupTyping = () => { if (typingTimer) { @@ -393,7 +391,8 @@ export async function getReplyFromConfig( const hasStoredOverride = Boolean( sessionEntry?.modelOverride || sessionEntry?.providerOverride, ); - const needsModelCatalog = hasModelDirective || hasAllowlist || hasStoredOverride; + const needsModelCatalog = + hasModelDirective || hasAllowlist || hasStoredOverride; let allowedModelKeys = new Set(); let allowedModelCatalog: Awaited> = []; let resetModelOverride = false; @@ -467,7 +466,8 @@ export async function getReplyFromConfig( } for (const entry of allowedModelCatalog) { const label = `${entry.provider}/${entry.id}`; - const suffix = entry.name && entry.name !== entry.id ? ` — ${entry.name}` : ""; + const suffix = + entry.name && entry.name !== entry.id ? ` — ${entry.name}` : ""; lines.push(`- ${label}${suffix}`); } cleanupTyping(); @@ -585,7 +585,8 @@ export async function getReplyFromConfig( const key = modelKey(parsed.provider, parsed.model); if (allowedModelKeys.size === 0 || allowedModelKeys.has(key)) { const isDefault = - parsed.provider === defaultProvider && parsed.model === defaultModel; + parsed.provider === defaultProvider && + parsed.model === defaultModel; if (isDefault) { delete sessionEntry.providerOverride; delete sessionEntry.modelOverride; @@ -767,46 +768,41 @@ export async function getReplyFromConfig( const shouldInjectGroupIntro = isGroupChat && (isFirstTurnInSession || sessionEntry?.groupActivationNeedsSystemIntro); - const groupIntro = - shouldInjectGroupIntro - ? (() => { - const activation = - normalizeGroupActivation(sessionEntry?.groupActivation) ?? - defaultGroupActivation(); - const subject = sessionCtx.GroupSubject?.trim(); - const members = sessionCtx.GroupMembers?.trim(); - const subjectLine = subject - ? `You are replying inside the WhatsApp group "${subject}".` - : "You are replying inside a WhatsApp group chat."; - const membersLine = members - ? `Group members: ${members}.` + const groupIntro = shouldInjectGroupIntro + ? (() => { + const activation = + normalizeGroupActivation(sessionEntry?.groupActivation) ?? + defaultGroupActivation(); + const subject = sessionCtx.GroupSubject?.trim(); + const members = sessionCtx.GroupMembers?.trim(); + const subjectLine = subject + ? `You are replying inside the WhatsApp group "${subject}".` + : "You are replying inside a WhatsApp group chat."; + const membersLine = members ? `Group members: ${members}.` : undefined; + const activationLine = + activation === "always" + ? "Activation: always-on (you receive every group message)." + : "Activation: trigger-only (you are invoked only when explicitly mentioned; recent context may be included)."; + const silenceLine = + activation === "always" + ? `If no response is needed, reply with exactly "${SILENT_REPLY_TOKEN}" (no other text) so Clawdis stays silent.` : undefined; - const activationLine = - activation === "always" - ? "Activation: always-on (you receive every group message)." - : "Activation: trigger-only (you are invoked only when explicitly mentioned; recent context may be included)."; - const silenceLine = - activation === "always" - ? `If no response is needed, reply with exactly "${SILENT_REPLY_TOKEN}" (no other text) so Clawdis stays silent.` - : undefined; - const cautionLine = - activation === "always" - ? "Be extremely selective: reply only when you are directly addressed, asked a question, or can add clear value. Otherwise stay silent." - : undefined; - return [ - subjectLine, - membersLine, - activationLine, - silenceLine, - cautionLine, - ] - .filter(Boolean) - .join(" ") - .concat( - " Address the specific sender noted in the message context.", - ); - })() - : ""; + const cautionLine = + activation === "always" + ? "Be extremely selective: reply only when you are directly addressed, asked a question, or can add clear value. Otherwise stay silent." + : undefined; + return [ + subjectLine, + membersLine, + activationLine, + silenceLine, + cautionLine, + ] + .filter(Boolean) + .join(" ") + .concat(" Address the specific sender noted in the message context."); + })() + : ""; const baseBody = sessionCtx.BodyStripped ?? sessionCtx.Body ?? ""; const rawBodyTrimmed = (ctx.Body ?? "").trim(); const baseBodyTrimmedRaw = baseBody.trim(); diff --git a/src/cli/gateway-cli.ts b/src/cli/gateway-cli.ts index 879137255..9909d0587 100644 --- a/src/cli/gateway-cli.ts +++ b/src/cli/gateway-cli.ts @@ -116,14 +116,11 @@ export function registerGatewayCli(program: Command) { } const authModeRaw = opts.auth ? String(opts.auth) : undefined; const authMode = - authModeRaw === "token" || - authModeRaw === "password" + authModeRaw === "token" || authModeRaw === "password" ? authModeRaw : null; if (authModeRaw && !authMode) { - defaultRuntime.error( - 'Invalid --auth (use "token" or "password")', - ); + defaultRuntime.error('Invalid --auth (use "token" or "password")'); defaultRuntime.exit(1); return; } @@ -334,14 +331,11 @@ export function registerGatewayCli(program: Command) { } const authModeRaw = opts.auth ? String(opts.auth) : undefined; const authMode = - authModeRaw === "token" || - authModeRaw === "password" + authModeRaw === "token" || authModeRaw === "password" ? authModeRaw : null; if (authModeRaw && !authMode) { - defaultRuntime.error( - 'Invalid --auth (use "token" or "password")', - ); + defaultRuntime.error('Invalid --auth (use "token" or "password")'); defaultRuntime.exit(1); return; } diff --git a/src/config/config.ts b/src/config/config.ts index f0093d697..166289ab4 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -535,12 +535,7 @@ const ClawdisSchema = z.object({ .optional(), auth: z .object({ - mode: z - .union([ - z.literal("token"), - z.literal("password"), - ]) - .optional(), + mode: z.union([z.literal("token"), z.literal("password")]).optional(), password: z.string().optional(), allowTailscale: z.boolean().optional(), }) diff --git a/src/gateway/server.ts b/src/gateway/server.ts index 41a31949d..18010fbdf 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -17,8 +17,8 @@ import { } from "../agents/defaults.js"; import { loadModelCatalog, - resetModelCatalogCacheForTest, type ModelCatalogEntry, + resetModelCatalogCacheForTest, } from "../agents/model-catalog.js"; import { installSkill } from "../agents/skills-install.js"; import { buildWorkspaceSkillStatus } from "../agents/skills-status.js"; @@ -1393,11 +1393,7 @@ export async function startGatewayServer( return; } const { e164, jid } = readWebSelfId(); - const identity = e164 - ? e164 - : jid - ? `jid ${jid}` - : "unknown"; + const identity = e164 ? e164 : jid ? `jid ${jid}` : "unknown"; logWhatsApp.info(`starting provider (${identity})`); whatsappAbort = new AbortController(); whatsappRuntime = {