@cubos/agent-sdk 0.0.1154897 → 0.0.1154905

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.
@@ -13,7 +13,7 @@ import { mcpsApi } from "./mcps.js";
13
13
  import { instanceMetricsApi, metricsApi } from "./metrics.js";
14
14
  import { skillsApi } from "./skills.js";
15
15
  import { backgroundTasksApi, scheduledRunsApi, taskTemplatesApi } from "./task-templates.js";
16
- import { userRolesApi, usersApi, userTokensApi } from "./users.js";
16
+ import { costLimitsApi, userRolesApi, usersApi, userTokensApi } from "./users.js";
17
17
  export interface AdminClientOptions {
18
18
  /** Where the Cubos Agent core is reachable, e.g. `https://agent.acme.com`. */
19
19
  baseUrl: string;
@@ -40,6 +40,8 @@ export interface TenantScope {
40
40
  users: ReturnType<typeof usersApi>;
41
41
  userRoles: ReturnType<typeof userRolesApi>;
42
42
  userTokens: ReturnType<typeof userTokensApi>;
43
+ /** Tenant-wide default spend ceilings; per-user overrides live under `users`. */
44
+ costLimits: ReturnType<typeof costLimitsApi>;
43
45
  knowledgeBases: ReturnType<typeof knowledgeBasesApi>;
44
46
  taskTemplates: ReturnType<typeof taskTemplatesApi>;
45
47
  scheduledRuns: ReturnType<typeof scheduledRunsApi>;
@@ -1,5 +1,11 @@
1
1
  import type { Transport } from "../http.js";
2
2
  import type { Schemas } from "../schemas.js";
3
+ /**
4
+ * The windows a spend ceiling can be set over. Each is also a `granularity` of
5
+ * the metrics API, so an operator who sets a weekly ceiling can read the
6
+ * weekly series of `cost_usd` filtered to that user.
7
+ */
8
+ export type CostPeriod = "hour" | "day" | "week" | "month";
3
9
  export declare function usersApi(t: Transport, tenantSlug: string): {
4
10
  list: (query?: {
5
11
  status?: string;
@@ -30,6 +36,51 @@ export declare function usersApi(t: Transport, tenantSlug: string): {
30
36
  updated_at: string;
31
37
  user_md: string;
32
38
  }>;
39
+ /**
40
+ * Spend against the ceilings actually in force for this user — tenant
41
+ * defaults and per-user overrides already resolved.
42
+ *
43
+ * This is the one to draw a meter from. `costLimits.get` returns only the
44
+ * user's own overrides, so an empty list there means "inherits", not
45
+ * "unlimited".
46
+ */
47
+ costStatus: (id: string, signal?: AbortSignal) => Promise<{
48
+ blocked_by: import("../generated/schema.js").components["schemas"]["Period"] | null;
49
+ windows: import("../generated/schema.js").components["schemas"]["CostStatusWindow"][];
50
+ }>;
51
+ /**
52
+ * This user's spend-ceiling overrides. Declarative: `put` replaces the
53
+ * whole set, and putting `{ rules: [] }` drops every override so they fall
54
+ * back to the tenant defaults.
55
+ */
56
+ costLimits: {
57
+ get: (id: string, signal?: AbortSignal) => Promise<{
58
+ default_messages: {
59
+ [key: string]: string;
60
+ } | null;
61
+ locale: import("../generated/schema.js").components["schemas"]["CostLimitLocale"] | null;
62
+ message: string | null;
63
+ placeholder_examples: {
64
+ [key: string]: {
65
+ [key: string]: string;
66
+ };
67
+ } | null;
68
+ rules: import("../generated/schema.js").components["schemas"]["CostLimitRule"][];
69
+ }>;
70
+ put: (id: string, input: Schemas["PutCostLimits"]) => Promise<{
71
+ default_messages: {
72
+ [key: string]: string;
73
+ } | null;
74
+ locale: import("../generated/schema.js").components["schemas"]["CostLimitLocale"] | null;
75
+ message: string | null;
76
+ placeholder_examples: {
77
+ [key: string]: {
78
+ [key: string]: string;
79
+ };
80
+ } | null;
81
+ rules: import("../generated/schema.js").components["schemas"]["CostLimitRule"][];
82
+ }>;
83
+ };
33
84
  create: (input: Schemas["CreateUserInput"]) => Promise<{
34
85
  created_at: string;
35
86
  display_name: string | null;
@@ -203,6 +254,55 @@ export declare function userRolesApi(t: Transport, tenantSlug: string): {
203
254
  }>;
204
255
  delete: (slug: string) => Promise<void>;
205
256
  };
257
+ /**
258
+ * Tenant-wide default spend ceilings — what applies to every user with no
259
+ * override of their own.
260
+ *
261
+ * Nothing is seeded: a tenant starts with an empty list, meaning no user has a
262
+ * ceiling. A rule with `limit_usd: 0` is not the same as no rule — absent is
263
+ * unlimited, zero stops the user outright.
264
+ */
265
+ export declare function costLimitsApi(t: Transport, tenantSlug: string): {
266
+ get: (signal?: AbortSignal) => Promise<{
267
+ default_messages: {
268
+ [key: string]: string;
269
+ } | null;
270
+ locale: import("../generated/schema.js").components["schemas"]["CostLimitLocale"] | null;
271
+ message: string | null;
272
+ placeholder_examples: {
273
+ [key: string]: {
274
+ [key: string]: string;
275
+ };
276
+ } | null;
277
+ rules: import("../generated/schema.js").components["schemas"]["CostLimitRule"][];
278
+ }>;
279
+ /** Replaces the whole set; any period left out is removed. */
280
+ put: (input: Schemas["PutCostLimits"]) => Promise<{
281
+ default_messages: {
282
+ [key: string]: string;
283
+ } | null;
284
+ locale: import("../generated/schema.js").components["schemas"]["CostLimitLocale"] | null;
285
+ message: string | null;
286
+ placeholder_examples: {
287
+ [key: string]: {
288
+ [key: string]: string;
289
+ };
290
+ } | null;
291
+ rules: import("../generated/schema.js").components["schemas"]["CostLimitRule"][];
292
+ }>;
293
+ /**
294
+ * Everyone currently blocked by a ceiling. Computed at read time — a
295
+ * ceiling lifts by the clock passing an instant, so there is no stored
296
+ * flag that could be trusted between the window turning over and a
297
+ * refresh.
298
+ */
299
+ overLimit: (query?: {
300
+ limit?: number;
301
+ }, signal?: AbortSignal) => Promise<{
302
+ truncated: boolean;
303
+ users: import("../generated/schema.js").components["schemas"]["OverLimitUser"][];
304
+ }>;
305
+ };
206
306
  /**
207
307
  * Mints the short-lived tokens a browser SDK runs on (see `createUserClient`).
208
308
  * Call this from your own backend only — it needs an api_key, which must never
package/dist/errors.d.ts CHANGED
@@ -1,3 +1,23 @@
1
+ /**
2
+ * A spend ceiling was reached. The body of a 402, parsed.
3
+ *
4
+ * Carries no amounts, deliberately. The dollar figures behind a ceiling are
5
+ * the operator's accounting: most products put their own layer over this —
6
+ * credits, a plan tier, a quota in requests — so "you spent 0.055 of 0.05"
7
+ * would be a unit the user was never sold in. Those figures live on the admin
8
+ * surface, behind `users:read`.
9
+ *
10
+ * What is here is what an app can act on: `message`, authored by the tenant
11
+ * and therefore already speaking in whatever unit they sell; the window that
12
+ * tripped; and when it lifts.
13
+ */
14
+ export interface OverBudget {
15
+ period: "hour" | "day" | "week" | "month";
16
+ /** When the window turns over and access returns, as an ISO instant. */
17
+ resetsAt: string;
18
+ /** Ready to show, already naming the window and the reset. */
19
+ message: string;
20
+ }
1
21
  /** Base for everything this SDK throws, so `catch (e) { if (e instanceof
2
22
  * AgentError) }` covers both an HTTP failure and a connection that never got
3
23
  * there. */
@@ -23,8 +43,25 @@ export declare class AgentApiError extends AgentError {
23
43
  /** The request clashed with current state — a slug already taken, a
24
44
  * conversation that belongs to a channel, a user already blocked. */
25
45
  get isConflict(): boolean;
26
- /** Worth retrying after a pause: the server is overloaded or briefly down. */
46
+ /**
47
+ * Worth retrying after a pause: the server is overloaded or briefly down.
48
+ *
49
+ * Note what is absent. A 402 is *not* retryable here, deliberately: the
50
+ * window it names is hours or days away, so retrying is pointless and
51
+ * retrying automatically would spend the user's backoff budget on a wall
52
+ * that is not going to move.
53
+ */
27
54
  get isRetryable(): boolean;
55
+ /**
56
+ * The spend ceiling this request hit, or `null` when the failure was
57
+ * something else.
58
+ *
59
+ * Distinct from `isAuthError` on purpose — a blocked user (403) and a user
60
+ * over budget (402) look the same to a client that only checks "was I
61
+ * refused", and they are opposite situations: one needs an operator, the
62
+ * other needs a clock.
63
+ */
64
+ get overBudget(): OverBudget | null;
28
65
  }
29
66
  /**
30
67
  * The client was constructed wrong — a `baseUrl` with no scheme, most often.