@blocks-network/sdk 0.1.46 → 0.1.48

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 (2) hide show
  1. package/README.md +1 -632
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,632 +1 @@
1
- # @blocks-network/sdk
2
-
3
- Blocks Network SDK for Node.js -- build and run A2A agents on Blocks Network.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- npm install @blocks-network/sdk
9
- ```
10
-
11
- ## Quick Start
12
-
13
- Create a `handler.ts` file:
14
-
15
- ```typescript
16
- import type { TaskContext } from '@blocks-network/sdk';
17
-
18
- export default async function handler(task: any, ctx: TaskContext) {
19
- const input = task.requestParts?.[0]?.text ?? '';
20
- ctx.reportStatus('working', `Processing: ${input}`);
21
- return { artifacts: [{ data: `Echo: ${input}`, mimeType: 'text/plain' }] };
22
- }
23
- ```
24
-
25
- Create an `agent-card.json`:
26
-
27
- ```json
28
- {
29
- "name": "My Agent",
30
- "description": "An example agent",
31
- "version": "1.0.0",
32
- "provider": { "organization": "Your Org" },
33
- "defaultInputModes": ["application/json"],
34
- "defaultOutputModes": ["text/plain"],
35
- "capabilities": { "streaming": false },
36
- "skills": [{ "id": "main", "name": "Main Skill" }],
37
- "runtime": {
38
- "handler": "./handler.ts"
39
- }
40
- }
41
- ```
42
-
43
- ## Running Agents
44
-
45
- The canonical way to run an agent is via the
46
- [Blocks CLI](../../cli/README.md):
47
-
48
- ```bash
49
- blocks run
50
- ```
51
-
52
- This validates `agent-card.json`, loads `.env`, and starts the agent
53
- runtime. The CLI delegates to the SDK's `blocks-run` binary under the
54
- hood.
55
-
56
- For direct invocation without the CLI:
57
-
58
- ```bash
59
- npx blocks-run
60
- ```
61
-
62
- Both approaches load environment variables from `.env` in the current
63
- working directory. Run `blocks publish` first to populate `BLOCKS_API_KEY`.
64
-
65
- ## agent-card.json
66
-
67
- The agent card follows the A2A specification with a `runtime` extension:
68
-
69
- ```json
70
- {
71
- "name": "My Agent",
72
- "description": "An example agent",
73
- "version": "1.0.0",
74
- "provider": { "organization": "Your Org" },
75
- "defaultInputModes": ["application/json"],
76
- "defaultOutputModes": ["text/plain"],
77
- "capabilities": { "streaming": false },
78
- "skills": [{ "id": "main", "name": "Main Skill" }],
79
- "runtime": {
80
- "handler": "./handler.ts",
81
- "handlerExport": "default",
82
- "concurrency": 1
83
- }
84
- }
85
- ```
86
-
87
- Note: `identity.agentName` must use only alphanumeric characters and
88
- underscores (no hyphens). The pattern is `^[a-zA-Z0-9_]+$`.
89
-
90
- ## Blocks Network Subscribe Strategy
91
-
92
- The Node SDK uses Blocks Network Event Engine (`enableEventEngine: true`) on all
93
- subscribing Blocks Network clients (control client, per-task client, and per-stream
94
- client). Event Engine replaces the legacy subscribe manager with a
95
- deterministic state machine for subscribe, reconnect, and retry. The SDK
96
- does not set `autoNetworkDetection` or `restore` (both are browser-only
97
- settings). No explicit `retryConfiguration` is added; the Blocks Network JS SDK
98
- applies a default exponential retry policy when Event Engine is enabled.
99
-
100
- ## API
101
-
102
- ### `await startAgentInstance(options)`
103
-
104
- Start an agent instance with full control over configuration. This is
105
- the primary runtime API.
106
-
107
- ## Authentication
108
-
109
- The SDK reads `BLOCKS_API_KEY` from the environment and uses it to
110
- authenticate with the backend. Set this in your `.env` file. The
111
- Go CLI's `blocks publish` command generates the API key and writes
112
- it to `.env` automatically. `blocks login` can also do this when
113
- invoked with `--write-env` (or by answering "y" to its prompt).
114
-
115
- PAM tokens for Blocks Network channel access are managed by the SDK at runtime
116
- (granted at registration, refreshed per-task on the control channel).
117
- No CLI involvement is needed for PAM.
118
-
119
- ## Examples
120
-
121
- For complete, runnable example agents, see the
122
- [Node examples](../../examples/node/README.md). Examples cover
123
- request/response, streaming, orchestration, pipe tasks, and advanced
124
- wrapper patterns.
125
-
126
- ## Consumer API
127
-
128
- The SDK provides a consumer-side API for submitting tasks, connecting to
129
- existing tasks, handling events, and downloading artifacts.
130
-
131
- ### `TaskClient.create(options)`
132
-
133
- Static async factory that creates a configured `TaskClient` from
134
- environment variables or CDM config.
135
-
136
- ```typescript
137
- const client = await TaskClient.create({ listing: 'playground' });
138
- ```
139
-
140
- - `listing` (required): `'playground'`, `'private'`, or `'public'`
141
- - Resolution: explicit options > `BLOCKS_*` env vars > CDM config
142
- - Auth: one of the token provider modes below
143
-
144
- ### Token Provider Modes
145
-
146
- `TaskClient.create()` supports three token provider modes for automatic
147
- token acquisition and refresh.
148
-
149
- **Mode 1: API key (server-side)**
150
-
151
- ```typescript
152
- const client = await TaskClient.create({
153
- listing: 'playground',
154
- apiKey: process.env.BLOCKS_API_KEY,
155
- onAuthError: (err) => console.error('Auth failed:', err.message),
156
- });
157
- ```
158
-
159
- The SDK exchanges the API key for a short-lived JWT via
160
- `POST /api/v1/auth/agent/consumer-token` and refreshes it
161
- automatically at 80% of its TTL. Use this for backend services, scripts,
162
- and cron jobs.
163
-
164
- **Mode 2: Token endpoint (client-side proxy)**
165
-
166
- Simplest form — a bare URL string. The SDK sends `POST <url>` with
167
- `Content-Type: application/json` and body `{}`:
168
-
169
- ```typescript
170
- const client = await TaskClient.create({
171
- listing: 'playground',
172
- tokenEndpoint: '/api/blocks-token',
173
- });
174
- ```
175
-
176
- Config-object form — pass a `TokenEndpointConfig` when your proxy
177
- needs cookies, custom headers, or a non-empty request body. Every
178
- field is optional except `url`:
179
-
180
- ```typescript
181
- import type { TokenEndpointConfig } from '@blocks-network/sdk';
182
-
183
- const tokenEndpoint: TokenEndpointConfig = {
184
- url: '/api/blocks-token',
185
- credentials: 'include', // send session cookies
186
- headers: { 'X-CSRF-Token': readCsrfMeta() }, // merged with Content-Type
187
- body: { sessionId: getCurrentSessionId() }, // replaces the default {}
188
- };
189
-
190
- const client = await TaskClient.create({
191
- listing: 'playground',
192
- tokenEndpoint,
193
- });
194
- ```
195
-
196
- `credentials` accepts any of `'include' | 'same-origin' | 'omit'`
197
- (matches `fetch`'s `RequestCredentials`). User-supplied `headers`
198
- merge on top of the SDK default `Content-Type: application/json`
199
- (user values win). `body` is JSON-serialized and replaces the default
200
- empty-object body. The same config is used for both the initial
201
- token acquisition and subsequent refreshes.
202
-
203
- The SDK sends POST requests whenever it needs a token. The endpoint
204
- identifies the caller, mints a Blocks consumer JWT, and returns
205
- `{ token, expiresIn, userId }`. The endpoint must include `userId`
206
- so `client.getUserId()` works. No long-lived credential ever reaches
207
- the client.
208
-
209
- `tokenEndpoint` has two first-class deployment shapes:
210
-
211
- 1. **Customer-owned backend proxy.** Your own service holds the
212
- Blocks API key, authenticates the browser caller however you
213
- choose (session cookie / OAuth / etc.), and forwards to the
214
- Blocks backend's `POST /api/v1/auth/agent/consumer-token`.
215
- 2. **Dashboard embedder (`afui_mvp` pattern).** The Blocks backend's
216
- own `POST /api/v1/auth/agent/consumer-token` endpoint, called
217
- directly from a signed-in dashboard with the user's session
218
- cookie plus `X-Active-Org` and `X-CSRF-Token` headers. No proxy,
219
- no API key in the browser. See `dev_docs/SDK_CONTRACT.md` §8.6.4g
220
- for the full wiring.
221
-
222
- Both shapes speak the same Mode 2 contract and are consumed
223
- uniformly by this SDK.
224
-
225
- > **Node/Python asymmetry.** `credentials` is Node-only because Python's
226
- > `urllib.request` has no equivalent of `fetch`'s credentials mode.
227
- > Python consumers pass cookies explicitly via
228
- > `headers={'Cookie': 'session=...'}`. See the
229
- > [Python README](../python/README.md) for the parity recipe.
230
-
231
- **Mode 3: Custom function**
232
-
233
- ```typescript
234
- const client = await TaskClient.create({
235
- listing: 'playground',
236
- tokenProvider: async () => {
237
- const resp = await fetch('/api/my-auth');
238
- const { token, expiresIn, userId } = await resp.json();
239
- return { token, expiresIn, userId };
240
- },
241
- });
242
- ```
243
-
244
- For OAuth2, custom SSO, or any auth architecture. The function is
245
- called on init and before each expiry.
246
-
247
- **Refresh and error handling**
248
-
249
- All modes refresh proactively at 80% TTL and reactively on HTTP 401.
250
- On 3 consecutive failures, `onAuthError` fires. The stale token
251
- remains usable until the next 401. `client.destroy()` stops the
252
- refresh timer but does not invalidate the current token.
253
-
254
- `ownerId` is auto-populated from the authenticated identity when
255
- omitted. Explicit `ownerId` still works and overrides the default.
256
- The backend rejects mismatches between `ownerId` and the
257
- authenticated identity.
258
-
259
- ### `client.connect({ taskId })`
260
-
261
- Connect to an existing task. Returns a `TaskSession` pre-populated with
262
- stream refs, artifact refs, and task state from history.
263
-
264
- ```typescript
265
- const session = await client.connect({ taskId: 'task-abc-123' });
266
- ```
267
-
268
- - Requires an authenticated `TaskClient` (for example one created with
269
- `apiKey`, `tokenEndpoint`, or `tokenProvider`). Fails with a clear
270
- error if not set.
271
- - Terminal tasks: session is not subscribed, read state via
272
- `listArtifacts()` and `session.state`.
273
- - Active tasks: session subscribes, live events flow through callbacks.
274
-
275
- ### `session.listArtifacts()`
276
-
277
- Returns all `ArtifactRef` instances seen so far (from history and live
278
- events).
279
-
280
- ```typescript
281
- const artifacts: ArtifactRef[] = session.listArtifacts();
282
- ```
283
-
284
- ### `session.downloadArtifact(ref)`
285
-
286
- Download an artifact. Handles inline (base64) and file-backed artifacts
287
- transparently.
288
-
289
- ```typescript
290
- const result: DownloadedArtifact = await session.downloadArtifact(ref);
291
- // result.data: Uint8Array, result.mimeType: string, result.fileName?: string
292
- ```
293
-
294
- Also available as a standalone function: `downloadArtifact(ref, client)`.
295
-
296
- ### `session.onError(cb)`
297
-
298
- Register a handler for callback errors. Returns an `Unsubscribe`
299
- function.
300
-
301
- ```typescript
302
- const unsub = session.onError((error, context) => {
303
- console.error(`Error in ${context.callbackType}:`, error.message);
304
- });
305
- ```
306
-
307
- `CallbackErrorContext` includes `entryPoint`, `callbackType`, and
308
- `event`. Without `onError` handlers, callback errors are logged at warn
309
- level.
310
-
311
- ### `session.waitForTerminal(timeoutMs?)`
312
-
313
- Wait for a terminal event. Returns a `Promise<TerminalEvent>`. Resolves
314
- immediately for already-terminal sessions (pre-closed idempotent hits,
315
- terminal `connect()`).
316
-
317
- ```typescript
318
- import { TaskClient, textPart } from '@blocks-network/sdk';
319
-
320
- const client = await TaskClient.create({ listing: 'playground', apiKey });
321
- const session = await client.sendMessage({
322
- agentName: 'acme_echo',
323
- requestParts: [textPart('Hello')],
324
- });
325
- // ownerId auto-populated from auth
326
- session.onProgress((e) => console.log(e.message));
327
- const terminal = await session.waitForTerminal(60_000);
328
- console.log('Completed:', terminal.state);
329
- await session.saveArtifacts('./artifacts');
330
- session.close();
331
- client.destroy();
332
- ```
333
-
334
- ### `session.saveArtifacts(dir)`
335
-
336
- Download all accumulated artifacts to a directory. Creates the directory
337
- if it does not exist. Returns `Promise<string[]>` of written file paths.
338
-
339
- ### `client.getAgentCard(agentName)`
340
-
341
- Fetch an agent's card from the registry. Returns `Promise<AgentCard | null>`.
342
-
343
- ### Part Helpers
344
-
345
- ```typescript
346
- import { textPart, filePart, filePartFromPath } from '@blocks-network/sdk';
347
-
348
- const parts = [
349
- textPart('Hello'),
350
-
351
- // Universal, browser-safe — accepts Uint8Array, ArrayBuffer, Blob, File:
352
- filePart(new Uint8Array([1, 2, 3]), { fileName: 'raw.bin' }),
353
-
354
- // Browser consumers hand a File straight from <input type="file">:
355
- // filePart(fileInput.files[0]),
356
- // Blob works too:
357
- // filePart(new Blob([bytes], { type: 'image/png' })),
358
-
359
- // Node-only — reads from disk via a lazy `node:fs` import (async):
360
- await filePartFromPath('./data.csv'),
361
- ];
362
- ```
363
-
364
- `filePart` is synchronous and has no `node:fs` dependency, so the
365
- package is safe to import from browser bundles. The legacy
366
- `filePart('./path')` signature is gone — path-based construction
367
- now lives on `filePartFromPath`, which bundlers targeting `browser`
368
- will error on because of the lazy `node:fs` import. `Buffer` values
369
- continue to work as `filePart` input at runtime because `Buffer`
370
- extends `Uint8Array`.
371
-
372
- ### Stream Consumer APIs
373
-
374
- All consumer iterators (`bytes()`, `events()`, `readable()`, `inbound`) deliver messages in sequence order. The SDK's reorder buffer transparently corrects out-of-order Blocks Network delivery and drops duplicate messages. To customize or disable reordering, pass `reorderTimeoutMs` to `open()`:
375
-
376
- ```typescript
377
- // Default: reorder buffer with 750ms gap timeout
378
- const stream = ref.open();
379
-
380
- // Custom timeout
381
- const stream = ref.open({ reorderTimeoutMs: 2000 });
382
-
383
- // Disable reordering (legacy arrival-order passthrough)
384
- const stream = ref.open({ reorderTimeoutMs: 0 });
385
- ```
386
-
387
- ```typescript
388
- // Decoded byte iterator (yields Uint8Array, browser-safe)
389
- for await (const chunk of stream.bytes()) {
390
- process.stdout.write(chunk); // Node
391
- }
392
-
393
- // Same iterator, browser-friendly text decoding
394
- const decoder = new TextDecoder();
395
- let text = '';
396
- for await (const chunk of stream.bytes()) {
397
- text += decoder.decode(chunk, { stream: true });
398
- }
399
-
400
- // Flattened event iterator (browser-safe)
401
- for await (const event of stream.events<MyEventType>()) {
402
- console.log(event);
403
- }
404
-
405
- // Node-only: Readable adapter for pipe() integration. Do not call
406
- // in browser bundles — returns a Node.js `Readable`.
407
- const readable = await stream.readable();
408
- readable.pipe(createWriteStream('./output.bin'));
409
- ```
410
-
411
- ### Handling Stream Errors
412
-
413
- Every `StreamClient` exposes an `onError(cb)` registration method.
414
- The callback fires whenever the stream's Blocks Network subscribe loop
415
- surfaces an error-category status event: PAM revocation, network
416
- issues, timeouts, or any other category the Blocks Network SDK marks as an
417
- error. The payload is a typed `StreamError`:
418
-
419
- ```typescript
420
- import type { StreamError } from '@blocks-network/sdk/stream';
421
-
422
- const stream = ref.open();
423
- stream.onError((err: StreamError) => {
424
- console.warn(
425
- `[stream] ${err.category} fatal=${err.fatal} channel=${err.channel}`,
426
- );
427
- });
428
-
429
- for await (const chunk of stream.bytes()) {
430
- process.stdout.write(chunk);
431
- }
432
- ```
433
-
434
- Two categories are **fatal** and cause the SDK to force-terminate
435
- the stream so `for await` / `for msg in ...` loops exit cleanly
436
- instead of hanging waiting for a `stream_end` that will never
437
- arrive:
438
-
439
- - `PNAccessDeniedCategory` — PAM revocation (admin-terminate,
440
- token denied). This is the signal that the server-side grant is
441
- gone even if the cached T7c's `exp` claim has not elapsed.
442
- - `PNBadRequestCategory` — auth configuration or malformed grant.
443
-
444
- All other error categories (network transients, timeouts, etc.)
445
- fire `onError` with `fatal: false` and leave the stream running so
446
- Blocks Network's built-in retry machinery can recover.
447
-
448
- ### Opening Task Streams
449
-
450
- On an active task, `StreamRef.open()` is the standard way to
451
- subscribe to a stream. Use `onStream((ref) => { const s = ref.open(); ... })`
452
- to open streams reactively as they are announced, or call
453
- `session.openAllStreams()` once to open every readable stream in one
454
- shot:
455
-
456
- ```typescript
457
- import { TaskClient, textPart } from '@blocks-network/sdk';
458
-
459
- const session = await client.sendMessage({
460
- agentName: 'multi_stream_agent',
461
- requestParts: [textPart('start')],
462
- });
463
-
464
- // Option 1 — react to each stream as it is announced
465
- session.onStream((ref) => {
466
- const stream = ref.open();
467
- void consume(stream, ref.descriptor.declaredStream);
468
- });
469
-
470
- // Option 2 — open every readable stream in one call, then branch
471
- await session.waitForStream(); // ensure at least one is announced
472
- const streams = session.openAllStreams(); // returns StreamClient[] in insertion order
473
- for (const s of streams) {
474
- void consume(s, /* whichever ref you care about */);
475
- }
476
- ```
477
-
478
- `openAllStreams()` is idempotent. Calling it again returns the same
479
- `StreamClient` objects for already-opened refs and skips outbound-only
480
- streams. It is an **active-session** convenience — it does not
481
- resurrect unopened streams after terminal; see the next section.
482
-
483
- **Drain window for already-open streams.** When the task reaches
484
- terminal, any stream that was already opened continues draining
485
- cleanly for up to **30 seconds** (raised from 2 seconds in prior
486
- versions) so consumers have time to finish iterating `for await`
487
- loops. Tune the window per session:
488
-
489
- ```typescript
490
- // Narrower window for fast-shutdown flows
491
- const session = await client.sendMessage({
492
- agentName: 'llm_streamer',
493
- requestParts: [textPart('stream please')],
494
- drainWindowMs: 5_000, // 5 seconds
495
- });
496
-
497
- // Wider window for long-tail consumers, or on connect()
498
- const resumed = await client.connect({
499
- taskId: 'task-abc',
500
- drainWindowMs: 60_000, // 60 seconds
501
- });
502
- ```
503
-
504
- The option is supported on both `sendMessage()` and `connect()`.
505
-
506
- ### Reconnecting to Terminal Tasks
507
-
508
- Stream data is **live-only** — Blocks Network does not persist stream
509
- payloads. When `client.connect({ taskId })` returns a session for a
510
- task that has already finished, a stream that was **never opened
511
- while the task was active** throws a typed `StreamUnavailableError`
512
- from `StreamRef.open()` instead of subscribing to a dead channel.
513
- `openAllStreams()` on the same session silently skips those
514
- never-opened refs:
515
-
516
- ```typescript
517
- import { TaskClient, StreamUnavailableError } from '@blocks-network/sdk';
518
-
519
- const session = await client.connect({ taskId: 'task-abc-123' });
520
-
521
- for (const ref of session.listStreams()) {
522
- try {
523
- const stream = ref.open();
524
- // ... consume stream ...
525
- } catch (err) {
526
- if (err instanceof StreamUnavailableError) {
527
- // Stream data is gone, but descriptor and artifacts remain:
528
- console.log('stream', ref.descriptor.declaredStream, err.terminalState);
529
- } else {
530
- throw err;
531
- }
532
- }
533
- }
534
-
535
- // Artifacts produced by the finished task are still available:
536
- const artifacts = session.listArtifacts();
537
- await session.saveArtifacts('./recovered');
538
- ```
539
-
540
- `StreamUnavailableError` carries named fields `taskId`, `streamId`,
541
- `declaredStream`, and `terminalState`. Inspection of
542
- `ref.descriptor` (format, metadata, declared name) continues to
543
- work on terminal-session refs without raising.
544
-
545
- > `openAllStreams()` is **not** a post-terminal reopen escape hatch.
546
- > If you want every stream opened, call it while the task is still
547
- > active (for example immediately after `session.waitForStream()` or
548
- > inside an `onStream` callback). On a terminal session it silently
549
- > returns any streams that were already active and skips the rest.
550
-
551
- ### Resource Management
552
-
553
- ```typescript
554
- // TypeScript 5.2+ using keyword
555
- {
556
- using client = await TaskClient.create({ listing: 'playground', apiKey });
557
- // client.destroy() called automatically at scope exit
558
- }
559
-
560
- {
561
- await using session = await client.sendMessage({ ... });
562
- // session.asyncClose() called automatically at scope exit
563
- }
564
- ```
565
-
566
- ## Browser Support
567
-
568
- The SDK works in modern browsers (Chrome, Firefox, Safari, Edge).
569
- Import from the package root — no special browser entrypoint needed:
570
-
571
- import { TaskClient, TaskSession, StreamRef } from '@blocks-network/sdk';
572
-
573
- The Node SDK package.json declares `engines.node >= 20.0.0` (Node 20
574
- LTS) alongside the `browser` exports field. Node 20+ has native
575
- `FormData`, `fetch`, `Blob`, `Uint8Array`, `TextEncoder`, and
576
- `TextDecoder`, which the SDK uses directly — no polyfill required on
577
- either platform.
578
-
579
- **Consumer APIs** (TaskClient, TaskSession, StreamRef, StreamClient)
580
- are browser-safe — no Node.js `Buffer` polyfill needed:
581
-
582
- - `sendMessage()` accepts `Uint8Array`, `ArrayBuffer`, `Blob`, and
583
- `File` on `requestParts[].file`. Hand a `File` from
584
- `<input type="file">` straight to `filePart(file)`; hand a `Blob`
585
- from `fetch('/somewhere').blob()` straight to `filePart(blob)`.
586
- - `uploadToStorage` (large-file path) uses native `FormData` with a
587
- `Blob` field. `fetch` computes the multipart boundary
588
- automatically — no manual `Content-Type: multipart/form-data`
589
- header, no `Buffer.concat`.
590
- - `downloadArtifact()` handles the three Blocks Network v10 download shapes
591
- (raw `Uint8Array`, `Blob`, and legacy `Blocks NetworkFile` with
592
- `toArrayBuffer()`) with typeof-guarded branches — no
593
- `Buffer.isBuffer` OR-order short-circuits.
594
- - `decodeInlineArtifact()` uses `atob` + `Uint8Array` and
595
- round-trips through `bytesToBase64` on encode, so inline artifacts
596
- decode correctly in browsers even though `Buffer` is not defined.
597
- - `filePart(data)` is synchronous and browser-safe; the file-path
598
- convenience lives on the Node-only async `filePartFromPath(path)`
599
- helper.
600
- - `stream.bytes()` yields `Uint8Array` chunks (decoded via
601
- `TextEncoder` / `atob`, no `Buffer.from`). Decode to text with
602
- `new TextDecoder().decode(chunk)`. `stream.events()` and
603
- `stream.inbound` are also browser-safe. **`stream.readable()`
604
- returns a Node.js `Readable` and is Node-only** — browser
605
- consumers should use `stream.bytes()` or `stream.inbound` instead.
606
-
607
- A jsdom-driven CI test (`tests/browser-execution.test.ts`) exercises
608
- the consumer paths end-to-end so browser regressions are caught
609
- before release. The bundle-smoke test (`tests/browser-bundle.test.ts`)
610
- continues to guard against accidental top-level `node:*` imports.
611
-
612
- **Provider APIs** (startAgentInstance) work in browsers for
613
- request-only, short-lived handlers but are not officially supported.
614
- Browser tab lifecycle (sleep, close, background throttling) makes
615
- long-lived agents unreliable.
616
-
617
- ### Configuration
618
-
619
- In browsers, use a customer-owned proxy endpoint or custom provider —
620
- the SDK does not read from `process.env` in browser environments:
621
-
622
- const client = await TaskClient.create({
623
- listing: 'playground',
624
- tokenEndpoint: {
625
- url: '/api/blocks-token',
626
- credentials: 'include', // send the app's session cookie
627
- },
628
- });
629
-
630
- ## License
631
-
632
- Blocks Network
1
+ # Placeholder
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blocks-network/sdk",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "type": "module",
5
5
  "description": "Blocks Network SDK — build and run A2A agents on Blocks Network",
6
6
  "exports": {