Implement extensions/vision/src/errors.ts (ToolError + categories) #253

Closed
opened 2026-09-17 03:32:30 +00:00 by david · 1 comment
Owner

Summary

Create extensions/vision/src/errors.ts, exporting the ToolError class and the ToolErrorCategory union that every other vision module throws to fail a tool call. This is the foundation the rest of the extension is written against, so it lands first.

Background

The vision extension turns a local image-understanding CLI into a pi tool. pi only reports a tool call as failed when execute() throws; returning a value never sets isError (pinned pi docs, docs/extensions.md, "Signaling errors"). The extension therefore funnels every failure through one typed error so callers and tests can distinguish categories while the LLM only ever sees an actionable message — never a raw stack trace.

This mirrors the sibling extensions/mongodb/src/errors.ts, which already establishes the pattern in this repo (a ToolError class carrying a category, with name = "ToolError").

Downstream modules (config.ts, images.ts, client.ts, tool.ts) all import ToolError and ToolErrorCategory from here, so this module must have no dependencies of its own on other vision modules.

Documentation Required

A separate process downloads these into the listed folders before this issue is implemented. Check the folder for the actual reference material before starting.

docs/reference/pi-coding-agent/

  • Local package docs: node_modules/@earendil-works/pi-coding-agent/docs/extensions.md, section "Signaling errors" — the throw-to-fail contract (returning a value never sets the error flag). The docs are shipped inside the installed package; the canonical location in this repo is that path.
  • Same file, section on tool execute() error handling — how a thrown error is surfaced to the LLM/UI.

docs/reference/typescript/

Implementation Details

Create extensions/vision/src/errors.ts with exactly this public surface:

export type ToolErrorCategory =
  | "config" | "image" | "http" | "auth" | "rate-limit"
  | "network" | "timeout" | "aborted" | "response" | "unexpected";

export class ToolError extends Error {
  readonly category: ToolErrorCategory;
  constructor(message: string, category: ToolErrorCategory);
}

Requirements:

  • constructor calls super(message), sets this.name = "ToolError" and this.category = category (readonly field).
  • Follow extensions/mongodb/src/errors.ts for shape/naming; use the exact 10 categories above (this repo's other extensions use their own category sets — do not copy those).
  • Keep the module free of imports from other vision files so it can be imported anywhere with no cycle.
  • Add a file-level doc comment explaining the throw-to-fail rationale and pointing at the mongodb precedent.
  • TypeScript source; the repo runs tests via node --test directly against .ts files (Node 24 strips types natively), so use import type where appropriate.

Test file extensions/vision/src/errors.test.ts (write it first, TDD):

  • new ToolError("msg", "image") is instanceof Error and instanceof ToolError.
  • name === "ToolError", message preserved verbatim, category preserved verbatim.
  • A ToolError can be thrown and caught with category intact.
  • Each of the 10 category literals is assignable (a compile-time satisfies / typed array is enough).

Acceptance Criteria

  • extensions/vision/src/errors.ts exports ToolError and ToolErrorCategory with the exact 10 categories listed above.
  • ToolError extends Error, sets name = "ToolError", and stores the category in a readonly category field.
  • The module imports nothing from other vision modules.
  • The category is a closed union (not string) and no any escapes into the public surface.
  • extensions/vision/src/errors.test.ts exists and asserts instanceof, name, message, category, and throw/catch behaviour.
  • node --test extensions/vision/src/errors.test.ts passes from the repo root.

Test Plan

cd /Users/david/Projects/pi-extensions-and-skills
node --test extensions/vision/src/errors.test.ts

Expected: all tests pass, zero network access, no files written.

Quick manual confirmation that the types are usable from a later module:

node --experimental-strip-types -e "import('./extensions/vision/src/errors.ts').then(m => { const e = new m.ToolError('x','timeout'); console.log(e.name, e.category, e instanceof Error); })"

Expected: ToolError timeout true.

## Summary Create `extensions/vision/src/errors.ts`, exporting the `ToolError` class and the `ToolErrorCategory` union that every other `vision` module throws to fail a tool call. This is the foundation the rest of the extension is written against, so it lands first. ## Background The `vision` extension turns a local image-understanding CLI into a pi tool. pi only reports a tool call as failed when `execute()` **throws**; returning a value never sets `isError` (pinned pi docs, `docs/extensions.md`, "Signaling errors"). The extension therefore funnels every failure through one typed error so callers and tests can distinguish categories while the LLM only ever sees an actionable message — never a raw stack trace. This mirrors the sibling `extensions/mongodb/src/errors.ts`, which already establishes the pattern in this repo (a `ToolError` class carrying a `category`, with `name = "ToolError"`). Downstream modules (`config.ts`, `images.ts`, `client.ts`, `tool.ts`) all import `ToolError` and `ToolErrorCategory` from here, so this module must have no dependencies of its own on other `vision` modules. ## Documentation Required A separate process downloads these into the listed folders before this issue is implemented. Check the folder for the actual reference material before starting. **`docs/reference/pi-coding-agent/`** - Local package docs: `node_modules/@earendil-works/pi-coding-agent/docs/extensions.md`, section "Signaling errors" — the throw-to-fail contract (`returning a value never sets the error flag`). The docs are shipped inside the installed package; the canonical location in this repo is that path. - Same file, section on tool `execute()` error handling — how a thrown error is surfaced to the LLM/UI. **`docs/reference/typescript/`** - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error — subclassing `Error` correctly (prototype, `name`, `message`), so `instanceof ToolError` works in tests. ## Implementation Details Create `extensions/vision/src/errors.ts` with exactly this public surface: ```ts export type ToolErrorCategory = | "config" | "image" | "http" | "auth" | "rate-limit" | "network" | "timeout" | "aborted" | "response" | "unexpected"; export class ToolError extends Error { readonly category: ToolErrorCategory; constructor(message: string, category: ToolErrorCategory); } ``` Requirements: - `constructor` calls `super(message)`, sets `this.name = "ToolError"` and `this.category = category` (readonly field). - Follow `extensions/mongodb/src/errors.ts` for shape/naming; use the exact 10 categories above (this repo's other extensions use their own category sets — do not copy those). - Keep the module free of imports from other `vision` files so it can be imported anywhere with no cycle. - Add a file-level doc comment explaining the throw-to-fail rationale and pointing at the mongodb precedent. - TypeScript source; the repo runs tests via `node --test` directly against `.ts` files (Node 24 strips types natively), so use `import type` where appropriate. Test file `extensions/vision/src/errors.test.ts` (write it first, TDD): - `new ToolError("msg", "image")` is `instanceof Error` and `instanceof ToolError`. - `name === "ToolError"`, `message` preserved verbatim, `category` preserved verbatim. - A `ToolError` can be thrown and caught with `category` intact. - Each of the 10 category literals is assignable (a compile-time `satisfies` / typed array is enough). ## Acceptance Criteria - [ ] `extensions/vision/src/errors.ts` exports `ToolError` and `ToolErrorCategory` with the exact 10 categories listed above. - [ ] `ToolError` extends `Error`, sets `name = "ToolError"`, and stores the category in a `readonly category` field. - [ ] The module imports nothing from other `vision` modules. - [ ] The category is a closed union (not `string`) and no `any` escapes into the public surface. - [ ] `extensions/vision/src/errors.test.ts` exists and asserts `instanceof`, `name`, `message`, `category`, and throw/catch behaviour. - [ ] `node --test extensions/vision/src/errors.test.ts` passes from the repo root. ## Test Plan ```bash cd /Users/david/Projects/pi-extensions-and-skills node --test extensions/vision/src/errors.test.ts ``` Expected: all tests pass, zero network access, no files written. Quick manual confirmation that the types are usable from a later module: ```bash node --experimental-strip-types -e "import('./extensions/vision/src/errors.ts').then(m => { const e = new m.ToolError('x','timeout'); console.log(e.name, e.category, e instanceof Error); })" ``` Expected: `ToolError timeout true`.
david self-assigned this 2026-09-17 03:32:30 +00:00
david closed this issue 2026-09-17 08:09:46 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #267

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/267
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
david/pi-extensions-and-skills#253
No description provided.