@lumavox/server 0.5.0 → 0.8.0

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.
package/dist/index.d.ts CHANGED
@@ -8,15 +8,16 @@ export { type Client, type ClientOptions, createClient, DEFAULT_BASE_URL, } from
8
8
  export { AuthenticationError, ConflictError, ConnectionError, InsufficientCreditsError, NotFoundError, PermissionError, PlatformError, type PlatformErrorCode, type PlatformErrorInit, RateLimitError, ServerError, TimeoutError, ValidationError, } from "./errors.js";
9
9
  export type * from "./gen/index.js";
10
10
  export type { Page } from "./paging.js";
11
- export type { AgentKnowledge, AgentSettings, Agents, } from "./resources/agents.js";
11
+ export type { AgentKnowledge, AgentSettings, Agents, AgentTools, } from "./resources/agents.js";
12
12
  export type { AssetListParams, Assets, AssetUploadFields, FolderListParams, Folders, } from "./resources/assets.js";
13
13
  export type { EndUserCreated, EndUserExport, EndUserListParams, EndUsers, } from "./resources/end-users.js";
14
- export type { EphemeralKeyCreateOptions, EphemeralKeyListParams, EphemeralKeys, } from "./resources/ephemeral-keys.js";
14
+ export type { EphemeralKeyCreateOptions, EphemeralKeyListParams, EphemeralKeyRevokeOptions, EphemeralKeys, } from "./resources/ephemeral-keys.js";
15
15
  export type { GenerationListParams, Generations, Jobs, Skybox, } from "./resources/generations.js";
16
16
  export type { Knowledge, KnowledgeInsightsParams, KnowledgeListParams, KnowledgeUploadFields, } from "./resources/knowledge.js";
17
- export type { Resource, Waitable } from "./resources/resource.js";
17
+ export type { Library, Listing, Resource, Waitable, WaitableLibrary, } from "./resources/resource.js";
18
18
  export type { Speech, Stt, SttEvent, SttParams, Transcript, Tts, } from "./resources/speech.js";
19
19
  export type { TemplateFile, Templates } from "./resources/templates.js";
20
+ export type { Tools } from "./resources/tools.js";
20
21
  export type { Usage, UsageParams } from "./resources/usage.js";
21
22
  export type { Voices, VoiceUploadFields } from "./resources/voices.js";
22
23
  export type { Fetch, RequestOptions } from "./transport.js";
@@ -6,15 +6,16 @@
6
6
  * - **A save reaches running sessions within a minute**, not at once: an
7
7
  * agent's version travels through the platform's `STANDING` copy.
8
8
  * - **`settings`** are the organisation's, for every agent;
9
- * **`knowledge.set`** replaces the entries one agent may look up.
9
+ * **`knowledge.set`** replaces the entries one agent may look up, and
10
+ * **`tools.set`** the tools it can call.
10
11
  * - **`create` is a 409 at the plan's maximum agents**; `delete` answers
11
12
  * `true`, and `false` for one already gone.
12
13
  * - **`list()` reads one page**: an organisation's agents are few, and the
13
14
  * route answers them all with `next_cursor` `null` (`paging.ts`).
14
15
  */
15
- import type { AgentCreateRequest, AgentInfo, AgentKnowledgeUpdate, AgentPage, AgentSettingsInfo, AgentSettingsUpdate, AgentSummary, AgentUpdate, KnowledgeAssigned, KnowledgeInfo } from "../gen/index.js";
16
+ import type { AgentCreateRequest, AgentInfo, AgentKnowledgeUpdate, AgentPage, AgentSettingsInfo, AgentSettingsUpdate, AgentSummary, AgentToolsUpdate, AgentUpdate, KnowledgeAssigned, KnowledgeInfo, ToolInfo, ToolsAssigned } from "../gen/index.js";
16
17
  import type { RequestOptions, Transport } from "../transport.js";
17
- import { Editable } from "./resource.js";
18
+ import { Library } from "./resource.js";
18
19
  /** `agents.settings`: the organisation's, for every agent. */
19
20
  export interface AgentSettings {
20
21
  get(options?: RequestOptions): Promise<AgentSettingsInfo>;
@@ -27,13 +28,18 @@ export interface AgentKnowledge {
27
28
  /** Replaces them all with these. */
28
29
  set(agentId: string, body: AgentKnowledgeUpdate, options?: RequestOptions): Promise<KnowledgeAssigned>;
29
30
  }
30
- export declare class Agents extends Editable<AgentInfo, AgentUpdate> {
31
+ /** `agents.tools`: the tools one agent can call. */
32
+ export interface AgentTools {
33
+ /** Every tool it can call. */
34
+ list(agentId: string, options?: RequestOptions): AsyncGenerator<ToolInfo, void, undefined>;
35
+ /** Replaces them all with these, 20 at most. */
36
+ set(agentId: string, body: AgentToolsUpdate, options?: RequestOptions): Promise<ToolsAssigned>;
37
+ }
38
+ export declare class Agents extends Library<AgentInfo, AgentUpdate, AgentSummary, AgentPage, AgentCreateRequest> {
31
39
  readonly settings: AgentSettings;
32
40
  readonly knowledge: AgentKnowledge;
41
+ readonly tools: AgentTools;
33
42
  constructor(api: Transport, base: string);
34
- /** The page of agents, as the route answers it. */
35
- page(options?: RequestOptions): Promise<AgentPage>;
36
- /** Every agent. */
37
- list(options?: RequestOptions): AsyncGenerator<AgentSummary, void, undefined>;
38
- create(body: AgentCreateRequest, options?: RequestOptions): Promise<AgentInfo>;
43
+ /** An agent's links to what the organisation has: its knowledge, its tools. */
44
+ protected links<T extends AgentKnowledge | AgentTools>(rest: string): T;
39
45
  }
@@ -6,34 +6,29 @@
6
6
  * - **A save reaches running sessions within a minute**, not at once: an
7
7
  * agent's version travels through the platform's `STANDING` copy.
8
8
  * - **`settings`** are the organisation's, for every agent;
9
- * **`knowledge.set`** replaces the entries one agent may look up.
9
+ * **`knowledge.set`** replaces the entries one agent may look up, and
10
+ * **`tools.set`** the tools it can call.
10
11
  * - **`create` is a 409 at the plan's maximum agents**; `delete` answers
11
12
  * `true`, and `false` for one already gone.
12
13
  * - **`list()` reads one page**: an organisation's agents are few, and the
13
14
  * route answers them all with `next_cursor` `null` (`paging.ts`).
14
15
  */
15
- import { Editable } from "./resource.js";
16
- export class Agents extends Editable {
16
+ import { Library } from "./resource.js";
17
+ export class Agents extends Library {
17
18
  constructor(api, base) {
18
19
  super(api, base);
19
20
  this.settings = {
20
21
  get: (options) => this.send("GET", "/settings", options),
21
22
  update: (body, options) => this.send("PUT", "/settings", options, body),
22
23
  };
23
- this.knowledge = {
24
- list: (id, options) => this.each(this.at(id, "/knowledge"), {}, options),
25
- set: (id, body, options) => this.send("PUT", this.at(id, "/knowledge"), options, body),
26
- };
27
- }
28
- /** The page of agents, as the route answers it. */
29
- page(options) {
30
- return this.send("GET", "", options);
24
+ this.knowledge = this.links("/knowledge");
25
+ this.tools = this.links("/tools");
31
26
  }
32
- /** Every agent. */
33
- list(options) {
34
- return this.each("", {}, options);
35
- }
36
- create(body, options) {
37
- return this.send("POST", "", options, body);
27
+ /** An agent's links to what the organisation has: its knowledge, its tools. */
28
+ links(rest) {
29
+ return {
30
+ list: (id, options) => this.each(this.at(id, rest), {}, options),
31
+ set: (id, body, options) => this.send("PUT", this.at(id, rest), options, body),
32
+ };
38
33
  }
39
34
  }
@@ -21,18 +21,15 @@
21
21
  * and an API key cannot read them (`/v1/agent/*` takes only an ephemeral
22
22
  * key): it mints a five-minute key for them, lists their sessions (200 a
23
23
  * page), reads each transcript back to its start by `messages.next_cursor`
24
- * (200 messages a page, four sessions at a time), reads their facts, and
25
- * revokes the key in `finally` — after an abort or a failure too. The
26
- * cost: one mint (and `limits()` when cold) + ⌈S/200⌉ lists + Σ⌈M/200⌉
27
- * transcript pages + one facts read + one revoke. **Its ceilings**: the
24
+ * (200 messages a page, four sessions at a time), reads their facts (200
25
+ * a page), and revokes the key in `finally` — after an abort or a failure
26
+ * too. The cost: one mint (and `limits()` when cold) + ⌈S/200⌉ lists +
27
+ * Σ⌈M/200⌉ transcript pages + ⌈F/200⌉ fact pages + one revoke. **Its ceilings**: the
28
28
  * key is ordinary, so at the person's live-key cap or the plan's mint rate
29
29
  * the export fails as the mint does (422, 429) and can be retried once a
30
30
  * key lapses; it lives five minutes, so a history that takes longer to
31
31
  * read fails with a 401; its one micro-credit is set aside like any key's,
32
- * so an organisation with no balance cannot export (402); and the facts
33
- * route answers the newest 100 in one page until it pages (left-overs
34
- * #109) — read here by its cursor, so the day it does, the export has
35
- * them all.
32
+ * so an organisation with no balance cannot export (402).
36
33
  */
37
34
  import type { EndUserCreateRequest, EndUserDeleted, EndUserInfo, EndUserPage, EndUserUpdateRequest, FactInfo, ListEndUsersData, SessionDetail } from "../gen/index.js";
38
35
  import { type RequestOptions, type Transport } from "../transport.js";
@@ -21,18 +21,15 @@
21
21
  * and an API key cannot read them (`/v1/agent/*` takes only an ephemeral
22
22
  * key): it mints a five-minute key for them, lists their sessions (200 a
23
23
  * page), reads each transcript back to its start by `messages.next_cursor`
24
- * (200 messages a page, four sessions at a time), reads their facts, and
25
- * revokes the key in `finally` — after an abort or a failure too. The
26
- * cost: one mint (and `limits()` when cold) + ⌈S/200⌉ lists + Σ⌈M/200⌉
27
- * transcript pages + one facts read + one revoke. **Its ceilings**: the
24
+ * (200 messages a page, four sessions at a time), reads their facts (200
25
+ * a page), and revokes the key in `finally` — after an abort or a failure
26
+ * too. The cost: one mint (and `limits()` when cold) + ⌈S/200⌉ lists +
27
+ * Σ⌈M/200⌉ transcript pages + ⌈F/200⌉ fact pages + one revoke. **Its ceilings**: the
28
28
  * key is ordinary, so at the person's live-key cap or the plan's mint rate
29
29
  * the export fails as the mint does (422, 429) and can be retried once a
30
30
  * key lapses; it lives five minutes, so a history that takes longer to
31
31
  * read fails with a 401; its one micro-credit is set aside like any key's,
32
- * so an organisation with no balance cannot export (402); and the facts
33
- * route answers the newest 100 in one page until it pages (left-overs
34
- * #109) — read here by its cursor, so the day it does, the export has
35
- * them all.
32
+ * so an organisation with no balance cannot export (402).
36
33
  */
37
34
  import { pages } from "../paging.js";
38
35
  import { segment, } from "../transport.js";
@@ -128,7 +125,7 @@ export class EndUsers extends Editable {
128
125
  messages: { data: held.flat(), next_cursor: null },
129
126
  };
130
127
  });
131
- const facts = await all(pages((cursor) => read("/v1/agent/facts", { cursor })));
128
+ const facts = await all(pages((cursor) => read("/v1/agent/facts", { limit: PAGE, cursor })));
132
129
  return { end_user_id: key.end_user_id ?? id, sessions, facts };
133
130
  }
134
131
  finally {
@@ -5,24 +5,24 @@
5
5
  * - **A key is prepaid**: its `credit_limit_micros` is set aside from the
6
6
  * balance when it is minted (402 when it cannot be), and what it does not spend
7
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.
8
+ * - **`create` clamps the lifetime by default** to the tighter of the
9
+ * platform's maximum and the plan's for one end user's key, both read
10
+ * from `limits()` — which the API never does: past a cap is a 422
11
+ * (`apps/api/src/org/minting.ts`).
13
12
  * - **`limits()` is cached five minutes per client**, so a mint is two
14
13
  * requests cold and one warm. Concurrent cold callers share one read.
15
14
  * - **`revoke` answers at once**, with the figures from before the key's
16
15
  * 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_micros` moves only when the key settles — at the end, or
21
- * daily — so a live key reads as unspent (left-overs #105).
16
+ * (`apps/api/src/org/areas/keys.ts` `revokeEphemeral`). With
17
+ * `{ settled: true }` it reads the key (`get`) until `settled_at` is set
18
+ * — seconds, longer while a memory job the key paid for runs — on the
19
+ * polls of `../wait.ts`; a `signal` is its deadline.
20
+ * - **`inspect` reads a key as itself** (`GET /v1/token` with that key):
21
+ * its spend as its end user DO counts it, turn by turn.
22
22
  */
23
23
  import type { EphemeralKeyCreateRequest, EphemeralKeyCreateResponse, EphemeralKeyInfo, EphemeralKeyLimits, EphemeralKeyPage, EphemeralTokenSelf, ListEphemeralKeysData } from "../gen/index.js";
24
24
  import { type RequestOptions } from "../transport.js";
25
- import { Resource } from "./resource.js";
25
+ import { Readable } from "./resource.js";
26
26
  export type EphemeralKeyListParams = NonNullable<ListEphemeralKeysData["query"]>;
27
27
  export interface EphemeralKeyCreateOptions extends RequestOptions {
28
28
  /**
@@ -32,12 +32,17 @@ export interface EphemeralKeyCreateOptions extends RequestOptions {
32
32
  */
33
33
  clampTtl?: boolean;
34
34
  }
35
- export declare class EphemeralKeys extends Resource {
35
+ export interface EphemeralKeyRevokeOptions extends RequestOptions {
36
+ /** Answer once the key's final figures are in (`settled_at` set), not at once. */
37
+ settled?: boolean;
38
+ }
39
+ export declare class EphemeralKeys extends Readable<EphemeralKeyInfo> {
36
40
  private cached;
37
41
  /**
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.
42
+ * What a key may be minted with: the platform's longest lifetime and
43
+ * largest credit limit, the plan's for one end user's key (`plan_*`,
44
+ * `null` where it sets none), and the scopes this API key may grant.
45
+ * Cached five minutes per client.
41
46
  */
42
47
  limits(options?: RequestOptions): Promise<EphemeralKeyLimits>;
43
48
  /**
@@ -49,10 +54,11 @@ export declare class EphemeralKeys extends Resource {
49
54
  create(body: EphemeralKeyCreateRequest, options?: EphemeralKeyCreateOptions): Promise<EphemeralKeyCreateResponse>;
50
55
  /**
51
56
  * 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.
57
+ * comes back when it settles — awaited with `{ settled: true }`. `null`
58
+ * when there is no such key — a revoked, expired or unknown id answers
59
+ * as itself, a non-UUID as none.
54
60
  */
55
- revoke(id: string, options?: RequestOptions): Promise<EphemeralKeyInfo | null>;
61
+ revoke(id: string, options?: EphemeralKeyRevokeOptions): Promise<EphemeralKeyInfo | null>;
56
62
  /**
57
63
  * A key as it reads itself: its scopes, limit, spend and expiry. `null`
58
64
  * when the key is refused — expired, revoked, spent or not a key.
@@ -5,24 +5,25 @@
5
5
  * - **A key is prepaid**: its `credit_limit_micros` is set aside from the
6
6
  * balance when it is minted (402 when it cannot be), and what it does not spend
7
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.
8
+ * - **`create` clamps the lifetime by default** to the tighter of the
9
+ * platform's maximum and the plan's for one end user's key, both read
10
+ * from `limits()` — which the API never does: past a cap is a 422
11
+ * (`apps/api/src/org/minting.ts`).
13
12
  * - **`limits()` is cached five minutes per client**, so a mint is two
14
13
  * requests cold and one warm. Concurrent cold callers share one read.
15
14
  * - **`revoke` answers at once**, with the figures from before the key's
16
15
  * 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_micros` moves only when the key settles — at the end, or
21
- * daily — so a live key reads as unspent (left-overs #105).
16
+ * (`apps/api/src/org/areas/keys.ts` `revokeEphemeral`). With
17
+ * `{ settled: true }` it reads the key (`get`) until `settled_at` is set
18
+ * — seconds, longer while a memory job the key paid for runs — on the
19
+ * polls of `../wait.ts`; a `signal` is its deadline.
20
+ * - **`inspect` reads a key as itself** (`GET /v1/token` with that key):
21
+ * its spend as its end user DO counts it, turn by turn.
22
22
  */
23
23
  import { AuthenticationError } from "../errors.js";
24
24
  import { orNull } from "../transport.js";
25
- import { Resource } from "./resource.js";
25
+ import { poll } from "../wait.js";
26
+ import { Readable } from "./resource.js";
26
27
  const LIMITS_TTL_MS = 5 * 60_000;
27
28
  /** The route's largest page. */
28
29
  const PAGE = 200;
@@ -44,12 +45,13 @@ function until(promise, signal) {
44
45
  });
45
46
  });
46
47
  }
47
- export class EphemeralKeys extends Resource {
48
+ export class EphemeralKeys extends Readable {
48
49
  cached = null;
49
50
  /**
50
- * What a key may be minted with: the **platform's** longest lifetime and
51
- * largest credit limit, and the scopes this API key may grant. Cached
52
- * five minutes per client.
51
+ * What a key may be minted with: the platform's longest lifetime and
52
+ * largest credit limit, the plan's for one end user's key (`plan_*`,
53
+ * `null` where it sets none), and the scopes this API key may grant.
54
+ * Cached five minutes per client.
53
55
  */
54
56
  limits(options = {}) {
55
57
  const now = Date.now();
@@ -82,18 +84,23 @@ export class EphemeralKeys extends Resource {
82
84
  const wanted = body.expires_in_seconds ?? limits.default_ttl_seconds;
83
85
  sent = {
84
86
  ...body,
85
- expires_in_seconds: Math.min(wanted, limits.max_ttl_seconds),
87
+ expires_in_seconds: Math.min(wanted, limits.max_ttl_seconds, limits.plan_max_ttl_seconds ?? wanted),
86
88
  };
87
89
  }
88
90
  return this.send("POST", "", request, sent);
89
91
  }
90
92
  /**
91
93
  * Revokes a key: it is refused from now on, and what it did not spend
92
- * comes back when it settles. `null` when there is no such key — a
93
- * revoked, expired or unknown id answers as itself, a non-UUID as none.
94
+ * comes back when it settles — awaited with `{ settled: true }`. `null`
95
+ * when there is no such key — a revoked, expired or unknown id answers
96
+ * as itself, a non-UUID as none.
94
97
  */
95
- revoke(id, options) {
96
- return orNull(this.send("DELETE", this.at(id), options));
98
+ async revoke(id, options = {}) {
99
+ const { settled, ...request } = options;
100
+ const key = await orNull(this.send("DELETE", this.at(id), request));
101
+ return key && settled
102
+ ? poll(() => this.get(id, request), request, key, (k) => !k.settled_at)
103
+ : key;
97
104
  }
98
105
  /**
99
106
  * A key as it reads itself: its scopes, limit, spend and expiry. `null`
@@ -40,3 +40,17 @@ export declare class Waitable<Item extends Job & {
40
40
  /** `item` as made, or as it ended when `options.wait` says to wait. */
41
41
  protected settled(item: Item, options: UploadOptions): Promise<Item>;
42
42
  }
43
+ /** What a `Library` adds to its base. */
44
+ export interface Listing<Listed, ListPage, Create, Created> {
45
+ /** The page, as the route answers it: all of them, `next_cursor` `null`. */
46
+ page(options?: RequestOptions): Promise<ListPage>;
47
+ /** Every one of them. */
48
+ list(options?: RequestOptions): AsyncGenerator<Listed, void, undefined>;
49
+ create(body: Create, options?: RequestOptions): Promise<Created>;
50
+ }
51
+ /** An editable listed whole and made by a `POST`: the agents, the tools. */
52
+ export declare const Library: new <Item, Update, Listed, ListPage, Create, Created = Item>(api: Transport, base: string) => Editable<Item, Update> & Listing<Listed, ListPage, Create, Created>;
53
+ /** A waitable listed whole and made by a `POST`: the voices. */
54
+ export declare const WaitableLibrary: new <Item extends Job & {
55
+ id: string;
56
+ }, Update, Listed, ListPage, Create>(api: Transport, base: string) => Waitable<Item, Update> & Listing<Listed, ListPage, Create, Item>;
@@ -13,6 +13,9 @@
13
13
  * `false` when there is nothing by that id — already done. One that
14
14
  * reports what went with it (an end user's, a folder's) answers those
15
15
  * counts, and `null` for nothing by that id: the same rule, with a body.
16
+ * - **`Library`** and **`WaitableLibrary`**: one of the two, listed whole
17
+ * in one page and made by a `POST` to the list — `page()`, `list()`,
18
+ * `create(body)`, written once (`listed`).
16
19
  * - **`Waitable`**: an editable whose items are jobs — a voice, an asset,
17
20
  * a knowledge entry — with `wait(id)` and `settled`, which an upload
18
21
  * answers through.
@@ -83,3 +86,23 @@ export class Waitable extends Editable {
83
86
  : item;
84
87
  }
85
88
  }
89
+ /**
90
+ * `Listing`'s three methods on `Base`, written once for every resource
91
+ * listed whole in one page and made by a `POST` to the list: a mixin, as
92
+ * the agents and the tools are editables and the voices waitables.
93
+ */
94
+ const listed = (Base) => class extends Base {
95
+ page(options) {
96
+ return this.send("GET", "", options);
97
+ }
98
+ list(options) {
99
+ return this.each("", {}, options);
100
+ }
101
+ create(body, options) {
102
+ return this.send("POST", "", options, body);
103
+ }
104
+ };
105
+ /** An editable listed whole and made by a `POST`: the agents, the tools. */
106
+ export const Library = listed(Editable);
107
+ /** A waitable listed whole and made by a `POST`: the voices. */
108
+ export const WaitableLibrary = listed(Waitable);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `client.tools`: what an organisation's agents can call while they reply
3
+ * — `/v1/tools*`. A tool is your server (`webhook`), sent a signed `POST`
4
+ * with the call, or the person's app (`app`), called over the agent's
5
+ * socket; each is turned on per agent (`agents.tools.set`), 20 at most.
6
+ *
7
+ * - **`create(body)`** answers a webhook's signing secret once, as does
8
+ * `rotateSecret` (`secret`); every call carries `X-Tool-Signature`.
9
+ * - **`test(id, { arguments })`** sends your server one call, signed as a
10
+ * real one, and answers what came back.
11
+ * - **`list()` reads one page**: a library holds 200 tools at most.
12
+ * - **`update` with `expected_version`** refuses an edit made over
13
+ * somebody else's (409); `delete` answers `true`, and `false` for one
14
+ * already gone.
15
+ */
16
+ import type { ToolCreate, ToolInfo, ToolPage, ToolTestRequest, ToolTestResult, ToolUpdate, ToolWithSecret } from "../gen/index.js";
17
+ import type { RequestOptions } from "../transport.js";
18
+ import { Library } from "./resource.js";
19
+ export declare class Tools extends Library<ToolInfo, ToolUpdate, ToolInfo, ToolPage, ToolCreate, ToolWithSecret> {
20
+ /** A new signing secret, shown this once. */
21
+ rotateSecret(id: string, options?: RequestOptions): Promise<ToolWithSecret>;
22
+ /** One call to your server, as a conversation would send it: `{ arguments }`. */
23
+ test(id: string, body?: ToolTestRequest, options?: RequestOptions): Promise<ToolTestResult>;
24
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `client.tools`: what an organisation's agents can call while they reply
3
+ * — `/v1/tools*`. A tool is your server (`webhook`), sent a signed `POST`
4
+ * with the call, or the person's app (`app`), called over the agent's
5
+ * socket; each is turned on per agent (`agents.tools.set`), 20 at most.
6
+ *
7
+ * - **`create(body)`** answers a webhook's signing secret once, as does
8
+ * `rotateSecret` (`secret`); every call carries `X-Tool-Signature`.
9
+ * - **`test(id, { arguments })`** sends your server one call, signed as a
10
+ * real one, and answers what came back.
11
+ * - **`list()` reads one page**: a library holds 200 tools at most.
12
+ * - **`update` with `expected_version`** refuses an edit made over
13
+ * somebody else's (409); `delete` answers `true`, and `false` for one
14
+ * already gone.
15
+ */
16
+ import { Library } from "./resource.js";
17
+ export class Tools extends Library {
18
+ /** A new signing secret, shown this once. */
19
+ rotateSecret(id, options) {
20
+ return this.send("POST", this.at(id, "/secret"), options);
21
+ }
22
+ /** One call to your server, as a conversation would send it: `{ arguments }`. */
23
+ test(id, body = {}, options) {
24
+ return this.send("POST", this.at(id, "/test"), options, body);
25
+ }
26
+ }
@@ -16,20 +16,18 @@
16
16
  import type { VoiceDesignRequest, VoiceDesignResult, VoiceDesignSaveRequest, VoiceDetail, VoiceInfo, VoiceInfoPage, VoiceUpdate, VoiceUpload, VoiceUploadRequest, VoiceUploadTicket } from "../gen/index.js";
17
17
  import type { RequestOptions } from "../transport.js";
18
18
  import { type UploadData, type UploadOptions } from "../upload.js";
19
- import { Waitable } from "./resource.js";
19
+ import { WaitableLibrary } from "./resource.js";
20
20
  /** A recording's voice: the create's fields, and the recording's type. */
21
21
  export type VoiceUploadFields = Omit<VoiceUpload, "upload_id"> & {
22
22
  /** A Blob's own type by default: `audio/wav`, `audio/mpeg`, … */
23
23
  content_type?: string;
24
24
  };
25
- /** `update` renames a voice, or changes its description or defaults. */
26
- export declare class Voices extends Waitable<VoiceDetail, VoiceUpdate> {
27
- /** The page of voices, as the route answers it: all of them, `next_cursor` `null`. */
28
- page(options?: RequestOptions): Promise<VoiceInfoPage>;
29
- /** Every voice this organisation may speak with. */
30
- list(options?: RequestOptions): AsyncGenerator<VoiceInfo, void, undefined>;
31
- /** Clones a recording already `PUT` to its ticket: `pending` at once. */
32
- create(body: VoiceUpload, options?: RequestOptions): Promise<VoiceDetail>;
25
+ /**
26
+ * `update` renames a voice, or changes its description or defaults;
27
+ * `list()` is every voice this organisation may speak with; `create(body)`
28
+ * clones a recording already `PUT` to its ticket, `pending` at once.
29
+ */
30
+ export declare class Voices extends WaitableLibrary<VoiceDetail, VoiceUpdate, VoiceInfo, VoiceInfoPage, VoiceUpload> {
33
31
  /** A signed URL to `PUT` a recording to, with exactly this type. */
34
32
  uploadUrl(body: VoiceUploadRequest, options?: RequestOptions): Promise<VoiceUploadTicket>;
35
33
  /** Uploads a recording and clones it: `uploadUrl`, the `PUT`, `create`. */
@@ -14,21 +14,13 @@
14
14
  * - **`list()` reads one page**: the route answers every voice in it.
15
15
  */
16
16
  import { needType, put, } from "../upload.js";
17
- import { Waitable } from "./resource.js";
18
- /** `update` renames a voice, or changes its description or defaults. */
19
- export class Voices extends Waitable {
20
- /** The page of voices, as the route answers it: all of them, `next_cursor` `null`. */
21
- page(options) {
22
- return this.send("GET", "", options);
23
- }
24
- /** Every voice this organisation may speak with. */
25
- list(options) {
26
- return this.each("", {}, options);
27
- }
28
- /** Clones a recording already `PUT` to its ticket: `pending` at once. */
29
- create(body, options) {
30
- return this.send("POST", "", options, body);
31
- }
17
+ import { WaitableLibrary } from "./resource.js";
18
+ /**
19
+ * `update` renames a voice, or changes its description or defaults;
20
+ * `list()` is every voice this organisation may speak with; `create(body)`
21
+ * clones a recording already `PUT` to its ticket, `pending` at once.
22
+ */
23
+ export class Voices extends WaitableLibrary {
32
24
  /** A signed URL to `PUT` a recording to, with exactly this type. */
33
25
  uploadUrl(body, options) {
34
26
  return this.send("POST", "/upload-url", options, body);
package/dist/wait.d.ts CHANGED
@@ -32,7 +32,9 @@ export interface Job {
32
32
  status: string;
33
33
  }
34
34
  /**
35
- * Reads `read` until its status is final, starting from `first` (a job
36
- * just made, so the first read waits) or with a read now.
35
+ * Reads `read` until its status is final — or until `pending` says it no
36
+ * longer is, for what is not a job (a revoked key's settlement) —
37
+ * starting from `first` (a job just made, so the first read waits) or
38
+ * with a read now.
37
39
  */
38
- export declare function poll<T extends Job>(read: () => Promise<T>, options?: RequestOptions, first?: T): Promise<T>;
40
+ export declare function poll<T>(read: () => Promise<T>, options?: RequestOptions, first?: T, pending?: (job: T) => boolean): Promise<T>;
package/dist/wait.js CHANGED
@@ -37,12 +37,14 @@ const IN_PROGRESS = [
37
37
  const FIRST_MS = 1_000;
38
38
  const MAX_MS = 10_000;
39
39
  /**
40
- * Reads `read` until its status is final, starting from `first` (a job
41
- * just made, so the first read waits) or with a read now.
40
+ * Reads `read` until its status is final — or until `pending` says it no
41
+ * longer is, for what is not a job (a revoked key's settlement) —
42
+ * starting from `first` (a job just made, so the first read waits) or
43
+ * with a read now.
42
44
  */
43
- export async function poll(read, options = {}, first) {
45
+ export async function poll(read, options = {}, first, pending = (job) => IN_PROGRESS.includes(job.status)) {
44
46
  let job = first ?? (await read());
45
- for (let ms = FIRST_MS; IN_PROGRESS.includes(job.status);) {
47
+ for (let ms = FIRST_MS; pending(job);) {
46
48
  await sleep(ms, options.signal);
47
49
  job = await read();
48
50
  ms = Math.min(ms * 1.5, MAX_MS);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumavox/server",
3
- "version": "0.5.0",
3
+ "version": "0.8.0",
4
4
  "description": "The Lumavox API from your server: end users, ephemeral keys and the rest, typed from the published OpenAPI document. Zero dependencies.",
5
5
  "license": "MIT",
6
6
  "type": "module",