# aimask

**Your code, the tokens of whoever uses it.**

Make an AI feature for your site. Whoever uses it spends their own AI tokens. No keys to hold, no server to run, no bill.

`window.aimask` · Chrome MV3 · v0.1

## Why

- **no keys** — You never touch an API key. Theirs stays in their browser.
- **no bill** — They pay for what they use. You get no bill.
- **no backend** — No server. One `window.aimask` call on the page.

## Quickstart

Add the SDK (types, a detector, named errors; zero deps):

```bash
npm install @aimask/sdk
```

Find the provider. Injected at `document_start`; `null` means not installed:

```ts
import { getAimask } from "@aimask/sdk";

const aimask = getAimask(); // Aimask | null
if (aimask === null) {
  // extension not installed, link the user to install it
}
```

Request a budget. The first call opens consent and a budget; silent after that, until spent or revoked:

```ts
const status = await aimask.availability();
// "available" | "requires-consent" | "unavailable"

const opened = await aimask.requestSession({
  needs: { context: null, vision: null, tools: null, json: null },
  prefer: { tier: "fast", models: null },
  intent: "Summarize the current article",
});
if (!opened.ok) return; // opened.error.code
const session = opened.data;
```

Run a completion. Every field is explicit. What you send is what runs:

```ts
const result = await session.chat({
  messages: [
    { role: "user", content: "Hello", name: null, tool_call_id: null, tool_calls: null },
  ],
  tools: null,
  tool_choice: null,
  response_format: null,
  temperature: null,
  max_tokens: 300,
  stop: null,
  signal: null,
});
if (!result.ok) return; // result.error.code
console.log(result.data.message.content);
```

> No default spends their money. If it isn't in the call, it doesn't happen.

Stream token by token. Same shape as `chat`; pass an `AbortSignal` to stop. Each delta is a Result, so nothing throws:

```ts
const stream = session.chatStream({ /* same shape as chat */ });
for await (const delta of stream) {
  if (!delta.ok) break; // delta.error.code
  if (delta.data.content) {
    process.stdout.write(delta.data.content);
  }
}
```

Typed errors and events. Nothing throws; errors come back with a named code:

```ts
import { ERROR_CODES } from "@aimask/sdk";

const result = await session.chat(/* ... */);
if (!result.ok && result.error.code === ERROR_CODES.BUDGET_EXCEEDED) {
  // the per-origin allowance is exhausted
}

aimask.on("budgetlow", () => {}); // 80% of the origin budget reached
aimask.on("disconnect", () => {}); // session revoked or extension locked
aimask.on("modelchanged", (data) => {}); // resolved model changed
```

## Error codes

| Code | Name | Meaning |
| --- | --- | --- |
| 4001 | USER_REJECTED | User said no, or a stream was aborted. |
| 4100 | UNAUTHORIZED | No active session for this site. |
| 4200 | BUDGET_EXCEEDED | This site's allowance is used up. |
| 4290 | RATE_LIMITED | This site is asking too fast. |
| 4400 | CAPABILITY_UNAVAILABLE | No model can do what was asked. |
| 4900 | DISCONNECTED | Extension locked or model unreachable. |
| 5000 | PROVIDER_ERROR | The model's provider failed. |

## Links

- Source: https://github.com/gantryops/aimask
- Spec: https://github.com/gantryops/aimask/blob/main/SPEC.md
- Package: https://www.npmjs.com/package/@aimask/sdk
- Full reference for agents: https://aimask.dev/llms-full.txt
- Maker: https://gantryops.dev
