@beeos-ai/bma-sdk 0.4.2 → 1.0.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.
Files changed (51) hide show
  1. package/README.md +122 -34
  2. package/dist/core.d.ts +105 -0
  3. package/dist/core.js +406 -0
  4. package/dist/core.js.map +1 -0
  5. package/dist/errors.d.ts +77 -0
  6. package/dist/errors.js +109 -0
  7. package/dist/errors.js.map +1 -0
  8. package/dist/facade-extensions.d.ts +17 -61
  9. package/dist/facade-extensions.js +25 -112
  10. package/dist/facade-extensions.js.map +1 -1
  11. package/dist/generated/facade-extensions-operations.d.ts +133 -0
  12. package/dist/generated/facade-extensions-operations.js +11 -0
  13. package/dist/generated/facade-extensions-operations.js.map +1 -0
  14. package/dist/generated/facade-extensions-resources.d.ts +34 -0
  15. package/dist/generated/facade-extensions-resources.js +47 -0
  16. package/dist/generated/facade-extensions-resources.js.map +1 -0
  17. package/dist/generated/facade-extensions-types.d.ts +14 -0
  18. package/dist/generated/facade-extensions-types.js +2 -0
  19. package/dist/generated/facade-extensions-types.js.map +1 -0
  20. package/dist/generated/rest-operations.d.ts +4079 -0
  21. package/dist/generated/rest-operations.js +234 -0
  22. package/dist/generated/rest-operations.js.map +1 -0
  23. package/dist/generated/rest-resources.d.ts +666 -0
  24. package/dist/generated/rest-resources.js +1124 -0
  25. package/dist/generated/rest-resources.js.map +1 -0
  26. package/dist/generated/rest-types.d.ts +572 -0
  27. package/dist/generated/rest-types.js +2 -0
  28. package/dist/generated/rest-types.js.map +1 -0
  29. package/dist/generated/rest.d.ts +233 -10
  30. package/dist/index.d.ts +41 -76
  31. package/dist/index.js +49 -239
  32. package/dist/index.js.map +1 -1
  33. package/dist/lib/artifacts.d.ts +14 -0
  34. package/dist/lib/artifacts.js +56 -0
  35. package/dist/lib/artifacts.js.map +1 -0
  36. package/dist/lib/tool-runner.d.ts +99 -0
  37. package/dist/lib/tool-runner.js +140 -0
  38. package/dist/lib/tool-runner.js.map +1 -0
  39. package/dist/pagination.d.ts +31 -0
  40. package/dist/pagination.js +66 -0
  41. package/dist/pagination.js.map +1 -0
  42. package/dist/resource.d.ts +8 -0
  43. package/dist/resource.js +9 -0
  44. package/dist/resource.js.map +1 -0
  45. package/dist/streaming.d.ts +27 -0
  46. package/dist/streaming.js +89 -0
  47. package/dist/streaming.js.map +1 -0
  48. package/dist/version.d.ts +1 -0
  49. package/dist/version.js +4 -0
  50. package/dist/version.js.map +1 -0
  51. package/package.json +4 -2
package/README.md CHANGED
@@ -3,43 +3,136 @@
3
3
  [![npm](https://img.shields.io/npm/v/@beeos-ai/bma-sdk.svg)](https://www.npmjs.com/package/@beeos-ai/bma-sdk)
4
4
 
5
5
  TypeScript client for the BMA REST API, generated from
6
- [`contracts/bma/rest.openapi.yaml`](../contracts/bma/rest.openapi.yaml), plus
7
- the BMA extensions to the Anthropic Managed Agents API.
6
+ [`contracts/bma/rest.openapi.yaml`](../contracts/bma/rest.openapi.yaml) and the
7
+ resource map [`contracts/bma/sdk.resources.yaml`](../contracts/bma/sdk.resources.yaml),
8
+ plus the BMA extensions to the Anthropic Managed Agents API.
8
9
 
9
10
  | | |
10
11
  | --- | --- |
11
- | Version | 0.4.2 (tag `v0.4.2`), see [CHANGELOG](../CHANGELOG.md) |
12
+ | Version | see [CHANGELOG](../CHANGELOG.md); upgrading from 0.4: [MIGRATION](../MIGRATION.md) |
12
13
  | Contract | BMA REST snapshot recorded in [`contracts/bma/lock.json`](../contracts/bma/lock.json) |
13
- | Node.js | 20 or newer; ESM, depends on `openapi-fetch` |
14
-
15
- ```sh
16
- npm install @beeos-ai/bma-sdk@0.4.2
17
- ```
14
+ | Node.js | 20 or newer; ESM |
18
15
 
19
16
  ```ts
20
- import { BmaClient, unwrap } from "@beeos-ai/bma-sdk";
17
+ import { BmaClient } from "@beeos-ai/bma-sdk";
21
18
 
22
- const bma = new BmaClient({ baseUrl: "https://host/api/v1", token: process.env.BMA_TOKEN! });
23
- const path = { orgID: "org_...", projectID: "proj_...", agentID: "agt_..." };
19
+ const bma = new BmaClient({
20
+ token: process.env.BMA_TOKEN!,
21
+ baseUrl: process.env.BMA_BASE_URL!, // origin, e.g. https://api.staging.bma.beeos.ai
22
+ orgId: "org_...", // defaults: also read from BMA_ORG_ID / BMA_PROJECT_ID
23
+ projectId: "prj_...",
24
+ });
24
25
 
25
- const { session } = unwrap(
26
- await bma.rest.POST("/orgs/{orgID}/projects/{projectID}/agents/{agentID}/sessions", {
27
- params: { path },
28
- body: { idempotency_key: "task-1", model_override: { model: "deepseek-flash", expires_with_session: true /* ... */ } },
29
- }),
26
+ const { session } = await bma.agents.sessions.create(agentId, { idempotency_key: "task-1" });
27
+ await bma.agents.inputs.create(
28
+ agentId,
29
+ { session_id: session.id, content_blocks: [{ type: "text", text: "Plan my week." }] },
30
+ { idempotencyKey: "input-1" },
30
31
  );
31
- await bma.rest.POST("/orgs/{orgID}/projects/{projectID}/agents/{agentID}/inputs", {
32
- params: { path, query: { session_id: session.id } },
33
- body: { content_blocks: [{ type: "text", text: "Start." }] },
34
- });
35
- for await (const frame of bma.streamEvents({ ...path, sessionId: session.id })) {
36
- console.log(frame.event, frame.sequence);
32
+
33
+ for await (const ev of bma.agents.events.stream(agentId, { session_id: session.id, stream_deltas: true })) {
34
+ switch (ev.event) {
35
+ case "model_output": console.log(ev.sequence, ev.data.content_blocks); break;
36
+ case "tool_call_update": console.log(ev.data.tool_call_id, ev.data.state); break;
37
+ case "model_output_delta": console.log(ev.data); break;
38
+ }
37
39
  }
38
40
  ```
39
41
 
40
- `bma.rest` is a fully typed [openapi-fetch](https://openapi-ts.dev/openapi-fetch/)
41
- client for every contract operation. `streamEvents` parses the SSE stream and
42
- `downloadArtifact` verifies the artifact size and sha256 digest.
42
+ Set `baseUrl` to the origin of your BMA deployment (for example
43
+ `https://api.staging.bma.beeos.ai`; the default `https://api.bma.beeos.ai` does
44
+ not resolve yet). The client adds `/api/v1`; a value already ending in
45
+ `/api/v1` is accepted. `token` and `baseUrl` default to
46
+ `BMA_TOKEN` and `BMA_BASE_URL`, `orgId` and `projectId` to `BMA_ORG_ID` and
47
+ `BMA_PROJECT_ID`.
48
+
49
+ ## Methods
50
+
51
+ Every contract operation is a method on a resource: path IDs are positional
52
+ arguments, `params` is the operation's query, header and body fields in one
53
+ object (wire names, snake_case), and `options` is the per-request
54
+ `{ timeout, maxRetries, signal, idempotencyKey, headers }`. Params and results
55
+ are the contract types; every schema is exported by name (`AgentSession`,
56
+ `ToolCall`, ...).
57
+
58
+ - **Scope.** `orgId` / `projectId` fill the organization and project of every
59
+ method; pass `org_id` / `project_id` in params to override one call.
60
+ - **Lists.** Every list whose response is `{ data, next_<param> }` returns a
61
+ `PagePromise`: `await` gives the first page (`page.data`,
62
+ `page.hasNextPage()`, `page.getNextPage()`), and
63
+ `for await (const agent of bma.agents.list())` walks every item. Besides
64
+ `cursor` lists this covers `agents.events.list` (forward by
65
+ `after_sequence`, backward when you pass `before_sequence`),
66
+ `agents.sessions.list` (`before_id`), `agentSessions.list` (`after_id`),
67
+ `agents.turns.list`, `agents.turns.events.list` and `files.list` (`page`).
68
+ - **Event stream.** `agents.events.stream()` yields frames discriminated by
69
+ `event`; durable frames carry `id` and `sequence`. Like EventSource, it
70
+ reconnects with `Last-Event-ID` set to the last durable frame when the
71
+ connection drops, when the server ends the response (a deployment), or on
72
+ an `error` frame with code `service_unavailable`; a frame cut off at the end
73
+ is discarded. It never ends on its own: it throws once `maxRetries`
74
+ reconnects in a row received no data (a heartbeat counts as data), and any
75
+ other error frame throws `EventStreamError`.
76
+ - **Files.** Uploads take the bytes as `body` (`Blob`, `ArrayBuffer`, typed
77
+ array or string); downloads (`files.download`, `agents.artifacts.download`,
78
+ ...) return the `Response`. `agents.artifacts.downloadVerified(artifact, { maxBytes })`
79
+ verifies the size and sha256 digest.
80
+
81
+ ## Errors, retries, timeouts
82
+
83
+ Errors are `BmaApiError` subclasses named after the status, as in the
84
+ Anthropic SDK: `BadRequestError` (400), `AuthenticationError` (401),
85
+ `PermissionDeniedError` (403), `NotFoundError` (404), `ConflictError` (409),
86
+ `ContentTooLargeError` (413), `UnprocessableEntityError` (422),
87
+ `RateLimitError` (429), `InternalServerError` (5xx), `ServiceUnavailableError`
88
+ (503), plus `APIConnectionError`, `APIConnectionTimeoutError` and
89
+ `APIUserAbortError`. `error.code` is the contract's `ErrorCode`.
90
+
91
+ Each call retries twice by default (`maxRetries`) with exponential backoff,
92
+ honouring `retry-after-ms` / `retry-after`. Reads and operations that accept
93
+ an `Idempotency-Key` retry 408, 429, 5xx and connection errors; the SDK sends
94
+ a generated key (`bma-sdk-retry-<uuid>`) unless you pass `idempotencyKey`, and
95
+ reuses it across the retries. Other writes retry only 429, 503 and requests
96
+ that never reached the server. 409 is never retried. The timeout is 60 s per
97
+ attempt (`timeout`), measured until the response headers arrive; `0`
98
+ disables it. Uploads (binary and multipart bodies) get only a per-call
99
+ `timeout`, so a large file is never cut off by the client default (Node's
100
+ fetch still bounds connecting and waiting for headers). The `signal` also
101
+ aborts reading the response body. Every error the SDK throws is a
102
+ `BmaError`.
103
+
104
+ ## Custom tools
105
+
106
+ A custom tool call waits in `ready` until a consumer submits its result.
107
+ `defineTool` takes any [Standard Schema](https://standardschema.dev) (zod 4.2+,
108
+ valibot, arktype) and `agents.toolCalls.runner` runs the matching calls of an
109
+ agent and its subagents:
110
+
111
+ ```ts
112
+ import { z } from "zod";
113
+ import { ToolError, defineTool } from "@beeos-ai/bma-sdk";
114
+
115
+ const weather = defineTool({
116
+ name: "get_weather",
117
+ description: "Current weather",
118
+ inputSchema: z.object({ city: z.string() }),
119
+ run: async ({ city }) => {
120
+ if (!city) throw new ToolError("city is empty"); // submitted as a failed result
121
+ return `Sunny in ${city}`; // string or result content blocks
122
+ },
123
+ });
124
+ // weather.definition is the agent config entry for tools.get_weather.
125
+
126
+ const runner = bma.agents.toolCalls.runner(agentId, { session_id: session.id, tools: [weather] });
127
+ // ... later
128
+ await runner.stop();
129
+ ```
130
+
131
+ The runner reconnects its event stream without limit by default (backoff up
132
+ to 8 s; `max_reconnects` bounds it), lists ready calls again after each
133
+ reconnect, treats a 409 on submit (another consumer answered first) as
134
+ handled, and `done()` rejects with the error that stopped it; it never ends
135
+ silently.
43
136
 
44
137
  ## BMA extensions to the Anthropic facade
45
138
 
@@ -49,16 +142,11 @@ Use the official Anthropic SDK for `/v1/agents`, `/v1/environments`,
49
142
  ```ts
50
143
  import { FacadeExtensionsClient, bmaSequence } from "@beeos-ai/bma-sdk/facade-extensions";
51
144
 
52
- const ext = new FacadeExtensionsClient({ baseUrl: "https://host", apiKey: key, projectId: "proj_..." });
53
- const ticket = await ext.getSessionDesktopStream(sessionId, { desktopSessionId, origin: location.origin });
54
- await ext.uploadSessionFile(sessionId, new Blob([bytes]), "report.pdf");
55
- const page = await ext.listSessionFiles(sessionId, { limit: 100 });
145
+ const ext = new FacadeExtensionsClient({ baseUrl: "https://host", apiKey: key, projectId: "prj_..." });
146
+ const ticket = await ext.sessions.desktop.stream(sessionId, { desktop_session_id, origin: location.origin });
147
+ await ext.sessions.files.upload(sessionId, { file: new File([bytes], "report.pdf") });
148
+ const page = await ext.sessions.files.list(sessionId, { limit: 100 });
56
149
  ```
57
150
 
58
- It also exports the extension field types `ModelConfigOverride`
59
- (`agent.model.api_url/api_key/api_format`), `MCPServerBMAAuth`
60
- (`mcp_servers[].bma_auth`), `IDEMPOTENCY_KEY_HEADER`, and `bmaSequence(event)`
61
- for `bma_sequence` resume cursors.
62
-
63
151
  The executor protocol (executor, harness and agent ends) is the separate
64
152
  package [`@beeos-ai/bma-executor-sdk`](https://github.com/beeos-ai/bma-executor-sdk/tree/main/typescript).
package/dist/core.d.ts ADDED
@@ -0,0 +1,105 @@
1
+ import { PagePromise, type CursorPageBody } from "./pagination.js";
2
+ import { Stream } from "./streaming.js";
3
+ /**
4
+ * Default per-attempt timeout (D3), measured until the response headers
5
+ * arrive. Uploads (binary and multipart bodies) have no default timeout;
6
+ * event streams and downloads are bounded only until the headers arrive.
7
+ */
8
+ export declare const DEFAULT_TIMEOUT_MS = 60000;
9
+ /** Default retries after the first attempt (D3; @anthropic-ai/sdk uses 2). */
10
+ export declare const DEFAULT_MAX_RETRIES = 2;
11
+ /** Generated description of one contract operation. */
12
+ export interface OperationDescriptor {
13
+ readonly id: string;
14
+ readonly method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
15
+ readonly path: string;
16
+ /** Path template name -> params key (scope params) or positional name. */
17
+ readonly pathParams?: Readonly<Record<string, string>>;
18
+ /** Query name -> serialization style. */
19
+ readonly query?: Readonly<Record<string, string>>;
20
+ /** Header name -> params key. */
21
+ readonly headers?: Readonly<Record<string, string>>;
22
+ readonly body?: "json" | "binary" | "multipart";
23
+ readonly bodyRequired?: boolean;
24
+ readonly multipartJSON?: readonly string[];
25
+ readonly response: "json" | "void" | "binary" | "sse";
26
+ /** The operation accepts an Idempotency-Key header. */
27
+ readonly idempotency?: boolean;
28
+ /**
29
+ * Paginated list: [query parameter, response field] pairs, the default
30
+ * direction first, e.g. [["cursor", "next_cursor"]].
31
+ */
32
+ readonly page?: readonly (readonly [string, string])[];
33
+ }
34
+ /** Per-request options; every resource method takes them last. */
35
+ export interface RequestOptions {
36
+ /**
37
+ * Per-attempt timeout in milliseconds, until the response headers arrive;
38
+ * 0 means none. Uploads use only this per-call value (no client default).
39
+ */
40
+ timeout?: number;
41
+ /** Retries after the first attempt; for event streams, reconnects in a row without receiving data. */
42
+ maxRetries?: number;
43
+ signal?: AbortSignal;
44
+ /**
45
+ * Idempotency-Key for operations that declare it. When omitted the SDK
46
+ * generates one and reuses it across the retries of this call.
47
+ */
48
+ idempotencyKey?: string;
49
+ headers?: Record<string, string>;
50
+ }
51
+ /** A raw request body: file contents or bytes. */
52
+ export type Uploadable = Blob | ArrayBuffer | ArrayBufferView | string;
53
+ /** @internal A body that may be omitted entirely. */
54
+ export type OptionalBody<B> = B | {
55
+ [K in keyof B]?: never;
56
+ };
57
+ /** @internal A multipart body whose binary fields take a Blob or File. */
58
+ export type MultipartBody<B, Files extends PropertyKey> = B extends unknown ? {
59
+ [K in keyof B]: K extends Files ? Blob : B[K];
60
+ } : never;
61
+ /** @internal */
62
+ export interface ClientCoreOptions {
63
+ baseUrl: string;
64
+ fetch?: typeof globalThis.fetch;
65
+ timeout?: number;
66
+ maxRetries?: number;
67
+ /** Authentication and other headers sent on every request. */
68
+ headers: Record<string, string>;
69
+ /** Scope defaults keyed by params name (org_id, project_id). */
70
+ scope: Record<string, string | undefined>;
71
+ /** Client option that sets each scope default, for error messages. */
72
+ scopeOption: Record<string, string>;
73
+ }
74
+ /**
75
+ * The deployment origin of a BMA base URL (BMA_BASE_URL): trailing slashes and
76
+ * a trailing /api/v1 (the pre-1.0 API-root form) are removed, so the SDK adds
77
+ * each API prefix exactly once.
78
+ */
79
+ export declare function bmaOrigin(baseUrl: string): string;
80
+ /** @internal The transport every resource calls. */
81
+ export declare class ClientCore {
82
+ #private;
83
+ readonly baseUrl: string;
84
+ constructor(options: ClientCoreOptions);
85
+ /** @internal */
86
+ _request<T>(op: OperationDescriptor, path: Record<string, string>, params: object, options?: RequestOptions): Promise<T>;
87
+ /**
88
+ * @internal A paginated list. The cursor parameter is the one of
89
+ * `op.page` the caller passed (e.g. before_sequence), else the default.
90
+ */
91
+ _page<R extends CursorPageBody>(op: OperationDescriptor, path: Record<string, string>, params: object, options?: RequestOptions): PagePromise<R>;
92
+ /**
93
+ * @internal Event stream with EventSource reconnection: when the
94
+ * connection drops or the server ends the response (a rolling restart), or
95
+ * sends an `error` frame with code `service_unavailable`, the stream
96
+ * reconnects with Last-Event-ID set to the last durable frame. A frame not
97
+ * terminated before the end is discarded. Reconnects are bounded by
98
+ * `maxReconnects` (default `maxRetries`) attempts in a row that receive no
99
+ * data; any other error frame ends the stream with EventStreamError.
100
+ */
101
+ _stream<T>(op: OperationDescriptor, path: Record<string, string>, params: object, options?: RequestOptions, hooks?: {
102
+ onOpen?: () => void;
103
+ maxReconnects?: number;
104
+ }): Stream<T>;
105
+ }