From 63eb22316a619f2ce16a14866965f15ddb11694a Mon Sep 17 00:00:00 2001 From: Ivan Pereira <183991+ivanrvpereira@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:49:33 +0000 Subject: [PATCH] Replace brittle error text detection with isError flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit renderPerplexityResult was pattern-matching content text prefixes to decide whether to render in error style. This was tightly coupled to exact message strings in index.ts — a wording change would silently break the error styling. Set isError: true in details on every error return path in execute, and check details.isError in the renderer instead. --- src/index.ts | 22 +++++++++++++++++----- src/render/result.ts | 11 ++--------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/index.ts b/src/index.ts index 14aa64e..aa91ccc 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import { Type } from "@sinclair/typebox"; import { registerPerplexityCommands } from "./commands/login.js"; import { authenticate } from "./auth/login.js"; -import { clearToken } from "./auth/storage.js"; + import { formatForLLM } from "./search/format.js"; import { searchPerplexity } from "./search/client.js"; import { renderPerplexityCall } from "./render/call.js"; @@ -99,17 +99,29 @@ export default function (pi: ExtensionAPI) { if (error instanceof AuthError) { return { content: [{ type: "text", text: `Authentication failed: ${error.message}` }], - details: { sourceCount, queryMs }, + details: { sourceCount, queryMs, isError: true }, }; } if (error instanceof SearchError) { if (error.code === "AUTH") { - await clearToken().catch(() => undefined); + // Do NOT clear the token here. The user must re-login explicitly via + // /perplexity-login --force. Clearing automatically would silently discard + // a token that may still be valid (e.g. a transient 401), and removes the + // user's ability to inspect or recover the cached credential themselves. + return { + content: [ + { + type: "text", + text: `Perplexity authentication failed. Run /perplexity-login --force to re-authenticate.`, + }, + ], + details: { sourceCount, queryMs, isError: true }, + }; } return { content: [{ type: "text", text: `Perplexity search failed: ${error.message}` }], - details: { sourceCount, queryMs }, + details: { sourceCount, queryMs, isError: true }, }; } @@ -120,7 +132,7 @@ export default function (pi: ExtensionAPI) { text: `Perplexity search failed: ${errorMessage(error)}`, }, ], - details: { sourceCount, queryMs }, + details: { sourceCount, queryMs, isError: true }, }; } }, diff --git a/src/render/result.ts b/src/render/result.ts index 67e345d..a909b76 100644 --- a/src/render/result.ts +++ b/src/render/result.ts @@ -9,6 +9,7 @@ interface PerplexityResultDetails { uuid?: unknown; toolCallId?: unknown; error?: unknown; + isError?: unknown; } function extractTextContent(result: AgentToolResult): string | undefined { if (!Array.isArray(result?.content)) { @@ -34,14 +35,6 @@ function extractTextContent(result: AgentToolResult): s return undefined; } -function isErrorText(text: string | undefined): boolean { - if (!text) { - return false; - } - - return text.startsWith("Authentication failed:") || text.startsWith("Perplexity search failed:"); -} - export function renderPerplexityResult( result: AgentToolResult, options: ToolRenderResultOptions, @@ -69,7 +62,7 @@ export function renderPerplexityResult( return new Text(theme.fg("error", `Perplexity error: ${error}`), 0, 0); } - if (isErrorText(contentText)) { + if (details.isError === true) { return new Text(theme.fg("error", truncate(contentText ?? "Perplexity request failed", 200)), 0, 0); }