@lumavox/server 0.0.0-stage → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -2
  3. package/dist/client.d.ts +59 -0
  4. package/dist/client.js +52 -0
  5. package/dist/errors.d.ts +86 -0
  6. package/dist/errors.js +138 -0
  7. package/dist/gen/index.d.ts +1 -0
  8. package/dist/gen/index.js +2 -0
  9. package/dist/gen/types.gen.d.ts +6563 -0
  10. package/dist/gen/types.gen.js +2 -0
  11. package/dist/index.d.ts +23 -0
  12. package/dist/index.js +8 -0
  13. package/dist/paging.d.ts +34 -0
  14. package/dist/paging.js +41 -0
  15. package/dist/refuse.d.ts +11 -0
  16. package/dist/refuse.js +12 -0
  17. package/dist/resources/agents.d.ts +34 -0
  18. package/dist/resources/agents.js +33 -0
  19. package/dist/resources/assets.d.ts +59 -0
  20. package/dist/resources/assets.js +80 -0
  21. package/dist/resources/end-users.d.ts +73 -0
  22. package/dist/resources/end-users.js +139 -0
  23. package/dist/resources/ephemeral-keys.d.ts +65 -0
  24. package/dist/resources/ephemeral-keys.js +129 -0
  25. package/dist/resources/generations.d.ts +40 -0
  26. package/dist/resources/generations.js +55 -0
  27. package/dist/resources/knowledge.d.ts +58 -0
  28. package/dist/resources/knowledge.js +91 -0
  29. package/dist/resources/resource.d.ts +52 -0
  30. package/dist/resources/resource.js +68 -0
  31. package/dist/resources/speech.d.ts +80 -0
  32. package/dist/resources/speech.js +64 -0
  33. package/dist/resources/templates.d.ts +37 -0
  34. package/dist/resources/templates.js +50 -0
  35. package/dist/resources/usage.d.ts +15 -0
  36. package/dist/resources/usage.js +16 -0
  37. package/dist/resources/voices.d.ts +41 -0
  38. package/dist/resources/voices.js +53 -0
  39. package/dist/runtime.d.ts +22 -0
  40. package/dist/runtime.js +24 -0
  41. package/dist/stream.d.ts +6 -0
  42. package/dist/stream.js +37 -0
  43. package/dist/transport.d.ts +83 -0
  44. package/dist/transport.js +236 -0
  45. package/dist/upload.d.ts +37 -0
  46. package/dist/upload.js +34 -0
  47. package/dist/wait.d.ts +42 -0
  48. package/dist/wait.js +51 -0
  49. package/package.json +50 -4
@@ -0,0 +1,2 @@
1
+ // This file is auto-generated by @hey-api/openapi-ts
2
+ export {};
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `@lumavox/server`: the API from a customer's server, with an org API key.
3
+ * Bodies and answers are the wire's, snake_case, typed from the published
4
+ * OpenAPI document (`src/gen/`, generated); methods are camelCase, and the
5
+ * SDK's own options (`RequestOptions`) go in a last argument.
6
+ */
7
+ export { type Client, type ClientOptions, createClient, DEFAULT_BASE_URL, } from "./client.js";
8
+ export { AuthenticationError, ConflictError, ConnectionError, InsufficientCreditsError, NotFoundError, PermissionError, PlatformError, type PlatformErrorInit, RateLimitError, ServerError, TimeoutError, ValidationError, } from "./errors.js";
9
+ export type * from "./gen/index.js";
10
+ export type { AgentKnowledge, AgentSettings, Agents, } from "./resources/agents.js";
11
+ export type { AssetListParams, Assets, AssetUploadFields, FolderListParams, Folders, } from "./resources/assets.js";
12
+ export type { EndUserCreated, EndUserExport, EndUserListParams, EndUsers, } from "./resources/end-users.js";
13
+ export type { EphemeralKeyCreateOptions, EphemeralKeyListParams, EphemeralKeys, } from "./resources/ephemeral-keys.js";
14
+ export type { GenerationListParams, Generations, Jobs, Skybox, } from "./resources/generations.js";
15
+ export type { Knowledge, KnowledgeInsightsParams, KnowledgeListParams, KnowledgeUploadFields, } from "./resources/knowledge.js";
16
+ export type { Resource, Waitable } from "./resources/resource.js";
17
+ export type { Speech, Stt, SttEvent, SttParams, Transcript, Tts, } from "./resources/speech.js";
18
+ export type { TemplateFile, Templates } from "./resources/templates.js";
19
+ export type { Usage, UsageParams } from "./resources/usage.js";
20
+ export type { Voices, VoiceUploadFields } from "./resources/voices.js";
21
+ export type { Fetch, RequestOptions } from "./transport.js";
22
+ export type { UploadData, UploadOptions } from "./upload.js";
23
+ export type { Job } from "./wait.js";
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * `@lumavox/server`: the API from a customer's server, with an org API key.
3
+ * Bodies and answers are the wire's, snake_case, typed from the published
4
+ * OpenAPI document (`src/gen/`, generated); methods are camelCase, and the
5
+ * SDK's own options (`RequestOptions`) go in a last argument.
6
+ */
7
+ export { createClient, DEFAULT_BASE_URL, } from "./client.js";
8
+ export { AuthenticationError, ConflictError, ConnectionError, InsufficientCreditsError, NotFoundError, PermissionError, PlatformError, RateLimitError, ServerError, TimeoutError, ValidationError, } from "./errors.js";
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Paging as the API pages: every `list()` is an async iterator over every
3
+ * item, page after page; `page()` beside it is one page, as the route
4
+ * answers it.
5
+ *
6
+ * The API has three styles:
7
+ * - **Offset** (end users, ephemeral keys, knowledge, and an end user's
8
+ * sessions inside the export): the next page starts where this one
9
+ * ended, and a page shorter than the limit, or the `total` reached, is
10
+ * the last. Newest first, so a row made while iterating shifts the rest
11
+ * by one and one item is seen twice — the cost of offsets, which the
12
+ * API chose.
13
+ * - **`next_cursor`** (generations, assets): the page says where the next
14
+ * starts, and the last has none. Nothing is seen twice.
15
+ * - **The backwards `next_before`** of a transcript, which only the
16
+ * export reads, by hand (`resources/end-users.ts`).
17
+ *
18
+ * A route that does not page (voices, agents, templates, a folder's
19
+ * level) has no iterator: its `list()` answers what the route does.
20
+ */
21
+ /** One page of an offset-paged list: its items, and the total when the route says. */
22
+ export interface OffsetPage<T> {
23
+ items: T[];
24
+ total?: number;
25
+ }
26
+ /** Every item, `limit` at a time from `offset`, until a short page or the total. */
27
+ export declare function byOffset<T>(read: (offset: number, limit: number) => Promise<OffsetPage<T>>, limit: number, offset?: number): AsyncGenerator<T, void, undefined>;
28
+ /** One page of a cursor-paged list: its items, and where the next one starts. */
29
+ export interface CursorPage<T> {
30
+ items: T[];
31
+ next?: string | null;
32
+ }
33
+ /** Every item, page after page, until a page names no next one. */
34
+ export declare function byCursor<T>(read: (cursor: string | undefined) => Promise<CursorPage<T>>): AsyncGenerator<T, void, undefined>;
package/dist/paging.js ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Paging as the API pages: every `list()` is an async iterator over every
3
+ * item, page after page; `page()` beside it is one page, as the route
4
+ * answers it.
5
+ *
6
+ * The API has three styles:
7
+ * - **Offset** (end users, ephemeral keys, knowledge, and an end user's
8
+ * sessions inside the export): the next page starts where this one
9
+ * ended, and a page shorter than the limit, or the `total` reached, is
10
+ * the last. Newest first, so a row made while iterating shifts the rest
11
+ * by one and one item is seen twice — the cost of offsets, which the
12
+ * API chose.
13
+ * - **`next_cursor`** (generations, assets): the page says where the next
14
+ * starts, and the last has none. Nothing is seen twice.
15
+ * - **The backwards `next_before`** of a transcript, which only the
16
+ * export reads, by hand (`resources/end-users.ts`).
17
+ *
18
+ * A route that does not page (voices, agents, templates, a folder's
19
+ * level) has no iterator: its `list()` answers what the route does.
20
+ */
21
+ /** Every item, `limit` at a time from `offset`, until a short page or the total. */
22
+ export async function* byOffset(read, limit, offset = 0) {
23
+ for (;;) {
24
+ const page = await read(offset, limit);
25
+ yield* page.items;
26
+ offset += page.items.length;
27
+ const last = page.items.length < limit ||
28
+ (page.total !== undefined && offset >= page.total);
29
+ if (last)
30
+ return;
31
+ }
32
+ }
33
+ /** Every item, page after page, until a page names no next one. */
34
+ export async function* byCursor(read) {
35
+ let cursor;
36
+ do {
37
+ const page = await read(cursor);
38
+ yield* page.items;
39
+ cursor = page.next ?? undefined;
40
+ } while (cursor);
41
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * What `@lumavox/server` resolves to under the `browser` and
3
+ * `react-native` export conditions (`package.json`; `runtime.ts` says
4
+ * why). It exports the refusal and nothing else, so a bundler that checks
5
+ * imports — esbuild, Rollup and so Vite — fails the build on
6
+ * `createClient` (esbuild only warns about a file with no exports at
7
+ * all); and it throws when it is loaded, for one that does not check
8
+ * (Metro). `package.json`'s `sideEffects` keeps it from being shaken out.
9
+ */
10
+ import { REFUSAL } from "./runtime.js";
11
+ export { REFUSAL };
package/dist/refuse.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * What `@lumavox/server` resolves to under the `browser` and
3
+ * `react-native` export conditions (`package.json`; `runtime.ts` says
4
+ * why). It exports the refusal and nothing else, so a bundler that checks
5
+ * imports — esbuild, Rollup and so Vite — fails the build on
6
+ * `createClient` (esbuild only warns about a file with no exports at
7
+ * all); and it throws when it is loaded, for one that does not check
8
+ * (Metro). `package.json`'s `sideEffects` keeps it from being shaken out.
9
+ */
10
+ import { REFUSAL } from "./runtime.js";
11
+ export { REFUSAL };
12
+ throw new Error(REFUSAL);
@@ -0,0 +1,34 @@
1
+ /**
2
+ * `client.agents`: the agents end users talk to — `/v1/agents*`. What one
3
+ * says, its voice, its hearing and the knowledge it may look up; the
4
+ * conversations themselves are the end users' (`endUsers.export`).
5
+ *
6
+ * - **A save reaches running sessions within a minute**, not at once: an
7
+ * agent's version travels through the platform's `STANDING` copy.
8
+ * - **`settings`** are the organisation's, for every agent;
9
+ * **`knowledge.set`** replaces the entries one agent may look up.
10
+ * - **`create` is a 409 at the plan's maximum agents**; `delete` answers
11
+ * the agent as it was, and `null` for one already gone.
12
+ */
13
+ import type { AgentCreateRequest, AgentInfo, AgentList, AgentSettingsInfo, AgentSettingsUpdate, AgentUpdate, KnowledgeAssigned, KnowledgeAssignment, KnowledgeList } from "../gen/index.js";
14
+ import type { RequestOptions, Transport } from "../transport.js";
15
+ import { Editable } from "./resource.js";
16
+ /** `agents.settings`: the organisation's, for every agent. */
17
+ export interface AgentSettings {
18
+ get(options?: RequestOptions): Promise<AgentSettingsInfo>;
19
+ update(body: AgentSettingsUpdate, options?: RequestOptions): Promise<AgentSettingsInfo>;
20
+ }
21
+ /** `agents.knowledge`: the entries one agent may look up. */
22
+ export interface AgentKnowledge {
23
+ list(agentId: string, options?: RequestOptions): Promise<KnowledgeList>;
24
+ /** Replaces them all with these. */
25
+ set(agentId: string, body: KnowledgeAssignment, options?: RequestOptions): Promise<KnowledgeAssigned>;
26
+ }
27
+ export declare class Agents extends Editable<AgentInfo, AgentUpdate> {
28
+ readonly settings: AgentSettings;
29
+ readonly knowledge: AgentKnowledge;
30
+ constructor(api: Transport, base: string);
31
+ /** Every agent. */
32
+ list(options?: RequestOptions): Promise<AgentList>;
33
+ create(body: AgentCreateRequest, options?: RequestOptions): Promise<AgentInfo>;
34
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `client.agents`: the agents end users talk to — `/v1/agents*`. What one
3
+ * says, its voice, its hearing and the knowledge it may look up; the
4
+ * conversations themselves are the end users' (`endUsers.export`).
5
+ *
6
+ * - **A save reaches running sessions within a minute**, not at once: an
7
+ * agent's version travels through the platform's `STANDING` copy.
8
+ * - **`settings`** are the organisation's, for every agent;
9
+ * **`knowledge.set`** replaces the entries one agent may look up.
10
+ * - **`create` is a 409 at the plan's maximum agents**; `delete` answers
11
+ * the agent as it was, and `null` for one already gone.
12
+ */
13
+ import { Editable } from "./resource.js";
14
+ export class Agents extends Editable {
15
+ constructor(api, base) {
16
+ super(api, base);
17
+ this.settings = {
18
+ get: (options) => this.send("GET", "/settings", options),
19
+ update: (body, options) => this.send("PUT", "/settings", options, body),
20
+ };
21
+ this.knowledge = {
22
+ list: (id, options) => this.send("GET", this.at(id, "/knowledge"), options),
23
+ set: (id, body, options) => this.send("PUT", this.at(id, "/knowledge"), options, body),
24
+ };
25
+ }
26
+ /** Every agent. */
27
+ list(options) {
28
+ return this.send("GET", "", options);
29
+ }
30
+ create(body, options) {
31
+ return this.send("POST", "", options, body);
32
+ }
33
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `client.assets`: this organisation's library — `/v1/assets*`, and its
3
+ * folders, `/v1/asset-folders*`, as `assets.folders`. Every generation's
4
+ * file lands here; uploads go here before a transcription or a video's
5
+ * frames can name them.
6
+ *
7
+ * - **An upload is converted after it arrives**: `confirm` and `upload`
8
+ * answer it `pending`, and `wait(id)` reads it until `ready` or
9
+ * `failed`. A transcription of one still `pending` is a 409.
10
+ * - **`upload(data, fields)`** is the ticket, the `PUT` and the confirm
11
+ * in one (`upload.ts`).
12
+ * - **`delete` of one already gone is done**: `null`. `deleteMany` and
13
+ * `move` answer how many they touched.
14
+ * - **`list()` pages by cursor**, 200 a page; a folder's `list()` is one
15
+ * level, as the route answers it.
16
+ */
17
+ import type { AssetBulkRequest, AssetDeleted, AssetDetail, AssetMoveRequest, AssetPage, AssetSummary, AssetsAffected, AssetUpdate, FolderCreate, FolderDeleted, FolderDetail, FolderLevel, FolderSummary, FolderUpdate, ListAssetsData, ListFoldersData, UploadConfirm, UploadTicket, UploadTicketRequest } from "../gen/index.js";
18
+ import type { RequestOptions, Transport } from "../transport.js";
19
+ import { type UploadData, type UploadOptions } from "../upload.js";
20
+ import { Editable, Waitable } from "./resource.js";
21
+ export type AssetListParams = NonNullable<ListAssetsData["query"]>;
22
+ export type FolderListParams = NonNullable<ListFoldersData["query"]>;
23
+ /** A file's place in the library: the confirm's fields, and its type. */
24
+ export type AssetUploadFields = Omit<UploadConfirm, "asset_id" | "content_type"> & {
25
+ /** A Blob's own type by default. */
26
+ content_type?: string;
27
+ };
28
+ /**
29
+ * `assets.folders`: the library's tree. `get` answers a folder with its
30
+ * counts and ancestors; `update` renames it or moves it under another;
31
+ * `delete` moves what it held up a level.
32
+ */
33
+ export declare class Folders extends Editable<FolderDetail, FolderUpdate, FolderSummary, FolderDeleted> {
34
+ /** One level: the top, or `parent_id`'s children. */
35
+ list(params?: FolderListParams, options?: RequestOptions): Promise<FolderLevel>;
36
+ create(body: FolderCreate, options?: RequestOptions): Promise<FolderSummary>;
37
+ }
38
+ /**
39
+ * `update` renames an asset or files it in a folder; `delete` deletes it
40
+ * and its files.
41
+ */
42
+ export declare class Assets extends Waitable<AssetDetail, AssetUpdate, AssetDetail, AssetDeleted> {
43
+ readonly folders: Folders;
44
+ constructor(api: Transport, base: string);
45
+ /** One page, with the `next_cursor` of the next. */
46
+ page(params?: AssetListParams, options?: RequestOptions): Promise<AssetPage>;
47
+ /** Every asset the filter matches, page after page (200 a page by default). */
48
+ list(params?: Omit<AssetListParams, "cursor">, options?: RequestOptions): AsyncGenerator<AssetSummary, void, undefined>;
49
+ /** A signed URL to `PUT` a file to, with exactly this type. */
50
+ uploadUrl(body: UploadTicketRequest, options?: RequestOptions): Promise<UploadTicket>;
51
+ /** Says the `PUT` is done: the asset, `pending` while it converts. */
52
+ confirm(body: UploadConfirm, options?: RequestOptions): Promise<AssetDetail>;
53
+ /** Uploads a file: `uploadUrl`, the `PUT`, `confirm`. */
54
+ upload(data: UploadData, fields?: AssetUploadFields, options?: UploadOptions): Promise<AssetDetail>;
55
+ /** Deletes several: how many were. */
56
+ deleteMany(body: AssetBulkRequest, options?: RequestOptions): Promise<AssetsAffected>;
57
+ /** Files several in one folder, `null` for the top: how many moved. */
58
+ move(body: AssetMoveRequest, options?: RequestOptions): Promise<AssetsAffected>;
59
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `client.assets`: this organisation's library — `/v1/assets*`, and its
3
+ * folders, `/v1/asset-folders*`, as `assets.folders`. Every generation's
4
+ * file lands here; uploads go here before a transcription or a video's
5
+ * frames can name them.
6
+ *
7
+ * - **An upload is converted after it arrives**: `confirm` and `upload`
8
+ * answer it `pending`, and `wait(id)` reads it until `ready` or
9
+ * `failed`. A transcription of one still `pending` is a 409.
10
+ * - **`upload(data, fields)`** is the ticket, the `PUT` and the confirm
11
+ * in one (`upload.ts`).
12
+ * - **`delete` of one already gone is done**: `null`. `deleteMany` and
13
+ * `move` answer how many they touched.
14
+ * - **`list()` pages by cursor**, 200 a page; a folder's `list()` is one
15
+ * level, as the route answers it.
16
+ */
17
+ import { byCursor } from "../paging.js";
18
+ import { needType, put, } from "../upload.js";
19
+ import { Editable, Waitable } from "./resource.js";
20
+ /** The route's own page bound. */
21
+ const PAGE = 200;
22
+ /**
23
+ * `assets.folders`: the library's tree. `get` answers a folder with its
24
+ * counts and ancestors; `update` renames it or moves it under another;
25
+ * `delete` moves what it held up a level.
26
+ */
27
+ export class Folders extends Editable {
28
+ /** One level: the top, or `parent_id`'s children. */
29
+ list(params = {}, options) {
30
+ return this.send("GET", "", options, params);
31
+ }
32
+ create(body, options) {
33
+ return this.send("POST", "", options, body);
34
+ }
35
+ }
36
+ /**
37
+ * `update` renames an asset or files it in a folder; `delete` deletes it
38
+ * and its files.
39
+ */
40
+ export class Assets extends Waitable {
41
+ constructor(api, base) {
42
+ super(api, base);
43
+ this.folders = new Folders(api, "/v1/asset-folders");
44
+ }
45
+ /** One page, with the `next_cursor` of the next. */
46
+ page(params = {}, options) {
47
+ return this.send("GET", "", options, params);
48
+ }
49
+ /** Every asset the filter matches, page after page (200 a page by default). */
50
+ list(params = {}, options) {
51
+ return byCursor(async (cursor) => {
52
+ const page = await this.page({ limit: PAGE, ...params, cursor }, options);
53
+ return { items: page.assets, next: page.next_cursor };
54
+ });
55
+ }
56
+ /** A signed URL to `PUT` a file to, with exactly this type. */
57
+ uploadUrl(body, options) {
58
+ return this.send("POST", "/upload-url", options, body);
59
+ }
60
+ /** Says the `PUT` is done: the asset, `pending` while it converts. */
61
+ confirm(body, options) {
62
+ return this.send("POST", "/uploaded", options, body);
63
+ }
64
+ /** Uploads a file: `uploadUrl`, the `PUT`, `confirm`. */
65
+ async upload(data, fields = {}, options = {}) {
66
+ const { content_type, ...rest } = fields;
67
+ const type = needType(data, content_type);
68
+ const ticket = await this.uploadUrl({ content_type: type }, options);
69
+ await put(this.api, ticket, data, type, options);
70
+ return this.settled(await this.confirm({ ...rest, asset_id: ticket.asset_id, content_type: type }, options), options);
71
+ }
72
+ /** Deletes several: how many were. */
73
+ deleteMany(body, options) {
74
+ return this.send("POST", "/delete", options, body);
75
+ }
76
+ /** Files several in one folder, `null` for the top: how many moved. */
77
+ move(body, options) {
78
+ return this.send("POST", "/move", options, body);
79
+ }
80
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `client.endUsers`: the people your agents talk to — `/v1/end-users*`,
3
+ * and everything about one of them in `export`.
4
+ *
5
+ * - **`create` with your own `external_id` is idempotent**: the same value
6
+ * is always the same person, made only the first time (200 found, 201
7
+ * made). So it is retried like a GET; without an `external_id` every call
8
+ * makes someone new, and only a 429 is retried.
9
+ * - **Either id works everywhere**: the `eu_…` the API made, or your
10
+ * `external_id`. Path parameters are encoded, so an id may hold `/`.
11
+ * - **`update` renames them, or changes your id for them**: absent fields
12
+ * are untouched, `null` clears one; 409 when another person has that
13
+ * `external_id`.
14
+ * - **`delete` is what to call when someone asks to be forgotten**: it
15
+ * deletes them, every conversation they had and everything remembered
16
+ * about them, and answers the counts. Someone already gone is done, not
17
+ * an error: `null`.
18
+ *
19
+ * **`export`** reads what the API holds about one person with a key of
20
+ * their own, since their conversations and facts are their end user DO's
21
+ * and an API key cannot read them (`/v1/agent/*` takes only an ephemeral
22
+ * key): it mints a five-minute key for them, lists their sessions (200 a
23
+ * page), reads each transcript backwards (200 messages a page, four
24
+ * sessions at a time), reads their facts, and revokes the key in
25
+ * `finally` — after an abort or a failure too. The cost: one mint (and
26
+ * `limits()` when cold) + ⌈S/200⌉ lists + Σ⌈M/200⌉ transcript pages + one
27
+ * facts read + one revoke. **Its ceilings**: the key is ordinary, so at
28
+ * the person's live-key cap or the plan's mint rate the export fails as
29
+ * the mint does (422, 429) and can be retried once a key lapses; it lives
30
+ * five minutes, so a history that takes longer to read fails with a 401;
31
+ * its one micro-credit is set aside like any key's, so an organisation
32
+ * with no balance cannot export (402); and the facts route answers the
33
+ * first 100 until it pages (left-overs #109).
34
+ */
35
+ import type { EndUserCreateRequest, EndUserDeleted, EndUserInfo, EndUserList, EndUserUpdateRequest, FactInfo, ListEndUsersData, SessionDetail } from "../gen/index.js";
36
+ import { type RequestOptions, type Transport } from "../transport.js";
37
+ import type { EphemeralKeys } from "./ephemeral-keys.js";
38
+ import { Editable } from "./resource.js";
39
+ export type EndUserListParams = NonNullable<ListEndUsersData["query"]>;
40
+ /** An end user `create` answered, and whether this call made them. */
41
+ export type EndUserCreated = EndUserInfo & {
42
+ /**
43
+ * `true` on a 201, `false` when the `external_id` already named them.
44
+ * The last attempt's: after a retried connection failure, a person
45
+ * the first attempt made reads as found.
46
+ */
47
+ created: boolean;
48
+ };
49
+ /** Everything the API holds about one person. */
50
+ export interface EndUserExport {
51
+ /** Their `eu_…` id. */
52
+ end_user_id: string;
53
+ /** Every conversation, newest first, each with every message, oldest first. */
54
+ sessions: SessionDetail[];
55
+ /** What is remembered about them, oldest first — the first 100 for now. */
56
+ facts: FactInfo[];
57
+ }
58
+ export declare class EndUsers extends Editable<EndUserInfo, EndUserUpdateRequest, EndUserInfo, EndUserDeleted> {
59
+ private readonly keys;
60
+ constructor(api: Transport, base: string, keys: EphemeralKeys);
61
+ /**
62
+ * Makes a person, or finds the one your `external_id` already names.
63
+ * 409 when the plan's maximum end users is reached (a repeat of an
64
+ * existing `external_id` is answered as ever).
65
+ */
66
+ create(body?: EndUserCreateRequest, options?: RequestOptions): Promise<EndUserCreated>;
67
+ /** One page, newest first, with the `total` the filter matches. */
68
+ page(params?: EndUserListParams, options?: RequestOptions): Promise<EndUserList>;
69
+ /** Every person, newest first, page after page (`limit` a page, 200 by default). */
70
+ list(params?: Omit<EndUserListParams, "offset">, options?: RequestOptions): AsyncGenerator<EndUserInfo, void, undefined>;
71
+ /** Everything about one person, read with a key of their own (see the header). */
72
+ export(id: string, options?: RequestOptions): Promise<EndUserExport>;
73
+ }
@@ -0,0 +1,139 @@
1
+ /**
2
+ * `client.endUsers`: the people your agents talk to — `/v1/end-users*`,
3
+ * and everything about one of them in `export`.
4
+ *
5
+ * - **`create` with your own `external_id` is idempotent**: the same value
6
+ * is always the same person, made only the first time (200 found, 201
7
+ * made). So it is retried like a GET; without an `external_id` every call
8
+ * makes someone new, and only a 429 is retried.
9
+ * - **Either id works everywhere**: the `eu_…` the API made, or your
10
+ * `external_id`. Path parameters are encoded, so an id may hold `/`.
11
+ * - **`update` renames them, or changes your id for them**: absent fields
12
+ * are untouched, `null` clears one; 409 when another person has that
13
+ * `external_id`.
14
+ * - **`delete` is what to call when someone asks to be forgotten**: it
15
+ * deletes them, every conversation they had and everything remembered
16
+ * about them, and answers the counts. Someone already gone is done, not
17
+ * an error: `null`.
18
+ *
19
+ * **`export`** reads what the API holds about one person with a key of
20
+ * their own, since their conversations and facts are their end user DO's
21
+ * and an API key cannot read them (`/v1/agent/*` takes only an ephemeral
22
+ * key): it mints a five-minute key for them, lists their sessions (200 a
23
+ * page), reads each transcript backwards (200 messages a page, four
24
+ * sessions at a time), reads their facts, and revokes the key in
25
+ * `finally` — after an abort or a failure too. The cost: one mint (and
26
+ * `limits()` when cold) + ⌈S/200⌉ lists + Σ⌈M/200⌉ transcript pages + one
27
+ * facts read + one revoke. **Its ceilings**: the key is ordinary, so at
28
+ * the person's live-key cap or the plan's mint rate the export fails as
29
+ * the mint does (422, 429) and can be retried once a key lapses; it lives
30
+ * five minutes, so a history that takes longer to read fails with a 401;
31
+ * its one micro-credit is set aside like any key's, so an organisation
32
+ * with no balance cannot export (402); and the facts route answers the
33
+ * first 100 until it pages (left-overs #109).
34
+ */
35
+ import { byOffset } from "../paging.js";
36
+ import { segment, } from "../transport.js";
37
+ import { Editable } from "./resource.js";
38
+ /** The routes' own page bound, for end users, sessions and transcripts alike. */
39
+ const PAGE = 200;
40
+ /** The export key's lifetime and limit: the shortest useful, the least set aside. */
41
+ const EXPORT_TTL_S = 300;
42
+ const EXPORT_CREDIT = "0.000001";
43
+ /** Transcripts read at once. */
44
+ const EXPORT_PARALLEL = 4;
45
+ /** `work` over every item, `width` at a time, answers in the items' order. */
46
+ async function inParallel(items, width, work) {
47
+ const out = new Array(items.length);
48
+ let next = 0;
49
+ const lane = async () => {
50
+ while (next < items.length) {
51
+ const i = next++;
52
+ out[i] = await work(items[i]);
53
+ }
54
+ };
55
+ await Promise.all(Array.from({ length: Math.min(width, items.length) }, lane));
56
+ return out;
57
+ }
58
+ export class EndUsers extends Editable {
59
+ constructor(api, base, keys) {
60
+ super(api, base);
61
+ this.keys = keys;
62
+ }
63
+ /**
64
+ * Makes a person, or finds the one your `external_id` already names.
65
+ * 409 when the plan's maximum end users is reached (a repeat of an
66
+ * existing `external_id` is answered as ever).
67
+ */
68
+ async create(body = {}, options) {
69
+ const answer = await this.api.request({
70
+ method: "POST",
71
+ path: this.base,
72
+ body,
73
+ // A string or nothing, by its type: so a non-empty one.
74
+ idempotent: !!body.external_id,
75
+ }, options);
76
+ return { ...answer.data, created: answer.status === 201 };
77
+ }
78
+ /** One page, newest first, with the `total` the filter matches. */
79
+ page(params = {}, options) {
80
+ return this.send("GET", "", options, params);
81
+ }
82
+ /** Every person, newest first, page after page (`limit` a page, 200 by default). */
83
+ list(params = {}, options) {
84
+ return byOffset(async (offset, limit) => {
85
+ const page = await this.page({ ...params, offset, limit }, options);
86
+ return { items: page.end_users, total: page.total };
87
+ }, params.limit ?? PAGE);
88
+ }
89
+ /** Everything about one person, read with a key of their own (see the header). */
90
+ async export(id, options = {}) {
91
+ const key = await this.keys.create({
92
+ end_user_id: id,
93
+ credit_limit: EXPORT_CREDIT,
94
+ expires_in_seconds: EXPORT_TTL_S,
95
+ label: "export",
96
+ }, options);
97
+ try {
98
+ // Every read as the person: the agent routes take nothing else.
99
+ const read = (path, query) => this.api
100
+ .request({ method: "GET", path, query, token: key.key }, options)
101
+ .then((answer) => answer.data);
102
+ const listed = [];
103
+ for await (const session of byOffset(async (offset, limit) => ({
104
+ items: await read("/v1/agent/sessions", {
105
+ offset,
106
+ limit,
107
+ }),
108
+ }), PAGE)) {
109
+ listed.push(session);
110
+ }
111
+ const sessions = await inParallel(listed, EXPORT_PARALLEL, async (s) => {
112
+ const path = `/v1/agent/sessions/${segment(s.id)}`;
113
+ const page = (before) => read(path, { limit: PAGE, before });
114
+ const newest = await page();
115
+ const pages = [newest.messages];
116
+ for (let before = newest.next_before; typeof before === "number";) {
117
+ const older = await page(before);
118
+ pages.unshift(older.messages);
119
+ before = older.next_before;
120
+ }
121
+ return { ...newest, messages: pages.flat(), next_before: null };
122
+ });
123
+ const facts = await read("/v1/agent/facts");
124
+ return {
125
+ end_user_id: key.end_user_id ?? facts.end_user_id,
126
+ sessions,
127
+ facts: facts.facts,
128
+ };
129
+ }
130
+ finally {
131
+ // Without the caller's signal: an aborted export still revokes.
132
+ const { signal: _, ...revoking } = options;
133
+ await this.keys.revoke(key.id, revoking).catch(() => {
134
+ // swallowed: the key lapses by itself within its five minutes, and
135
+ // the export's own answer or error is the one the caller needs.
136
+ });
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * `client.ephemeralKeys`: the keys an end user's device talks to agents
3
+ * with — `/v1/ephemeral-keys*` and `/v1/token`.
4
+ *
5
+ * - **A key is prepaid**: its `credit_limit` is set aside from the balance
6
+ * when it is minted (402 when it cannot be), and what it does not spend
7
+ * comes back when it settles.
8
+ * - **`create` clamps the lifetime by default** to the platform's maximum,
9
+ * read from `limits()` — which the API never does: past a cap is a 422
10
+ * (`apps/api/src/org/minting.ts`). The plan's own caps for one end
11
+ * user's key are not reported yet (left-overs #107), so a key within the
12
+ * platform's can still be refused.
13
+ * - **`limits()` is cached five minutes per client**, so a mint is two
14
+ * requests cold and one warm. Concurrent cold callers share one read.
15
+ * - **`revoke` answers at once**, with the figures from before the key's
16
+ * end user DO settles: revocation leaves through the org DO's outbox
17
+ * (`apps/api/src/org/areas/keys.ts` `revokeEphemeral`). Waiting for the
18
+ * final figures needs the key's read route (left-overs #106).
19
+ * - **`inspect` reads a key as itself** (`GET /v1/token` with that key).
20
+ * Its `credits_spent` moves only when the key settles — at the end, or
21
+ * daily — so a live key reads as unspent (left-overs #105).
22
+ */
23
+ import type { EphemeralKeyCreateRequest, EphemeralKeyCreateResponse, EphemeralKeyInfo, EphemeralKeyLimits, EphemeralTokenSelf, ListEphemeralKeysData } from "../gen/index.js";
24
+ import { type RequestOptions } from "../transport.js";
25
+ import { Resource } from "./resource.js";
26
+ export type EphemeralKeyListParams = NonNullable<ListEphemeralKeysData["query"]>;
27
+ export interface EphemeralKeyCreateOptions extends RequestOptions {
28
+ /**
29
+ * Cut `expires_in_seconds` to the platform's maximum instead of being
30
+ * refused for it, and send the default when it is absent. On by default;
31
+ * `false` sends the body as written, in one request.
32
+ */
33
+ clampTtl?: boolean;
34
+ }
35
+ export declare class EphemeralKeys extends Resource {
36
+ private cached;
37
+ /**
38
+ * What a key may be minted with: the **platform's** longest lifetime and
39
+ * largest credit limit, and the scopes this API key may grant. Cached
40
+ * five minutes per client.
41
+ */
42
+ limits(options?: RequestOptions): Promise<EphemeralKeyLimits>;
43
+ /**
44
+ * Mints a key for one end user — returned **once**, as `key`. Scopes
45
+ * are exactly `["agent"]` (the default). 402 when the balance cannot set
46
+ * `credit_limit` aside, 422 past a cap, 429 past the plan's mints a
47
+ * minute (retried after its `Retry-After`).
48
+ */
49
+ create(body: EphemeralKeyCreateRequest, options?: EphemeralKeyCreateOptions): Promise<EphemeralKeyCreateResponse>;
50
+ /**
51
+ * Revokes a key: it is refused from now on, and what it did not spend
52
+ * comes back when it settles. `null` when there is no such key — a
53
+ * revoked, expired or unknown id answers as itself, a non-UUID as none.
54
+ */
55
+ revoke(id: string, options?: RequestOptions): Promise<EphemeralKeyInfo | null>;
56
+ /**
57
+ * A key as it reads itself: its scopes, limit, spend and expiry. `null`
58
+ * when the key is refused — expired, revoked, spent or not a key.
59
+ */
60
+ inspect(key: string, options?: RequestOptions): Promise<EphemeralTokenSelf | null>;
61
+ /** One page of this organisation's keys: live ones unless `live_only` is `false`. */
62
+ page(params?: EphemeralKeyListParams, options?: RequestOptions): Promise<EphemeralKeyInfo[]>;
63
+ /** Every key, page after page (`limit` a page, 500 by default). */
64
+ list(params?: Omit<EphemeralKeyListParams, "offset">, options?: RequestOptions): AsyncGenerator<EphemeralKeyInfo, void, undefined>;
65
+ }