diff options
Diffstat (limited to 'src/errors.ts')
| -rw-r--r-- | src/errors.ts | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/src/errors.ts b/src/errors.ts new file mode 100644 index 0000000..d003e4f --- /dev/null +++ b/src/errors.ts @@ -0,0 +1,56 @@ +/** + * Failure vocabulary. + * + * Every one of these is *thrown*, never returned. pi only sets `isError: true` + * on a tool result when `execute()` throws — a returned object with an `error` + * field looks to the model exactly like a successful search that found nothing. + * + * `SearchChallengeError` is the one the agent is expected to act on rather than + * just report: it means a human has to touch the browser before search can work + * again, so its message says so in words the agent can pass to the operator. + */ + +/** The search engine served a captcha, consent wall, or other human check. */ +export class SearchChallengeError extends Error { + override readonly name = "SearchChallengeError"; + /** Machine-readable flavour of the challenge, for callers that branch on it. */ + readonly challenge: string; + /** The page the human needs to visit to clear it. */ + readonly pageUrl: string; + /** Which browser is blocked, e.g. "10.88.0.25:9223 (built-in default)". */ + readonly endpoint: string; + + constructor(options: { challenge: string; pageUrl: string; endpoint: string; detail?: string | undefined }) { + super( + [ + `Search is blocked by a human verification challenge (${options.challenge}).`, + options.detail ? `Page said: ${options.detail}` : null, + `This cannot be solved by the agent — a person has to clear it in the browser at ${options.endpoint}.`, + `Ask the user to open ${options.pageUrl} in that Chrome, complete the challenge or accept the consent dialog,`, + `then retry the search. The cookie it sets persists in that browser profile, so one pass unblocks later searches.`, + ] + .filter((line) => line !== null) + .join(" "), + ); + this.challenge = options.challenge; + this.pageUrl = options.pageUrl; + this.endpoint = options.endpoint; + } +} + +/** The browser could not be reached or spoke CDP badly. */ +export class BrowserUnavailableError extends Error { + override readonly name = "BrowserUnavailableError"; +} + +/** The search itself ran but produced nothing usable. */ +export class NoResultsError extends Error { + override readonly name = "NoResultsError"; +} + +/** The per-search deadline expired, or the user pressed Esc. */ +export class SearchAbortedError extends Error { + override readonly name = "SearchAbortedError"; +} + +export const messageOf = (e: unknown): string => (e instanceof Error ? e.message : String(e)); |
