# @harrisx/asktheo

Partner SDK for AskTheo. Your backend authenticates itself once with
`client_credentials`, then asks AskTheo for a short-lived **delegated session** for a user
you have already authenticated. Your users never see a WorkOS or AskTheo login screen, and
there is no OAuth redirect anywhere in the flow.

## Install

Add to your project's `.npmrc`:

```
@harrisx:registry=https://sdk.justasktheo.com/npm/
```

```bash
npm install @harrisx/asktheo
```

Full guide: [INTEGRATION.md](./INTEGRATION.md).

## Two calls

```ts
import { AskTheo } from "@harrisx/asktheo";

const asktheo = new AskTheo({
  partner: {
    clientId: process.env.PARTNER_M2M_CLIENT_ID!,
    clientSecret: process.env.PARTNER_M2M_CLIENT_SECRET!,   // server-side only
  },
});
```

**1. Register a user, once.** No invitation is sent and no password is set. Idempotent.
The email must be on a domain your organization has verified with us.

```ts
await asktheo.registerUser({
  subject: user.id,                 // your stable, immutable user id
  email: user.email,
  name: user.name,
  workspaceId: user.workspaceId,
});
```

**2. Delegate a session, per sign-in.** Returns a token scoped to that user. A subject you
have not registered is rejected with `user_not_registered`.

```ts
const session = await asktheo.completeAuthentication({
  subject: user.id,
  email: user.email,
  workspaceId: user.workspaceId,
  authenticatedAt: user.authenticatedAt,   // unix seconds, within the last 5 minutes
});
// session.accessToken, session.expiresIn (900s), session.tenant
```

Hand **only** `session.accessToken` to the browser. Keep `clientSecret` on your server.

## Asking questions

```ts
const stream = await asktheo.ask({ question: "What are the top Q3 trends?" });
for await (const chunk of stream) {
  if (chunk.text) process.stdout.write(chunk.text);
}
```

Or collect it all: `const answer = await stream.result();`

### Report generation

Pass `reportSettings` to switch the agent into report mode:

```ts
const stream = await asktheo.ask({
  question: "Build the Q3 deck",
  reportSettings: {
    output_format: "powerpoint",   // "powerpoint" | "charts_only" | "interactive"
    verbosity: "short",            // "short" | "medium" | "heavy"
    style: "briefing",
    color_palette: "corporate",
    inline_preview: true,
  },
});
```

### In the browser

Expose one authenticated route on your server that returns the delegated token, then drop in
the component. Partner credentials stay on your server.

**React** — verified on React 18.3 and 19.3 (`react` is an optional peer, `>=18`):

```tsx
import { AskTheoAsk } from "@harrisx/asktheo/react";

<AskTheoAsk />                       {/* paths discovered; override with backend="…" */}
```

Headless instead — `useAskTheo({ backend })` returns
`{ ask, answer, chunks, isStreaming, sessionId, error, abort, reset }`.

**Any other frontend** — a custom element, no build step:

```html
<script type="module" src="/static/asktheo-element.js"></script>
<asktheo-ask backend="/internal/asktheo"></asktheo-ask>
```

**Programmatic** — `AskTheoClient` from `@harrisx/asktheo/browser`, with a `tokenProvider`
pointing at your route.

The `/react`, `/browser` and `/element` entries contain no partner functions at all, so a
`clientSecret` cannot reach a browser bundle by accident.

In Python, `AskTheoComponent` serves the other half: `component.token(...)` for your route and
`component.embed_html(...)` for the markup.

`authenticateAs(...)` is a server-side shortcut that delegates **and** adopts the token on
the same client, so it can call `ask()` immediately as that user.

## Removing access

```ts
await asktheo.deactivateUser({ subject: user.id, workspaceId: user.workspaceId });
```

Deactivates the membership and revokes sessions. Tokens already issued stay valid until they
expire, which is why the lifetime is short.

## Python

The same lifecycle is available server-side in Python — see
[INTEGRATION.md](./INTEGRATION.md#python).

## Errors

Every failure returns `{ code, message }`. Do not retry `400`/`401`/`403`/`409`/`422`.
Retry `429` per `Retry-After`, and use bounded backoff for `502`/`503`. The full table is in
[INTEGRATION.md](./INTEGRATION.md#failure-contract).

## License

UNLICENSED — distributed to HarrisX partners under agreement.
