@blocks-network/sdk 0.1.47 → 0.1.49
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/README.md +24 -13
- package/dist/runtime/agent-auth.d.ts +2 -0
- package/dist/runtime/agent-instance.d.ts +1 -0
- package/dist/runtime/agent-instance.js +1 -1
- package/dist/runtime/agent-registry.d.ts +2 -0
- package/dist/runtime/agent-registry.js +1 -1
- package/dist/runtime/channel-manager.d.ts +3 -3
- package/dist/runtime/channel-manager.js +1 -1
- package/dist/runtime/task-client.js +1 -1
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @blocks-network/sdk
|
|
2
2
|
|
|
3
|
-
Blocks Network SDK for Node.js -- build and run A2A agents on
|
|
3
|
+
Blocks Network SDK for Node.js -- build and run A2A agents on PubNub.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -87,6 +87,16 @@ The agent card follows the A2A specification with a `runtime` extension:
|
|
|
87
87
|
Note: `identity.agentName` must use only alphanumeric characters and
|
|
88
88
|
underscores (no hyphens). The pattern is `^[a-zA-Z0-9_]+$`.
|
|
89
89
|
|
|
90
|
+
## PubNub Subscribe Strategy
|
|
91
|
+
|
|
92
|
+
The Node SDK uses PubNub Event Engine (`enableEventEngine: true`) on all
|
|
93
|
+
subscribing PubNub 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 PubNub JS SDK
|
|
98
|
+
applies a default exponential retry policy when Event Engine is enabled.
|
|
99
|
+
|
|
90
100
|
## API
|
|
91
101
|
|
|
92
102
|
### `await startAgentInstance(options)`
|
|
@@ -102,7 +112,7 @@ Go CLI's `blocks publish` command generates the API key and writes
|
|
|
102
112
|
it to `.env` automatically. `blocks login` can also do this when
|
|
103
113
|
invoked with `--write-env` (or by answering "y" to its prompt).
|
|
104
114
|
|
|
105
|
-
PAM tokens for channel access are managed by the SDK at runtime
|
|
115
|
+
PAM tokens for PubNub channel access are managed by the SDK at runtime
|
|
106
116
|
(granted at registration, refreshed per-task on the control channel).
|
|
107
117
|
No CLI involvement is needed for PAM.
|
|
108
118
|
|
|
@@ -281,7 +291,7 @@ const result: DownloadedArtifact = await session.downloadArtifact(ref);
|
|
|
281
291
|
// result.data: Uint8Array, result.mimeType: string, result.fileName?: string
|
|
282
292
|
```
|
|
283
293
|
|
|
284
|
-
Also available as a standalone function: `downloadArtifact(ref,
|
|
294
|
+
Also available as a standalone function: `downloadArtifact(ref, pubnub)`.
|
|
285
295
|
|
|
286
296
|
### `session.onError(cb)`
|
|
287
297
|
|
|
@@ -361,7 +371,7 @@ extends `Uint8Array`.
|
|
|
361
371
|
|
|
362
372
|
### Stream Consumer APIs
|
|
363
373
|
|
|
364
|
-
All consumer iterators (`bytes()`, `events()`, `readable()`, `inbound`) deliver messages in sequence order. The SDK's reorder buffer transparently corrects out-of-order delivery and drops duplicate messages. To customize or disable reordering, pass `reorderTimeoutMs` to `open()`:
|
|
374
|
+
All consumer iterators (`bytes()`, `events()`, `readable()`, `inbound`) deliver messages in sequence order. The SDK's reorder buffer transparently corrects out-of-order PubNub delivery and drops duplicate messages. To customize or disable reordering, pass `reorderTimeoutMs` to `open()`:
|
|
365
375
|
|
|
366
376
|
```typescript
|
|
367
377
|
// Default: reorder buffer with 750ms gap timeout
|
|
@@ -401,9 +411,10 @@ readable.pipe(createWriteStream('./output.bin'));
|
|
|
401
411
|
### Handling Stream Errors
|
|
402
412
|
|
|
403
413
|
Every `StreamClient` exposes an `onError(cb)` registration method.
|
|
404
|
-
The callback fires whenever the stream's subscribe loop
|
|
405
|
-
error-category status event: PAM revocation, network
|
|
406
|
-
timeouts,
|
|
414
|
+
The callback fires whenever the stream's PubNub subscribe loop
|
|
415
|
+
surfaces an error-category status event: PAM revocation, network
|
|
416
|
+
issues, timeouts, or any other category the PubNub SDK marks as an
|
|
417
|
+
error. The payload is a typed `StreamError`:
|
|
407
418
|
|
|
408
419
|
```typescript
|
|
409
420
|
import type { StreamError } from '@blocks-network/sdk/stream';
|
|
@@ -432,7 +443,7 @@ arrive:
|
|
|
432
443
|
|
|
433
444
|
All other error categories (network transients, timeouts, etc.)
|
|
434
445
|
fire `onError` with `fatal: false` and leave the stream running so
|
|
435
|
-
|
|
446
|
+
PubNub's built-in retry machinery can recover.
|
|
436
447
|
|
|
437
448
|
### Opening Task Streams
|
|
438
449
|
|
|
@@ -494,8 +505,8 @@ The option is supported on both `sendMessage()` and `connect()`.
|
|
|
494
505
|
|
|
495
506
|
### Reconnecting to Terminal Tasks
|
|
496
507
|
|
|
497
|
-
Stream data is **live-only** —
|
|
498
|
-
|
|
508
|
+
Stream data is **live-only** — PubNub does not persist stream
|
|
509
|
+
payloads. When `client.connect({ taskId })` returns a session for a
|
|
499
510
|
task that has already finished, a stream that was **never opened
|
|
500
511
|
while the task was active** throws a typed `StreamUnavailableError`
|
|
501
512
|
from `StreamRef.open()` instead of subscribing to a dead channel.
|
|
@@ -576,8 +587,8 @@ are browser-safe — no Node.js `Buffer` polyfill needed:
|
|
|
576
587
|
`Blob` field. `fetch` computes the multipart boundary
|
|
577
588
|
automatically — no manual `Content-Type: multipart/form-data`
|
|
578
589
|
header, no `Buffer.concat`.
|
|
579
|
-
- `downloadArtifact()` handles
|
|
580
|
-
`Uint8Array`, `Blob`, and legacy
|
|
590
|
+
- `downloadArtifact()` handles the three PubNub v10 download shapes
|
|
591
|
+
(raw `Uint8Array`, `Blob`, and legacy `PubNubFile` with
|
|
581
592
|
`toArrayBuffer()`) with typeof-guarded branches — no
|
|
582
593
|
`Buffer.isBuffer` OR-order short-circuits.
|
|
583
594
|
- `decodeInlineArtifact()` uses `atob` + `Uint8Array` and
|
|
@@ -618,4 +629,4 @@ the SDK does not read from `process.env` in browser environments:
|
|
|
618
629
|
|
|
619
630
|
## License
|
|
620
631
|
|
|
621
|
-
|
|
632
|
+
PubNub
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{StreamClient as e}from"../stream/index.js";import{buildArtifactRef as t,shouldInlineArtifact as a,decodeInlineArtifact as r}from"./artifacts.js";import{createPubNubClient as s}from"./pubnub-client.js";import{createChannelManager as n}from"./channel-manager.js";import{toPayload as o}from"./pubnub-types.js";import{connectAgent as i,getAgent as d}from"./agent-registry.js";import{parseStreamSetupResponse as c,parseStreamSetupError as l}from"./stream-setup-helper.js";import{DEFAULTS as u}from"../defaults.js";import{getEnv as m}from"../env.js";import{uploadFile as f}from"./file-upload.js";import{StaticAuthProvider as p}from"./auth-provider.js";import{StreamRegistry as k}from"./stream-registry.js";import{CredentialCache as I}from"./credential-cache.js";import{createStreamObject as h,createExternalStreamObject as y,runOnActivate as g}from"./stream-context.js";import{TaskClient as w}from"./task-client.js";import{fetchCdmConfig as b}from"./cdm-config.js";import{AgentAuth as v,AgentAuthFatalError as T}from"./agent-auth.js";import{CURRENT_PROTOCOL_VERSION as S,SUPPORTED_PROTOCOL_VERSIONS as $,isProtocolVersionSupported as E}from"./protocol-version.js";const K=async(e,t,a,r,s,i=S)=>{const d=n(r);try{await e.publish({channel:d.taskChannel(t,a),message:o({...s,protocolVersion:i}),storeInHistory:!0,sendByPost:!0,meta:o({agentName:r,taskId:t,protocolVersion:i})})}catch{}};export const _LOG_LEVEL_ORDER={error:0,warn:1,info:2,debug:3};export function _resolveLogLevel(){const e=(m("LOG_LEVEL")??"info").toLowerCase();return _LOG_LEVEL_ORDER[e]??_LOG_LEVEL_ORDER.info}const x=(e,t,a)=>{const r=_resolveLogLevel();if((_LOG_LEVEL_ORDER[e]??_LOG_LEVEL_ORDER.info)>r)return;const s={level:e,message:t,ts:Date.now(),...a};"error"===e?console.error("[AgentInstance]",s):"warn"===e?console.warn("[AgentInstance]",s):console.log("[AgentInstance]",s)};export function resolveMaxRunningTimeSec(e,t){return void 0!==e&&void 0!==t&&e!==t&&console.info(`[AgentInstance] opts.maxRunningTimeSec (${e}) overrides card.runtime.maxRunningTimeSec (${t}).`),e??t}export function computeStreamDurationMinutes(e,t,a){return"number"==typeof e?e:t?60:Math.ceil((a??3600)/60)}const A=async(e,r,s,n,o,i,d,c,l)=>{const u={type:"artifact",taskId:r};if(c&&(u.outputId=c),a(o.length))await K(e,r,s,n,{...u,artifactRef:t({data:o,mimeType:i,fileName:d})});else{if(!l)throw new Error("Artifact size exceeds the inline threshold but no baseUrl is configured. Set baseUrl in AgentInstanceOptions to enable the pre-signed URL upload flow.");await f(l,{role:"provider-output",taskId:r,fileName:d??`${r}-artifact`,fileSize:o.length,mimeType:i,outputId:c},o)}};async function _(e,t){const a=`setup.${t.orgId}.${t.taskId}`,r={type:"stream_setup",taskId:t.taskId,orgId:t.orgId,agentName:t.agentName,streamId:t.streamId,channel:t.channel,direction:t.direction,format:t.format,taskKind:t.taskKind,durationMinutes:t.durationMinutes,affinity:t.affinity,declaredStream:t.declaredStream??"_default",protocolVersion:S};t.phase&&(r.phase=t.phase),t.metadata&&(r.metadata=t.metadata);try{throw await e.publish({channel:a,message:o(r),storeInHistory:!1,sendByPost:!0}),new Error("stream_setup publish should have been aborted by Function")}catch(e){const t=l(e);if(t)throw new Error(`Stream setup failed: [${t.code}] ${t.message}`);const a=c(e);if(a)return{token:a.token,tokenTtlMinutes:a.tokenTtlMinutes};throw e}}export const startAgentInstance=async t=>{const a=t.agentName;if(!a)throw new Error("agentName is required: provide opts.agentName");if(!/^[a-zA-Z0-9_]+$/.test(a))throw new Error("agentName must contain only alphanumeric characters and underscores (no hyphens)");const o=n(a),c=t.instanceId??`AG-${a}-${crypto.randomUUID()}`,l=t.token;let f,N=null,R="playground",C=R;if(t.pubnub){const e=t.pubnub._configuration?.keySet,a={publishKey:e?.publishKey??"",subscribeKey:e?.subscribeKey??""};f={playground:a,network:a}}else N=await b(t.cdmUrl),f={playground:N.playground,network:N.network},console.log("[AgentInstance] CDM config loaded — environment switching enabled (playground + network)");const P=m("BLOCKS_API_KEY");if(!P)throw new Error("BLOCKS_API_KEY is required. Run 'blocks publish' to set up credentials.");let M;const L=t.baseUrl??N?.api.baseUrl;let j,U;if(P&&L&&(M=new v(P,L),console.log("[AgentInstance] AgentAuth created — will authenticate via connect")),N){const e=t.baseUrl??N.api.baseUrl,r=await d(a,{baseUrl:e});r?(j=r.listing,R="playground"===j?"playground":"network",console.log(`[AgentInstance] Registry listing: ${j} — using ${R} environment`)):console.warn(`[AgentInstance] Agent "${a}" not found in registry — defaulting to playground environment`),C=R}let q=!1;if(t.pubnub)U=t.pubnub,q=!1,l&&U.setToken(l);else{const e=f[C];U=s({publishKey:e.publishKey,subscribeKey:e.subscribeKey,userId:c,presenceTimeout:20}),q=!0,l&&U.setToken(l)}let D=f[C].subscribeKey,O=f[C].publishKey;const V=new w({subscribeKey:D,authProvider:l?new p(l):void 0,baseUrl:t.baseUrl??N?.api.baseUrl,agentAuth:M,createPubNub:()=>{const e=s({publishKey:O,subscribeKey:D,userId:`${c}-taskclient`});return l&&e.setToken(l),e}}),B=t.concurrency??u.concurrency,F=t.expectedInstances??u.expectedInstances,G=t.maxPendingBacklog,z=resolveMaxRunningTimeSec(t.maxRunningTimeSec,t.card?.runtime?.maxRunningTimeSec),H=Date.now();let Y=0;const W=new k,Z=new I,J=new Map,Q=new Map,X=new Map,ee=new Map,te=new Map,ae=new Set,re=new Set,se=new Map,ne=new Set,oe=new Map;let ie;t.logChannel;const de=o.controlChannel(),ce=async()=>{if("function"==typeof U.setState)try{await U.setState({channels:[de],state:{instanceId:c,activeTasks:Y,activeStreams:W.activeStreamCount,concurrency:B,startedAt:H,preferredProtocolVersion:S,protocolVersions:[...$]}})}catch{}},le=(e,t)=>{const a=J.get(e);a&&(a.delete(t),0===a.size&&J.delete(e))},ue=async e=>{(e=>{for(const[t,a]of J)a.has(e)&&(a.delete(e),0===a.size&&J.delete(t))})(e);const t=W.releaseAllForTask(e);for(const e of t)if(e.streamClient)try{await e.streamClient.end()}catch{}},me=async(e,t)=>{le(e,t);const a=W.get(e);if(0===W.release(e,t)&&a?.streamClient){console.log("[AgentInstance]",{level:"info",message:"last-ref teardown",event:"stream_registry_last_ref_teardown",streamId:e,affinity:a.affinity,releasingTaskId:t,ts:Date.now()});try{await a.streamClient.end()}catch{}}ce().catch(()=>{})},fe=async(e,t)=>{const a=W.forceRemove(e);if((e=>{J.delete(e)})(e),a){if(a.streamClient)try{await a.streamClient.end()}catch{}for(const e of a.taskIds){const a=Z.get(e);if(a){try{const r=f[a.environment??R]??f[R],n=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c});n.setToken(a.writeToken),await K(n,e,a.orgId,a.agentName,{type:"terminal",taskId:e,state:"failed",error:t}),n.destroy()}catch{}Z.remove(e)}}await ce()}},pe=async(e,t)=>{const a=Z.get(e);if(!a)throw new Error(`No cached credentials for task ${e}`);const r=f[a.environment??R]??f[R],n=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c});n.setToken(a.writeToken);try{await K(n,e,a.orgId,a.agentName,{...t,type:"terminal",taskId:e})}finally{n.destroy()}await ue(e),Z.remove(e),await ce()},ke=async(s,n,i,d,c)=>{const u=a,m="pipe"===s.taskKind,f=m?"pipe":"request",k=computeStreamDurationMinutes(s.duration,m,z);if(m&&"number"==typeof s.durationExpiresAtMs){const e=Math.max(0,s.durationExpiresAtMs-Date.now());if(e>0){const t=setTimeout(()=>{ae.add(s.taskId),c.abort()},e);se.set(s.taskId,t)}}await K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,progress:0,state:"running"}),oe.set(s.taskId,0);let I=0,w=null,b=null;const v=()=>{if(null===w)return;const e=w;w=null,I=Date.now(),K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,message:e}).catch(()=>{})},T={taskId:s.taskId,requestParts:s.requestParts??[],reportStatus:e=>{ee.set(s.taskId,e);const t=Date.now();if(t-I>=1e3)w=null,I=t,K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,message:e}).catch(()=>{});else if(w=e,!b){b=setTimeout(()=>{b=null,v()},1e3-(t-I))}},get isCancelled(){return c.signal.aborted},get isExpired(){return ae.has(s.taskId)},hasStream:!!s.hasStream,cancelSignal:c.signal,taskClient:V,consumerPublicKey:s.consumerPublicKey,downloadInputArtifact:async e=>{const t=e.artifactRef;if(!t)throw new Error(`Request part has no artifactRef (partId: ${e.partId??"none"})`);if("inline"===t.kind)return Buffer.from(r(t));if(!t.channel||!t.fileId||!t.fileName)throw new Error("File artifactRef missing channel, fileId, or fileName");if(!n.downloadFile)throw new Error("PubNub client does not support downloadFile");const a=await n.downloadFile({channel:t.channel,id:t.fileId,name:t.fileName}),s=a?.data;if(Buffer.isBuffer(s))return s;if(s instanceof Uint8Array)return Buffer.from(s);if(s&&"function"==typeof s.arrayBuffer){const e=await s.arrayBuffer();return Buffer.from(e)}throw new Error("Unexpected downloadFile response format")},publishArtifact:async(e,t)=>{const a="string"==typeof e?Buffer.from(e,"utf-8"):e,r=t?.mimeType??"application/octet-stream",o=L?{baseUrl:L,authProvider:l?new p(l):void 0,agentAuth:M}:void 0;await A(n,s.taskId,d,u,a,r,t?.fileName,t?.outputId,o)},createStream:async a=>{if(void 0!==a&&("object"!=typeof a||null===a||Array.isArray(a)))throw new TypeError("createStream expects an options object. The positional streamId argument was removed; move any declared-stream key into CreateStreamOptions.declaredStream. See SDK_CONTRACT §8.2.1.");if(!s.hasStream)throw new Error("Streaming was not negotiated for this task. Ensure the agent card is registered with streaming capability.");const r=t.card.streams;if(!r||0===Object.keys(r).length)throw new Error("Agent card has no streams block. Streaming requires declared streams in the card.");const i=Object.keys(r);let c;if(a?.declaredStream)c=a.declaredStream;else{if(1!==i.length)throw new Error(`Card declares multiple streams (${i.join(", ")}). Specify declaredStream in CreateStreamOptions.`);c=i[0]}const l=r[c];if(!l)throw new Error(`Stream "${c}" is not declared in the agent card. Available streams: ${i.join(", ")}`);const p=a?.direction??l.direction,I=a?.format??l.format;if(a?.direction&&a.direction!==l.direction)throw new Error(`Direction "${a.direction}" conflicts with card declaration "${l.direction}" for stream "${c}".`);if(a?.format&&a.format!==l.format)throw new Error(`Format "${a.format}" conflicts with card declaration "${l.format}" for stream "${c}".`);const w=a?.external??!1,b=a?.metadata,v=a?.onActivate,T=l.affinity??"dedicated";if("shared"===T&&w)throw new Error(`Shared-affinity external streams are not supported. Declared stream '${c}' has affinity: 'shared' and createStream was called with external: true. Shared affinity requires a single SDK-managed writer with per-task ref-counting; external streams delegate the writer entirely. Use affinity: 'dedicated' with external: true, or affinity: 'shared' without external.`);if(!m){if("shared"===T)throw new Error(`Shared-affinity streams are not supported on request tasks. Declared stream '${c}' has affinity: 'shared'. Request tasks are single-shot; cross-task broadcast is inherently a pipe-task concept. Use affinity: 'dedicated' or remove 'request' from the agent card's taskKinds.`);if("outbound"!==p)throw new Error("Request tasks only support outbound streams");if(w)throw new Error("Request tasks cannot use external streams")}let S;if("shared"===T)S=c;else{const e=(oe.get(s.taskId)??0)+1;oe.set(s.taskId,e),S=`${s.taskId}-${e}`}const $=o.streamChannel(S);if("shared"===T){const e=J.get(S)?.get(s.taskId);if(e)return e}const{entry:E,isNew:K,isNewForTask:x}=W.acquire(S,s.taskId,{direction:p,format:I,external:w,affinity:T});Z.addStream(s.taskId,S);const A=e=>{if("shared"!==T)return;let t=J.get(S);t||(t=new Map,J.set(S,t)),t.set(s.taskId,e)};if(!x){E.setupPromise&&await E.setupPromise;const e=J.get(S)?.get(s.taskId);if(e)return e;if(E.streamClient){const e=h(S,E.streamClient,s.taskId,{releaseStream:me});return A(e),e}throw new Error(`Stream "${S}" same-task reacquire reached an impossible state: setup completed but no client installed. This indicates a logic error in the shared-stream handle cache.`)}if(!K)try{if(E.setupPromise&&await E.setupPromise,"shared"===T&&await(async()=>{await _(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:"activate",metadata:b,declaredStream:c})})(),E.streamClient){const e=h(S,E.streamClient,s.taskId,{releaseStream:me});return A(e),e}if(w)return y(S,$,"",async()=>{throw new Error("External stream already activated")});throw new Error(`Stream "${S}" exists but has no client`)}catch(e){le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}let N,R;E.setupPromise=new Promise((e,t)=>{N=e,R=t}),E.setupPromise.catch(()=>{});const C=w?"token_request":"embedded";let P;try{P=await _(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:C,metadata:b,declaredStream:c})}catch(e){R(e),le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}if(w){N(),E.setupPromise=null;const e=P.token??"";return y(S,$,e,async e=>{await _(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:"activate",metadata:e?.metadata??b,declaredStream:c})})}const M=P.token;if(!M){const e=new Error("No T7a token received from stream setup handshake");R(e),le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}const L=new e({subscribeKey:D,publishKey:O,token:M,agentName:u,streamId:S,channel:$,format:I,direction:p,affinity:T,gating:m,bundleSizeBytes:a?.bundleSizeBytes,maxLatencyMs:a?.maxLatencyMs});E.streamClient=L;const j=h(S,L,s.taskId,{releaseStream:me});if(A(j),N(),E.setupPromise=null,"outbound"===p||"bidirectional"===p){const e=a?.subscribeGraceMs??1e3;e>0&&await new Promise(t=>setTimeout(t,e))}return v&&(E.activated=!0,E.activatePromise=g(S,j,v,fe)),j}};let S;try{S=t.handler?await t.handler(s,T):void 0}finally{oe.delete(s.taskId),b&&(clearTimeout(b),b=null),v()}const $=L?{baseUrl:L,authProvider:l?new p(l):void 0,agentAuth:M}:void 0;if(S?.artifacts&&S.artifacts.length>0)for(const e of S.artifacts){const t="string"==typeof e.data?Buffer.from(e.data,"utf-8"):e.data;await A(n,s.taskId,d,u,t,e.mimeType,e.fileName,e.outputId,$)}const E=c.signal.aborted;if(m){const e=ae.has(s.taskId),t=re.has(s.taskId);if(E||e||t){await ue(s.taskId);const a=E&&!e?"canceled":"completed";await K(n,s.taskId,d,u,{type:"terminal",taskId:s.taskId,state:a,...e?{completionReason:"duration_expired"}:{},...t?{reason:"terminated"}:{}}),Z.remove(s.taskId),x("info",`Task ${s.taskId} ${a}`,{taskId:s.taskId,agentName:u,owner:i})}else x("info",`Task ${s.taskId} handler returned (pipe, no auto-terminal)`,{taskId:s.taskId,agentName:u,owner:i})}else{const e=re.has(s.taskId);await ue(s.taskId);const t=E?"canceled":"completed";await K(n,s.taskId,d,u,{type:"terminal",taskId:s.taskId,state:t,...e?{reason:"terminated"}:{}}),Z.remove(s.taskId),x("info",`Task ${s.taskId} ${t}`,{taskId:s.taskId,agentName:u,owner:i})}},Ie=(e,t)=>{if(x("info",`SwitchEnvironment requested: ${C} -> ${e}`,{pamToken:t?"present":"absent"}),"playground"===e&&"network"===C)return void x("error",`SwitchEnvironment rejected: cannot demote from ${C} to ${e}. Forbidden listing transition.`);if(!t)return void x("error","SwitchEnvironment rejected: pamToken is required but was absent. The backend must include a pamToken in SwitchEnvironment messages.");const a=U,r=q;try{a.removeListener(ye),a.unsubscribe({channels:[de]})}catch{}const n=f[e];if(U=s({publishKey:n.publishKey,subscribeKey:n.subscribeKey,userId:c,presenceTimeout:20}),q=!0,ie=void 0,U.setToken(t),0!==F&&"function"==typeof U.setFilterExpression&&U.setFilterExpression(`meta.instance == '${c}' || meta.broadcast == "true"`),U.addListener(ye),U.subscribe({channels:[de]}),r)try{a.destroy()}catch{}C=e,D=n.subscribeKey,O=n.publishKey,V.updateKeys(n.subscribeKey,n.publishKey),x("info",`Switched to ${e} environment`),ce().catch(()=>{})},he=(()=>{let e=!1;return t=>{if("PNAccessDeniedCategory"===t&&!e){e=!0,console.error(`[AgentInstance] PAM token expired or revoked — agent ${c} is no longer receiving tasks. Re-register the agent to resume.`);try{U.destroy()}catch{}}}})(),ye={status:e=>{const t=e,a=String(t.category??""),r=String(t.operation??"");"PNConnectedCategory"===a?console.log(`[AgentInstance] PubNub connected to ${de}`):t.error&&console.error(`[AgentInstance] PubNub status: ${a} (op=${r}, code=${t.statusCode})`),he(a)},message:e=>{const r=e.message;if(!r)return;if("SwitchEnvironment"===r.type){const e=r,t=e.environment;return void(t&&t!==C&&t in f&&Ie(t,e.pamToken))}(async(e,r)=>{if("StartTask"===e.type){const n=C,o=((e,t)=>{if("string"==typeof e&&e.length>0)return e;const a=t?.sub;return"string"==typeof a&&a.length>0?a:"anonymous"})(e.ownerId,e.callerClaims),i=e.orgId??o;if(ne.has(e.taskId))return void x("warn",`Ignoring duplicate StartTask for in-flight task ${e.taskId}`,{taskId:e.taskId});const d=Boolean(r?.broadcast);if(e.protocolVersion&&!E(e.protocolVersion))return d?void x("debug",`Ignoring broadcast StartTask ${e.taskId} with unsupported protocolVersion ${e.protocolVersion}`,{taskId:e.taskId}):(await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"unsupported_protocol_version"},e.protocolVersion),void x("warn",`Task ${e.taskId} rejected: unsupported protocolVersion ${e.protocolVersion}`,{taskId:e.taskId,instanceId:c}));if("pipe"===(e.taskKind||"request")){const t=e.duration,r=e.durationExpiresAtMs;if(null==t||!Number.isInteger(t)||t<1||t>43200||"number"!=typeof r||r<=0)return x("error",`Task ${e.taskId} rejected: pipe StartTask with invalid duration (duration=${t}, durationExpiresAtMs=${r})`,{taskId:e.taskId,instanceId:c}),void await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"invalid_start_task"},e.protocolVersion)}const l=Y+W.activeStreamCount;if(B>0&&l>=B)return d?void x("debug",`Task ${e.taskId} skipped (broadcast, at capacity ${l}/${B})`,{taskId:e.taskId,instanceId:c}):(await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"agent_at_capacity"}),void x("warn",`Task ${e.taskId} rejected: agent at capacity`,{taskId:e.taskId,instanceId:c,activeTasks:Y}));Q.set(e.taskId,o),X.set(e.taskId,i),ne.add(e.taskId),Y++,e.controlToken&&(ie=e.controlToken);const u=new AbortController;te.set(e.taskId,u);let m=null;try{const r=f[n];if(!r.publishKey||!r.subscribeKey)throw new Error("Per-task PubNub client requires valid pub/sub keys. When using an injected PubNub client, ensure it was created with publishKey and subscribeKey.");m=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c}),e.writeToken&&m.setToken(e.writeToken),Z.set(e.taskId,{ownerId:o,orgId:i,writeToken:e.writeToken??"",agentName:a,environment:n}),await ce(),t.onStartTask?await t.onStartTask(e,m):await ke(e,m,o,i,u)}catch(r){const s=r?.message??"Agent instance error",n=m??U;try{await ue(e.taskId)}catch{}await K(n,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:s}),t.onError&&await t.onError(e.taskId,r),x("error",s,{taskId:e.taskId}),Z.remove(e.taskId)}finally{const t=se.get(e.taskId);if(t&&(clearTimeout(t),se.delete(e.taskId)),ne.delete(e.taskId),Q.delete(e.taskId),X.delete(e.taskId),ee.delete(e.taskId),te.delete(e.taskId),ae.delete(e.taskId),re.delete(e.taskId),m)try{m.destroy()}catch{}Y--,0===Y&&ie&&U.setToken(ie),await ce()}}else if("CancelTask"===e.type){const t=te.get(e.taskId);t?(t.abort(),x("info",`Task ${e.taskId} cancel requested (cooperative)`,{taskId:e.taskId,instanceId:c})):Z.get(e.taskId)&&await pe(e.taskId,{state:"canceled"})}else if("ExpireTask"===e.type)te.has(e.taskId)?(ae.add(e.taskId),te.get(e.taskId)?.abort(),x("info",`Task ${e.taskId} expired`,{taskId:e.taskId,instanceId:c})):Z.get(e.taskId)&&await pe(e.taskId,{state:"completed",completionReason:"duration_expired"});else if("TerminateTask"===e.type)te.has(e.taskId)?(re.add(e.taskId),te.get(e.taskId)?.abort()):Z.get(e.taskId)&&await pe(e.taskId,{state:"canceled",reason:"terminated"});else if("PauseTask"===e.type){if(ne.has(e.taskId)){const t=X.get(e.taskId)??"anonymous";await K(U,e.taskId,t,a,{type:"system",taskId:e.taskId,status:"paused"})}}else if("ResumeTask"===e.type){if(ne.has(e.taskId)){const t=X.get(e.taskId)??"anonymous";await K(U,e.taskId,t,a,{type:"system",taskId:e.taskId,status:"resumed"})}}else"RetryTask"===e.type&&x("info","RetryTask received (no-op)",{taskId:e.taskId})})(r,e.userMetadata&&"object"==typeof e.userMetadata?e.userMetadata:void 0).catch(e=>{console.error("[AgentInstance] Unhandled error in message handler:",e)})}};U.addListener(ye),i(a,{instanceId:c,description:t.description,scaling:{expectedInstances:F,concurrency:B,maxPendingBacklog:G,maxRunningTimeSec:z},card:t.card,cardRef:t.cardRef,cardSummary:t.cardSummary,listing:j??t.listing,actor:`agent-instance:${c}`,baseUrl:t.baseUrl??N?.api.baseUrl,agentAuth:M}).then(e=>{console.log(`[AgentInstance] Registered agent: ${a} (instance: ${c})`),e.pamToken&&U.setToken(e.pamToken),0!==F&&"function"==typeof U.setFilterExpression&&U.setFilterExpression(`meta.instance == '${c}' || meta.broadcast == "true"`),U.subscribe({channels:[de]}),ce().catch(()=>{})}).catch(e=>{const t=e instanceof Error?e.message:String(e);console.error(`[AgentInstance] Failed to register agent: ${a} — ${t}`),e instanceof T&&process.exit(1)}),x("info",`Agent instance ${c} started (agent name: ${a})`,{agentName:a,event:"agent_instance_started",instanceId:c});return{stop:()=>{try{V.destroy(),U.removeListener(ye),U.unsubscribe({channels:[de]}),q&&U.destroy();for(const e of W.streamIds()){const t=W.get(e);if(t?.streamClient)try{t.streamClient.end()}catch{}}W.clear(),J.clear(),Z.clear()}catch{}},agentName:a,instanceId:c,get pubnub(){return U},get subscribeKey(){return f[C].subscribeKey},taskClient:V,publishTerminal:pe,failStream:fe,cdmConfig:N}};export const artifactUtils={buildArtifactRef:t,shouldInlineArtifact:a};
|
|
1
|
+
import{StreamClient as e}from"../stream/index.js";import{buildArtifactRef as t,shouldInlineArtifact as a,decodeInlineArtifact as r}from"./artifacts.js";import{createPubNubClient as s}from"./pubnub-client.js";import{createChannelManager as n}from"./channel-manager.js";import{toPayload as o}from"./pubnub-types.js";import{connectAgent as i,getAgent as d}from"./agent-registry.js";import{parseStreamSetupResponse as c,parseStreamSetupError as l}from"./stream-setup-helper.js";import{DEFAULTS as u}from"../defaults.js";import{getEnv as m}from"../env.js";import{uploadFile as f}from"./file-upload.js";import{StaticAuthProvider as p}from"./auth-provider.js";import{StreamRegistry as k}from"./stream-registry.js";import{CredentialCache as I}from"./credential-cache.js";import{createStreamObject as h,createExternalStreamObject as y,runOnActivate as g}from"./stream-context.js";import{TaskClient as w}from"./task-client.js";import{fetchCdmConfig as b}from"./cdm-config.js";import{AgentAuth as v,AgentAuthFatalError as T}from"./agent-auth.js";import{CURRENT_PROTOCOL_VERSION as S,SUPPORTED_PROTOCOL_VERSIONS as $,isProtocolVersionSupported as E}from"./protocol-version.js";const K=async(e,t,a,r,s,i=S)=>{const d=n(r);try{await e.publish({channel:d.taskChannel(t,a),message:o({...s,protocolVersion:i}),storeInHistory:!0,sendByPost:!0,meta:o({agentName:r,taskId:t,protocolVersion:i})})}catch{}};export const _LOG_LEVEL_ORDER={error:0,warn:1,info:2,debug:3};export function _resolveLogLevel(){const e=(m("LOG_LEVEL")??"info").toLowerCase();return _LOG_LEVEL_ORDER[e]??_LOG_LEVEL_ORDER.info}const x=(e,t,a)=>{const r=_resolveLogLevel();if((_LOG_LEVEL_ORDER[e]??_LOG_LEVEL_ORDER.info)>r)return;const s={level:e,message:t,ts:Date.now(),...a};"error"===e?console.error("[AgentInstance]",s):"warn"===e?console.warn("[AgentInstance]",s):console.log("[AgentInstance]",s)};export function resolveMaxRunningTimeSec(e,t){return void 0!==e&&void 0!==t&&e!==t&&console.info(`[AgentInstance] opts.maxRunningTimeSec (${e}) overrides card.runtime.maxRunningTimeSec (${t}).`),e??t}export function computeStreamDurationMinutes(e,t,a){return"number"==typeof e?e:t?60:Math.ceil((a??3600)/60)}const A=async(e,r,s,n,o,i,d,c,l)=>{const u={type:"artifact",taskId:r};if(c&&(u.outputId=c),a(o.length))await K(e,r,s,n,{...u,artifactRef:t({data:o,mimeType:i,fileName:d})});else{if(!l)throw new Error("Artifact size exceeds the inline threshold but no baseUrl is configured. Set baseUrl in AgentInstanceOptions to enable the pre-signed URL upload flow.");await f(l,{role:"provider-output",taskId:r,fileName:d??`${r}-artifact`,fileSize:o.length,mimeType:i,outputId:c},o)}};async function C(e,t){const a=`setup.${t.orgId}.${t.taskId}`,r={type:"stream_setup",taskId:t.taskId,orgId:t.orgId,agentName:t.agentName,streamId:t.streamId,channel:t.channel,direction:t.direction,format:t.format,taskKind:t.taskKind,durationMinutes:t.durationMinutes,affinity:t.affinity,declaredStream:t.declaredStream??"_default",protocolVersion:S};t.phase&&(r.phase=t.phase),t.metadata&&(r.metadata=t.metadata);try{throw await e.publish({channel:a,message:o(r),storeInHistory:!1,sendByPost:!0}),new Error("stream_setup publish should have been aborted by Function")}catch(e){const t=l(e);if(t)throw new Error(`Stream setup failed: [${t.code}] ${t.message}`);const a=c(e);if(a)return{token:a.token,tokenTtlMinutes:a.tokenTtlMinutes};throw e}}export const startAgentInstance=async t=>{const a=t.agentName;if(!a)throw new Error("agentName is required: provide opts.agentName");if(!/^[a-zA-Z0-9_]+$/.test(a))throw new Error("agentName must contain only alphanumeric characters and underscores (no hyphens)");const o=n(a),c=t.instanceId??`AG-${a}-${crypto.randomUUID()}`,l=t.token;let f,_=null,N="playground",R=N;if(t.pubnub){const e=t.pubnub._configuration?.keySet,a={publishKey:e?.publishKey??"",subscribeKey:e?.subscribeKey??""};f={playground:a,network:a}}else _=await b(t.cdmUrl),f={playground:_.playground,network:_.network},console.log("[AgentInstance] CDM config loaded — environment switching enabled (playground + network)");const P=m("BLOCKS_API_KEY");if(!P)throw new Error("BLOCKS_API_KEY is required. Run 'blocks publish' to set up credentials.");let M;const L=t.baseUrl??_?.api.baseUrl;let j,U;if(P&&L&&(M=new v(P,L),console.log("[AgentInstance] AgentAuth created — will authenticate via connect")),_){const e=t.baseUrl??_.api.baseUrl,r=await d(a,{baseUrl:e});r?(j=r.listing,N="playground"===j?"playground":"network",console.log(`[AgentInstance] Registry listing: ${j} — using ${N} environment`)):console.warn(`[AgentInstance] Agent "${a}" not found in registry — defaulting to playground environment`),R=N}let q=!1;if(t.pubnub)U=t.pubnub,q=!1,l&&U.setToken(l);else{const e=f[R];U=s({publishKey:e.publishKey,subscribeKey:e.subscribeKey,userId:c,presenceTimeout:20}),q=!0,l&&U.setToken(l)}let D=f[R].subscribeKey,O=f[R].publishKey;const V=new w({subscribeKey:D,authProvider:l?new p(l):void 0,baseUrl:t.baseUrl??_?.api.baseUrl,agentAuth:M,createPubNub:()=>{const e=s({publishKey:O,subscribeKey:D,userId:`${c}-taskclient`});return l&&e.setToken(l),e}}),B=t.concurrency??u.concurrency,F=t.expectedInstances??u.expectedInstances,G=t.maxPendingBacklog,z=resolveMaxRunningTimeSec(t.maxRunningTimeSec,t.card?.runtime?.maxRunningTimeSec),H=Date.now();let Y=0;const W=new k,Z=new I,J=new Map,Q=new Map,X=new Map,ee=new Map,te=new Map,ae=new Set,re=new Set,se=new Map,ne=new Set,oe=new Map;let ie,de;t.logChannel;const ce=async()=>{if("function"==typeof U.setState&&de)try{await U.setState({channels:[de],state:{instanceId:c,activeTasks:Y,activeStreams:W.activeStreamCount,concurrency:B,startedAt:H,preferredProtocolVersion:S,protocolVersions:[...$]}})}catch{}},le=(e,t)=>{const a=J.get(e);a&&(a.delete(t),0===a.size&&J.delete(e))},ue=async e=>{(e=>{for(const[t,a]of J)a.has(e)&&(a.delete(e),0===a.size&&J.delete(t))})(e);const t=W.releaseAllForTask(e);for(const e of t)if(e.streamClient)try{await e.streamClient.end()}catch{}},me=async(e,t)=>{le(e,t);const a=W.get(e);if(0===W.release(e,t)&&a?.streamClient){console.log("[AgentInstance]",{level:"info",message:"last-ref teardown",event:"stream_registry_last_ref_teardown",streamId:e,affinity:a.affinity,releasingTaskId:t,ts:Date.now()});try{await a.streamClient.end()}catch{}}ce().catch(()=>{})},fe=async(e,t)=>{const a=W.forceRemove(e);if((e=>{J.delete(e)})(e),a){if(a.streamClient)try{await a.streamClient.end()}catch{}for(const e of a.taskIds){const a=Z.get(e);if(a){try{const r=f[a.environment??N]??f[N],n=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c});n.setToken(a.writeToken),await K(n,e,a.orgId,a.agentName,{type:"terminal",taskId:e,state:"failed",error:t}),n.destroy()}catch{}Z.remove(e)}}await ce()}},pe=async(e,t)=>{const a=Z.get(e);if(!a)throw new Error(`No cached credentials for task ${e}`);const r=f[a.environment??N]??f[N],n=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c});n.setToken(a.writeToken);try{await K(n,e,a.orgId,a.agentName,{...t,type:"terminal",taskId:e})}finally{n.destroy()}await ue(e),Z.remove(e),await ce()},ke=async(s,n,i,d,c)=>{const u=a,m="pipe"===s.taskKind,f=m?"pipe":"request",k=computeStreamDurationMinutes(s.duration,m,z);if(m&&"number"==typeof s.durationExpiresAtMs){const e=Math.max(0,s.durationExpiresAtMs-Date.now());if(e>0){const t=setTimeout(()=>{ae.add(s.taskId),c.abort()},e);se.set(s.taskId,t)}}await K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,progress:0,state:"running"}),oe.set(s.taskId,0);let I=0,w=null,b=null;const v=()=>{if(null===w)return;const e=w;w=null,I=Date.now(),K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,message:e}).catch(()=>{})},T={taskId:s.taskId,requestParts:s.requestParts??[],reportStatus:e=>{ee.set(s.taskId,e);const t=Date.now();if(t-I>=1e3)w=null,I=t,K(n,s.taskId,d,u,{type:"progress",taskId:s.taskId,message:e}).catch(()=>{});else if(w=e,!b){b=setTimeout(()=>{b=null,v()},1e3-(t-I))}},get isCancelled(){return c.signal.aborted},get isExpired(){return ae.has(s.taskId)},hasStream:!!s.hasStream,cancelSignal:c.signal,taskClient:V,consumerPublicKey:s.consumerPublicKey,downloadInputArtifact:async e=>{const t=e.artifactRef;if(!t)throw new Error(`Request part has no artifactRef (partId: ${e.partId??"none"})`);if("inline"===t.kind)return Buffer.from(r(t));if(!t.channel||!t.fileId||!t.fileName)throw new Error("File artifactRef missing channel, fileId, or fileName");if(!n.downloadFile)throw new Error("PubNub client does not support downloadFile");const a=await n.downloadFile({channel:t.channel,id:t.fileId,name:t.fileName}),s=a?.data;if(Buffer.isBuffer(s))return s;if(s instanceof Uint8Array)return Buffer.from(s);if(s&&"function"==typeof s.arrayBuffer){const e=await s.arrayBuffer();return Buffer.from(e)}throw new Error("Unexpected downloadFile response format")},publishArtifact:async(e,t)=>{const a="string"==typeof e?Buffer.from(e,"utf-8"):e,r=t?.mimeType??"application/octet-stream",o=L?{baseUrl:L,authProvider:l?new p(l):void 0,agentAuth:M}:void 0;await A(n,s.taskId,d,u,a,r,t?.fileName,t?.outputId,o)},createStream:async a=>{if(void 0!==a&&("object"!=typeof a||null===a||Array.isArray(a)))throw new TypeError("createStream expects an options object. The positional streamId argument was removed; move any declared-stream key into CreateStreamOptions.declaredStream. See SDK_CONTRACT §8.2.1.");if(!s.hasStream)throw new Error("Streaming was not negotiated for this task. Ensure the agent card is registered with streaming capability.");const r=t.card.streams;if(!r||0===Object.keys(r).length)throw new Error("Agent card has no streams block. Streaming requires declared streams in the card.");const i=Object.keys(r);let c;if(a?.declaredStream)c=a.declaredStream;else{if(1!==i.length)throw new Error(`Card declares multiple streams (${i.join(", ")}). Specify declaredStream in CreateStreamOptions.`);c=i[0]}const l=r[c];if(!l)throw new Error(`Stream "${c}" is not declared in the agent card. Available streams: ${i.join(", ")}`);const p=a?.direction??l.direction,I=a?.format??l.format;if(a?.direction&&a.direction!==l.direction)throw new Error(`Direction "${a.direction}" conflicts with card declaration "${l.direction}" for stream "${c}".`);if(a?.format&&a.format!==l.format)throw new Error(`Format "${a.format}" conflicts with card declaration "${l.format}" for stream "${c}".`);const w=a?.external??!1,b=a?.metadata,v=a?.onActivate,T=l.affinity??"dedicated";if("shared"===T&&w)throw new Error(`Shared-affinity external streams are not supported. Declared stream '${c}' has affinity: 'shared' and createStream was called with external: true. Shared affinity requires a single SDK-managed writer with per-task ref-counting; external streams delegate the writer entirely. Use affinity: 'dedicated' with external: true, or affinity: 'shared' without external.`);if(!m){if("shared"===T)throw new Error(`Shared-affinity streams are not supported on request tasks. Declared stream '${c}' has affinity: 'shared'. Request tasks are single-shot; cross-task broadcast is inherently a pipe-task concept. Use affinity: 'dedicated' or remove 'request' from the agent card's taskKinds.`);if("outbound"!==p)throw new Error("Request tasks only support outbound streams");if(w)throw new Error("Request tasks cannot use external streams")}let S;if("shared"===T)S=c;else{const e=(oe.get(s.taskId)??0)+1;oe.set(s.taskId,e),S=`${s.taskId}-${e}`}const $=o.streamChannel(S);if("shared"===T){const e=J.get(S)?.get(s.taskId);if(e)return e}const{entry:E,isNew:K,isNewForTask:x}=W.acquire(S,s.taskId,{direction:p,format:I,external:w,affinity:T});Z.addStream(s.taskId,S);const A=e=>{if("shared"!==T)return;let t=J.get(S);t||(t=new Map,J.set(S,t)),t.set(s.taskId,e)};if(!x){E.setupPromise&&await E.setupPromise;const e=J.get(S)?.get(s.taskId);if(e)return e;if(E.streamClient){const e=h(S,E.streamClient,s.taskId,{releaseStream:me});return A(e),e}throw new Error(`Stream "${S}" same-task reacquire reached an impossible state: setup completed but no client installed. This indicates a logic error in the shared-stream handle cache.`)}if(!K)try{if(E.setupPromise&&await E.setupPromise,"shared"===T&&await(async()=>{await C(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:"activate",metadata:b,declaredStream:c})})(),E.streamClient){const e=h(S,E.streamClient,s.taskId,{releaseStream:me});return A(e),e}if(w)return y(S,$,"",async()=>{throw new Error("External stream already activated")});throw new Error(`Stream "${S}" exists but has no client`)}catch(e){le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}let _,N;E.setupPromise=new Promise((e,t)=>{_=e,N=t}),E.setupPromise.catch(()=>{});const R=w?"token_request":"embedded";let P;try{P=await C(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:R,metadata:b,declaredStream:c})}catch(e){N(e),le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}if(w){_(),E.setupPromise=null;const e=P.token??"";return y(S,$,e,async e=>{await C(n,{taskId:s.taskId,orgId:d,agentName:u,streamId:S,channel:$,direction:p,format:I,taskKind:f,durationMinutes:k,affinity:T,phase:"activate",metadata:e?.metadata??b,declaredStream:c})})}const M=P.token;if(!M){const e=new Error("No T7a token received from stream setup handshake");N(e),le(S,s.taskId);try{await W.release(S,s.taskId)}catch{}throw e}const L=new e({subscribeKey:D,publishKey:O,token:M,agentName:u,streamId:S,channel:$,format:I,direction:p,affinity:T,gating:m,bundleSizeBytes:a?.bundleSizeBytes,maxLatencyMs:a?.maxLatencyMs});E.streamClient=L;const j=h(S,L,s.taskId,{releaseStream:me});if(A(j),_(),E.setupPromise=null,"outbound"===p||"bidirectional"===p){const e=a?.subscribeGraceMs??1e3;e>0&&await new Promise(t=>setTimeout(t,e))}return v&&(E.activated=!0,E.activatePromise=g(S,j,v,fe)),j}};let S;try{S=t.handler?await t.handler(s,T):void 0}finally{oe.delete(s.taskId),b&&(clearTimeout(b),b=null),v()}const $=L?{baseUrl:L,authProvider:l?new p(l):void 0,agentAuth:M}:void 0;if(S?.artifacts&&S.artifacts.length>0)for(const e of S.artifacts){const t="string"==typeof e.data?Buffer.from(e.data,"utf-8"):e.data;await A(n,s.taskId,d,u,t,e.mimeType,e.fileName,e.outputId,$)}const E=c.signal.aborted;if(m){const e=ae.has(s.taskId),t=re.has(s.taskId);if(E||e||t){await ue(s.taskId);const a=E&&!e?"canceled":"completed";await K(n,s.taskId,d,u,{type:"terminal",taskId:s.taskId,state:a,...e?{completionReason:"duration_expired"}:{},...t?{reason:"terminated"}:{}}),Z.remove(s.taskId),x("info",`Task ${s.taskId} ${a}`,{taskId:s.taskId,agentName:u,owner:i})}else x("info",`Task ${s.taskId} handler returned (pipe, no auto-terminal)`,{taskId:s.taskId,agentName:u,owner:i})}else{const e=re.has(s.taskId);await ue(s.taskId);const t=E?"canceled":"completed";await K(n,s.taskId,d,u,{type:"terminal",taskId:s.taskId,state:t,...e?{reason:"terminated"}:{}}),Z.remove(s.taskId),x("info",`Task ${s.taskId} ${t}`,{taskId:s.taskId,agentName:u,owner:i})}},Ie=(e,t)=>{if(x("info",`SwitchEnvironment requested: ${R} -> ${e}`,{pamToken:t?"present":"absent"}),"playground"===e&&"network"===R)return void x("error",`SwitchEnvironment rejected: cannot demote from ${R} to ${e}. Forbidden listing transition.`);if(!t)return void x("error","SwitchEnvironment rejected: pamToken is required but was absent. The backend must include a pamToken in SwitchEnvironment messages.");const a=U,r=q;try{a.removeListener(ye),de&&a.unsubscribe({channels:[de]})}catch{}const n=f[e];if(U=s({publishKey:n.publishKey,subscribeKey:n.subscribeKey,userId:c,presenceTimeout:20}),q=!0,ie=void 0,U.setToken(t),0!==F&&"function"==typeof U.setFilterExpression&&U.setFilterExpression(`meta.instance == '${c}' || meta.broadcast == "true"`),U.addListener(ye),de&&U.subscribe({channels:[de]}),r)try{a.destroy()}catch{}R=e,D=n.subscribeKey,O=n.publishKey,V.updateKeys(n.subscribeKey,n.publishKey),x("info",`Switched to ${e} environment`),ce().catch(()=>{})},he=(()=>{let e=!1;return t=>{if("PNAccessDeniedCategory"===t&&!e){e=!0,console.error(`[AgentInstance] PAM token expired or revoked — agent ${c} is no longer receiving tasks. Re-register the agent to resume.`);try{U.destroy()}catch{}}}})(),ye={status:e=>{const t=e,a=String(t.category??""),r=String(t.operation??"");"PNConnectedCategory"===a?console.log(`[AgentInstance] PubNub connected to ${de}`):t.error&&console.error(`[AgentInstance] PubNub status: ${a} (op=${r}, code=${t.statusCode})`),he(a)},message:e=>{const r=e.message;if(!r)return;if("SwitchEnvironment"===r.type){const e=r,t=e.environment;return void(t&&t!==R&&t in f&&Ie(t,e.pamToken))}(async(e,r)=>{if("StartTask"===e.type){const n=R,o=((e,t)=>{if("string"==typeof e&&e.length>0)return e;const a=t?.sub;return"string"==typeof a&&a.length>0?a:"anonymous"})(e.ownerId,e.callerClaims),i=e.orgId??o;if(ne.has(e.taskId))return void x("warn",`Ignoring duplicate StartTask for in-flight task ${e.taskId}`,{taskId:e.taskId});const d=Boolean(r?.broadcast);if(e.protocolVersion&&!E(e.protocolVersion))return d?void x("debug",`Ignoring broadcast StartTask ${e.taskId} with unsupported protocolVersion ${e.protocolVersion}`,{taskId:e.taskId}):(await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"unsupported_protocol_version"},e.protocolVersion),void x("warn",`Task ${e.taskId} rejected: unsupported protocolVersion ${e.protocolVersion}`,{taskId:e.taskId,instanceId:c}));if("pipe"===(e.taskKind||"request")){const t=e.duration,r=e.durationExpiresAtMs;if(null==t||!Number.isInteger(t)||t<1||t>43200||"number"!=typeof r||r<=0)return x("error",`Task ${e.taskId} rejected: pipe StartTask with invalid duration (duration=${t}, durationExpiresAtMs=${r})`,{taskId:e.taskId,instanceId:c}),void await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"invalid_start_task"},e.protocolVersion)}const l=Y+W.activeStreamCount;if(B>0&&l>=B)return d?void x("debug",`Task ${e.taskId} skipped (broadcast, at capacity ${l}/${B})`,{taskId:e.taskId,instanceId:c}):(await K(U,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:"agent_at_capacity"}),void x("warn",`Task ${e.taskId} rejected: agent at capacity`,{taskId:e.taskId,instanceId:c,activeTasks:Y}));Q.set(e.taskId,o),X.set(e.taskId,i),ne.add(e.taskId),Y++,e.controlToken&&(ie=e.controlToken);const u=new AbortController;te.set(e.taskId,u);let m=null;try{const r=f[n];if(!r.publishKey||!r.subscribeKey)throw new Error("Per-task PubNub client requires valid pub/sub keys. When using an injected PubNub client, ensure it was created with publishKey and subscribeKey.");m=s({publishKey:r.publishKey,subscribeKey:r.subscribeKey,userId:c}),e.writeToken&&m.setToken(e.writeToken),Z.set(e.taskId,{ownerId:o,orgId:i,writeToken:e.writeToken??"",agentName:a,environment:n}),await ce(),t.onStartTask?await t.onStartTask(e,m):await ke(e,m,o,i,u)}catch(r){const s=r?.message??"Agent instance error",n=m??U;try{await ue(e.taskId)}catch{}await K(n,e.taskId,i,a,{type:"terminal",taskId:e.taskId,state:"failed",error:s}),t.onError&&await t.onError(e.taskId,r),x("error",s,{taskId:e.taskId}),Z.remove(e.taskId)}finally{const t=se.get(e.taskId);if(t&&(clearTimeout(t),se.delete(e.taskId)),ne.delete(e.taskId),Q.delete(e.taskId),X.delete(e.taskId),ee.delete(e.taskId),te.delete(e.taskId),ae.delete(e.taskId),re.delete(e.taskId),m)try{m.destroy()}catch{}Y--,0===Y&&ie&&U.setToken(ie),await ce()}}else if("CancelTask"===e.type){const t=te.get(e.taskId);t?(t.abort(),x("info",`Task ${e.taskId} cancel requested (cooperative)`,{taskId:e.taskId,instanceId:c})):Z.get(e.taskId)&&await pe(e.taskId,{state:"canceled"})}else if("ExpireTask"===e.type)te.has(e.taskId)?(ae.add(e.taskId),te.get(e.taskId)?.abort(),x("info",`Task ${e.taskId} expired`,{taskId:e.taskId,instanceId:c})):Z.get(e.taskId)&&await pe(e.taskId,{state:"completed",completionReason:"duration_expired"});else if("TerminateTask"===e.type)te.has(e.taskId)?(re.add(e.taskId),te.get(e.taskId)?.abort()):Z.get(e.taskId)&&await pe(e.taskId,{state:"canceled",reason:"terminated"});else if("PauseTask"===e.type){if(ne.has(e.taskId)){const t=X.get(e.taskId)??"anonymous";await K(U,e.taskId,t,a,{type:"system",taskId:e.taskId,status:"paused"})}}else if("ResumeTask"===e.type){if(ne.has(e.taskId)){const t=X.get(e.taskId)??"anonymous";await K(U,e.taskId,t,a,{type:"system",taskId:e.taskId,status:"resumed"})}}else"RetryTask"===e.type&&x("info","RetryTask received (no-op)",{taskId:e.taskId})})(r,e.userMetadata&&"object"==typeof e.userMetadata?e.userMetadata:void 0).catch(e=>{console.error("[AgentInstance] Unhandled error in message handler:",e)})}};U.addListener(ye),i(a,{instanceId:c,description:t.description,scaling:{expectedInstances:F,concurrency:B,maxPendingBacklog:G,maxRunningTimeSec:z},card:t.card,cardRef:t.cardRef,cardSummary:t.cardSummary,listing:j??t.listing,actor:`agent-instance:${c}`,baseUrl:t.baseUrl??_?.api.baseUrl,agentAuth:M}).then(e=>{if(console.log(`[AgentInstance] Registered agent: ${a} (instance: ${c})`),!e.controlChannel)throw new Error("Connect response missing controlChannel — server may be outdated");de=e.controlChannel,e.pamToken&&U.setToken(e.pamToken),0!==F&&"function"==typeof U.setFilterExpression&&U.setFilterExpression(`meta.instance == '${c}' || meta.broadcast == "true"`),U.subscribe({channels:[de]}),ce().catch(()=>{})}).catch(e=>{const t=e instanceof Error?e.message:String(e);console.error(`[AgentInstance] Failed to register agent: ${a} — ${t}`),e instanceof T&&process.exit(1)}),x("info",`Agent instance ${c} started (agent name: ${a})`,{agentName:a,event:"agent_instance_started",instanceId:c});return{stop:()=>{try{V.destroy(),U.removeListener(ye),de&&U.unsubscribe({channels:[de]}),q&&U.destroy();for(const e of W.streamIds()){const t=W.get(e);if(t?.streamClient)try{t.streamClient.end()}catch{}}W.clear(),J.clear(),Z.clear()}catch{}},agentName:a,instanceId:c,get controlChannel(){return de},get pubnub(){return U},get subscribeKey(){return f[R].subscribeKey},taskClient:V,publishTerminal:pe,failStream:fe,cdmConfig:_}};export const artifactUtils={buildArtifactRef:t,shouldInlineArtifact:a};
|
|
@@ -229,6 +229,8 @@ declare function detectDeviceOs(): string;
|
|
|
229
229
|
*/
|
|
230
230
|
export declare function connectAgent(agentName: string, options: ConnectAgentOptions): Promise<{
|
|
231
231
|
pamToken?: string;
|
|
232
|
+
agentId?: string;
|
|
233
|
+
controlChannel?: string;
|
|
232
234
|
}>;
|
|
233
235
|
/**
|
|
234
236
|
* Get a single agent by name.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import t from"node:os";import{registryAllChannel as n,registrySkillChannel as e,registryVisibilityChannel as a,registryLogChannel as r,normalizeSkillSlug as
|
|
1
|
+
import t from"node:os";import{registryAllChannel as n,registrySkillChannel as e,registryVisibilityChannel as a,registryLogChannel as r,normalizeSkillSlug as o}from"./channel-manager.js";import{getEnv as s}from"../env.js";import{CURRENT_PROTOCOL_VERSION as i,SUPPORTED_PROTOCOL_VERSIONS as c,SDK_VERSION as u,PROTOCOL_VERSION_HEADER as l}from"./protocol-version.js";import{captureAffinity as g,injectAffinity as d}from"./write-affinity.js";function m(){try{return t.platform()}catch{return"unknown"}}async function f(t,n=3,e=500){let a;for(let r=0;r<n;r++)try{return await t()}catch(t){a=t;const o=String(t);if(!(o.includes("ENOTFOUND")||o.includes("ETIMEDOUT")||o.includes("ECONNRESET")||o.includes("NetworkIssues"))||r===n-1)throw t;const s=e*Math.pow(2,r)+100*Math.random();await new Promise(t=>setTimeout(t,s))}throw a}function h(t,n){let e=`${function(t){if(t)return t.replace(/\/+$/,"");throw new Error("[AgentRegistry] baseUrl is required: provide it via options or CDM config")}(n)}/api/v1/registry/agents`;if(t){const n=new URLSearchParams;for(const[e,a]of Object.entries(t))void 0!==a&&""!==a&&n.set(e,a);const a=n.toString();a&&(e+="?"+a)}return e}async function p(t,n){const e=new Headers(n?.headers);e.has(l)||e.set(l,i);const a={};d(a);for(const[t,n]of Object.entries(a))e.set(t,n);const r=await fetch(t,{...n,headers:e});if(g(r.headers),404===r.status)return null;if(!r.ok){const t=(await r.json().catch(()=>({}))).message??`HTTP ${r.status}`;throw new Error(`[AgentRegistry] Request failed: ${t}`)}return{data:await r.json(),status:r.status}}function y(t){return{agentName:t.agentName,displayName:t.name??t.agentName,description:t.description,skills:t.skills,scaling:t.scaling,card:t.card,cardRef:t.cardRef,cardSummary:t.cardSummary,listing:t.listing??"playground",createdAt:t.registeredAt}}export async function connectAgent(t,n){if(!/^[a-zA-Z0-9_]+$/.test(t))throw new Error("agentName must contain only alphanumeric characters and underscores (no hyphens)");h(void 0,n.baseUrl);const e=s("BLOCKS_CLI_VERSION"),a={agentName:t,instanceId:n.instanceId,listing:n.listing,expectedInstances:n.scaling?.expectedInstances,concurrency:n.scaling?.concurrency,maxPendingBacklog:n.scaling?.maxPendingBacklog,maxRunningTimeSec:n.scaling?.maxRunningTimeSec,deviceOs:m(),sdkLanguage:"Node",sdkVersion:u,protocolVersions:[...c],preferredProtocolVersion:i};let r;if(e&&(a.cliVersion=e),!n.agentAuth)throw new Error("[AgentRegistry] agentAuth is required. Set BLOCKS_API_KEY and pass agentAuth to connectAgent.");const o=await f(async()=>n.agentAuth.init(a));return r=o.pamToken??void 0,{pamToken:r,agentId:o.agentId,controlChannel:o.controlChannel}}export async function getAgent(t,n){const e=h({agentName:t},n?.baseUrl),a=await f(async()=>p(e));return a?y(a.data.agent):null}export async function fetchAgentRegistry(t){const n={include:"full"};t?.limit&&(n.limit=String(t.limit)),t?.cursor&&(n.cursor=t.cursor);const e=h(n,t?.baseUrl),a=await f(async()=>p(e));return a?{agents:a.data.agents.map(y),next:a.data.next??void 0,totalCount:a.data.totalCount}:{agents:[],totalCount:0}}export async function fetchAgentsBySkill(t,n){const e={include:"full",skill:t};n?.limit&&(e.limit=String(n.limit)),n?.cursor&&(e.cursor=n.cursor);const a=h(e,n?.baseUrl),r=await f(async()=>p(a));return r?{agents:r.data.agents.map(y),next:r.data.next??void 0,totalCount:r.data.totalCount}:{agents:[],totalCount:0}}export async function fetchAgentsByListing(t,n){const e={include:"full",listing:t};n?.limit&&(e.limit=String(n.limit)),n?.cursor&&(e.cursor=n.cursor);const a=h(e,n?.baseUrl),r=await f(async()=>p(a));return r?{agents:r.data.agents.map(y),next:r.data.next??void 0,totalCount:r.data.totalCount}:{agents:[],totalCount:0}}export async function removeAgent(t,n){const e=h({agentName:t},n?.baseUrl);if(n?.agentAuth){return 404!==(await f(()=>n.agentAuth.authenticatedFetch(e,{method:"DELETE"}))).status}const a=s("BLOCKS_API_KEY");return null!==await f(async()=>p(e,{method:"DELETE",headers:{...a?{Authorization:`Bearer ${a}`}:{}}}))}export{m as detectDeviceOs,n as registryAllChannel,e as registrySkillChannel,a as registryVisibilityChannel,r as registryLogChannel,o as normalizeSkillSlug};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Channel topology (org-scoped with 3-level constraint):
|
|
5
5
|
* - u.{orgId}.{taskId} - Org-scoped task events (progress, artifacts, terminal, logs)
|
|
6
|
-
* - agent.{
|
|
6
|
+
* - agent.{agentId}.control - Control plane per agent ID
|
|
7
7
|
* - obs.{agentName}.log - Agent-level observability
|
|
8
8
|
*
|
|
9
9
|
* Registry channels (App Context membership indexes):
|
|
@@ -72,9 +72,9 @@ export declare class ChannelManager {
|
|
|
72
72
|
taskChannel(taskId: string, orgId: string): string;
|
|
73
73
|
/**
|
|
74
74
|
* Control channel for agent.
|
|
75
|
-
* Format: agent.{
|
|
75
|
+
* Format: agent.{agentId}.control (3 levels)
|
|
76
76
|
*/
|
|
77
|
-
controlChannel(
|
|
77
|
+
controlChannel(agentId: string): string;
|
|
78
78
|
/**
|
|
79
79
|
* Agent-level observability channel.
|
|
80
80
|
* Format: obs.{agentName}.log (3 levels)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const registryAllChannel=()=>"registry.all";export const registrySkillChannel=r=>`registry.skill.${normalizeSkillSlug(r)}`;export const registryVisibilityChannel=r=>r?"registry.public":"registry.private";export const registryLogChannel=()=>"registry.log";export const normalizeSkillSlug=r=>r.toLowerCase().replace(/[^a-z0-9._]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,"");const r=["agent","obs","sys","u","anonymous","system","stream"];export function validateOwnerId(e){return!(!e||"string"!=typeof e)&&!r.includes(e.toLowerCase())}export class ChannelManager{constructor(r={}){if(!r.agentName)throw new Error("agentName is required for ChannelManager");this.agentName=r.agentName}getAgentName(){return this.agentName}taskChannel(r,e){if(!e)throw new Error("orgId required for task channel");if(!r)throw new Error("taskId required for task channel");return`u.${e}.${r}`}controlChannel(r){return`agent.${r
|
|
1
|
+
export const registryAllChannel=()=>"registry.all";export const registrySkillChannel=r=>`registry.skill.${normalizeSkillSlug(r)}`;export const registryVisibilityChannel=r=>r?"registry.public":"registry.private";export const registryLogChannel=()=>"registry.log";export const normalizeSkillSlug=r=>r.toLowerCase().replace(/[^a-z0-9._]/g,"_").replace(/_+/g,"_").replace(/^_|_$/g,"");const r=["agent","obs","sys","u","anonymous","system","stream"];export function validateOwnerId(e){return!(!e||"string"!=typeof e)&&!r.includes(e.toLowerCase())}export class ChannelManager{constructor(r={}){if(!r.agentName)throw new Error("agentName is required for ChannelManager");this.agentName=r.agentName}getAgentName(){return this.agentName}taskChannel(r,e){if(!e)throw new Error("orgId required for task channel");if(!r)throw new Error("taskId required for task channel");return`u.${e}.${r}`}controlChannel(r){return`agent.${r}.control`}obsChannel(r){return`obs.${r??this.agentName}.log`}userTaskPattern(r){if(!r)throw new Error("orgId required for user task pattern");return`u.${r}.*`}agentWildcard(r){return`agent.${r??this.agentName}.*`}streamChannel(r,e){if(!r)throw new Error("streamId required for stream channel");return`stream.${e??this.agentName}.${r}`}streamWildcard(r){return`stream.${r??this.agentName}.*`}}export const createChannelManager=r=>new ChannelManager({agentName:r});export const taskChannel=(r,e)=>{if(!e)throw new Error("orgId required for task channel");if(!r)throw new Error("taskId required for task channel");return`u.${e}.${r}`};export const userTaskPattern=r=>{if(!r)throw new Error("orgId required for user task pattern");return`u.${r}.*`};export const streamChannel=(r,e)=>{if(!r)throw new Error("agentName required for stream channel");if(!e)throw new Error("streamId required for stream channel");return`stream.${r}.${e}`};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import e from"pubnub";import{callRpc as t}from"./rpc-client.js";import{taskChannel as s}from"./channel-manager.js";import{TaskSession as n}from"./task-session.js";import{ConsumerAuth as i}from"./consumer-auth.js";import{shouldInlineArtifact as r,buildArtifactRef as o}from"./artifacts.js";import{uploadFile as a}from"./file-upload.js";import{normalizeFileInput as c}from"./file-input.js";import{fetchCdmConfig as u}from"./cdm-config.js";import{captureAffinity as b,injectAffinity as d}from"./write-affinity.js";import{getEnv as l}from"../env.js";import{StreamRef as h}from"./stream-ref.js";import{invertDirection as f}from"../stream/index.js";import{asPubNubFetcher as p}from"./pubnub-types.js";import{CURRENT_PROTOCOL_VERSION as m,PROTOCOL_VERSION_HEADER as y}from"./protocol-version.js";import{getAgent as k}from"./agent-registry.js";const g=new Set(["completed","failed","canceled"]);export class TaskClient{constructor(e){this._ownsPubNub=!1,this.config={subscribeKey:e.subscribeKey,authProvider:e.authProvider,baseUrl:e.baseUrl,agentAuth:e.agentAuth},this._pubnub=e.pubnub,this._createPubNub=e.createPubNub,this._createSessionPubNub=e.createSessionPubNub,this.defaultOwnerId=e.defaultOwnerId,this._subscribeKey=e.subscribeKey,this._publishKey=e.publishKey??"",this._ownsPubNub=!e.pubnub&&!!e.createPubNub}static async create(t){const s=t??{},n=s.listing;if(!n)throw new Error("TaskClient.create() requires a listing option ('playground', 'private', or 'public') to select the CDM keyset.");const r=!!(s.apiKey||s.tokenEndpoint||s.tokenProvider);if([s.apiKey,s.tokenEndpoint,s.tokenProvider].filter(Boolean).length>1)throw new Error("Only one token provider mode may be specified");const o=s.cdmUrl??l("BLOCKS_CDM_URL"),a=await u(o),c="playground"===n?a.playground:a.network,b=s.subscribeKey??l("BLOCKS_SUBSCRIBE_KEY")??c.subscribeKey,d=s.publishKey??l("BLOCKS_PUBLISH_KEY")??c.publishKey,h=s.baseUrl??l("BLOCKS_BACKEND_URL")??a.api.baseUrl;if(!b)throw new Error("TaskClient.create() could not resolve subscribeKey from options, env, or CDM");if(!h)throw new Error("TaskClient.create() could not resolve baseUrl. Set baseUrl option, BLOCKS_BACKEND_URL env var, or ensure CDM config has api.baseUrl.");const f=()=>{const t=`blocks-task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`;return new e({subscribeKey:b,publishKey:d||void 0,userId:t,enableEventEngine:!0})};if(r){const e=new i({apiKey:s.apiKey,tokenEndpoint:s.tokenEndpoint,tokenProvider:s.tokenProvider,baseUrl:h,onAuthError:s.onAuthError});await e.init();const t=new TaskClient({subscribeKey:b,publishKey:d,baseUrl:h,createSessionPubNub:f,defaultOwnerId:e.getUserId()??void 0});return t.config.authProvider=e,t._consumerAuth=e,t}return new TaskClient({subscribeKey:b,publishKey:d,baseUrl:h,createSessionPubNub:f})}getUserId(){return this._consumerAuth?.getUserId()??null}async getAgentCard(e){const t=await k(e,{baseUrl:this.config.baseUrl});return t?.card??null}updateKeys(e,t){this.config.subscribeKey=e,this._subscribeKey=e,this._publishKey=t??""}getPubNub(){if(!this._pubnub&&this._createPubNub&&(this._pubnub=this._createPubNub()),!this._pubnub)throw new Error("TaskClient requires a pubnub instance for subscribe. Pass pubnub or createPubNub in TaskClientOptions.");return this._pubnub}destroy(){this._pubnub&&this._ownsPubNub&&(this._pubnub.destroy(),this._pubnub=void 0),this._consumerAuth&&this._consumerAuth.destroy()}[Symbol.dispose](){this.destroy()}createPerSessionPubNub(t){let s;if(this._createSessionPubNub)s=this._createSessionPubNub();else{const t=`blocks-task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`;s=new e({subscribeKey:this._subscribeKey,publishKey:this._publishKey||void 0,userId:t,enableEventEngine:!0})}return t&&s.setToken(t),s}async fetchConsumerReadToken(e){if(!this.config.baseUrl)throw new Error("connect() requires a backend baseUrl. Set baseUrl in TaskClientOptions.");const t=`${this.config.baseUrl.replace(/\/+$/,"")}/api/v1/auth/task-read-token`,s=async()=>{const s={"Content-Type":"application/json",[y]:m},n=this.config.authProvider?.getAuthHeader();return n&&(s.Authorization=n),d(s),fetch(t,{method:"POST",headers:s,body:JSON.stringify({taskId:e,role:"consumer"})})};let n=await s();if(b(n.headers),401===n.status&&this.config.authProvider){await this.config.authProvider.onAuthFailure()&&(n=await s(),b(n.headers))}if(!n.ok){const e=await n.text().catch(()=>"");throw new Error(`task-read-token failed: HTTP ${n.status}${e?` ${e}`:""}`)}return n.json()}async sendMessage(e){const s=e.ownerId||this.defaultOwnerId||"",i=e.taskKind,u=e.duration;if("pipe"===i){if(null==u||!Number.isInteger(u)||u<1||u>43200)throw new Error("Pipe tasks require a duration between 1 and 43200 minutes")}else if(null!=u)throw new Error("Request tasks must not include a duration. Duration is only valid for pipe tasks.");let b;const d=[];for(const t of e.requestParts)if(t.file){if(!t.partId)throw new Error("partId is required for file-bearing request parts");const s=c(t.file),n=t.fileName??"unnamed",i=t.contentType??"application/octet-stream";if(r(s.size)){const e=await s.getBytes(),r=o({data:e,mimeType:i,fileName:n}),{file:a,fileName:c,text:u,...b}=t;d.push({...b,artifactRef:r})}else{if(!this.config.baseUrl)throw new Error("File upload requires a backend baseUrl. Set baseUrl in TaskClientOptions.");const r={baseUrl:this.config.baseUrl,authProvider:this.config.authProvider,agentAuth:this.config.agentAuth},o={role:"consumer-input",agentName:e.agentName,fileName:n,fileSize:s.size,mimeType:i,partId:t.partId,uploadSessionId:b},c=await a(r,o,s.uploadBody);c.uploadSessionId&&(b=c.uploadSessionId);const{file:u,fileName:l,...h}=t;d.push({partId:h.partId})}}else{const{file:e,fileName:s,...n}=t;d.push(n)}const l={agentName:e.agentName,requestParts:d};b&&(l.uploadSessionId=b),e.idempotencyKey&&(l.idempotencyKey=e.idempotencyKey),l.ownerId=s;const h={};i&&(h.taskKind=i),null!=u&&(h.duration=u),e.consumerPublicKey&&(h.consumerPublicKey=e.consumerPublicKey),Object.keys(h).length>0&&(l.extensions={blocks:h}),e.pushNotificationConfig&&(l.pushNotificationConfig=e.pushNotificationConfig),e.retryPolicy&&(l.retryPolicy=e.retryPolicy);const f=await t(this.config,"SendMessage",l),p=f.extensions?.blocks,m=p?.readToken??null,y=p?.streamChannels?.status??void 0;if(!0===f.idempotent&&!!f.state&&g.has(f.state))return new n({taskId:f.taskId,ownerId:s,orgId:f.orgId??s,readToken:m,statusChannel:y,agentName:e.agentName,pubnub:null,ownsSubscribeClient:!1,sdkOptions:{subscribeKey:this._subscribeKey,publishKey:this._publishKey},rpcConfig:this.config,idempotent:f.idempotent,queued:f.queued,pushConfigId:f.pushConfigId,preClosed:!0,state:f.state});const k=this.createPerSessionPubNub(m);return new n({taskId:f.taskId,ownerId:s,orgId:f.orgId??s,readToken:m,statusChannel:y,agentName:e.agentName,pubnub:k,ownsSubscribeClient:!0,sdkOptions:{subscribeKey:this._subscribeKey,publishKey:this._publishKey},rpcConfig:this.config,idempotent:f.idempotent,queued:f.queued,pushConfigId:f.pushConfigId,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs})}async connect(e){const{taskId:t}=e;if(!this.config.authProvider?.getAuthHeader())throw new Error("connect() requires an authenticated TaskClient. Use apiKey, tokenEndpoint, or tokenProvider. AgentAuth is not supported for consumer task connections.");const s=await this.getTask(t);if(!s)throw new Error(`Task not found: ${t}`);const i=s.agentName??"",r=s.state??"",o=await this.fetchConsumerReadToken(t),{pamToken:a,channel:c}=o,u={subscribeKey:this._subscribeKey,publishKey:this._publishKey},b=this.createPerSessionPubNub(a);try{const o=await b.time(),d=String(o.timetoken),l=p(b);let m,y=new Map,k=[],w="0";if(l?.fetchMessages){const e=await async function(e,t){const s=[];let n;for(;;){const i={channels:[t],count:100};n&&(i.start=n);const r=await e.fetchMessages(i),o=r.channels?.[t]??[];if(0===o.length)break;if(s.push(...o),o.length<100)break;n=o[0].timetoken}return s.sort((e,t)=>e.timetoken<t.timetoken?-1:e.timetoken>t.timetoken?1:0),s}(l,c),s=function(e,t,s,n){const i=new Map,r=[];let o,a="0",c="0";for(const u of e){u.timetoken&&u.timetoken>a&&(a=u.timetoken);const e=u.message;if(e&&"object"==typeof e&&e.type){if("terminal"===e.type){const t=String(u.timetoken??"0");if(t>=c){const s=e.state;"completed"!==s&&"failed"!==s&&"canceled"!==s||(o=s,c=t)}}if("progress"===e.type&&"stream_started"===e.streamEvent&&e.streams){const r=e.declaredStream,o=e.streams;for(const[e,a]of Object.entries(o)){if(!a||"object"!=typeof a||i.has(e))continue;const o=a.direction,c=f(o),u=a.format;if(!u||"bytes"!==u&&"events"!==u)continue;const b=a.affinity;if("dedicated"!==b&&"shared"!==b){console.warn(`[TaskClient] history-preload: dropping stream "${e}" for task "${t}" — invalid or missing affinity (got ${JSON.stringify(a.affinity)})`);continue}const d={taskId:t,streamId:e,agentName:s,channel:a.channel,token:a.token,agentDirection:o,localDirection:c,format:u,affinity:b,metadata:a.metadata,declaredStream:r};i.set(e,new h(d,n))}}if("artifact"===e.type&&e.artifactRef){const t=e.artifactRef;t&&"object"==typeof t&&t.kind&&r.push(t)}}}return{streams:i,artifacts:r,highWaterMark:a,terminalState:o}}(e,t,i,u);y=s.streams,k=s.artifacts,w=s.highWaterMark,m=s.terminalState}const K=m&&g.has(m)?m:r;if(g.has(K))return new n({taskId:t,ownerId:s.owner??"",readToken:a,statusChannel:c,agentName:i,pubnub:b,ownsSubscribeClient:!0,sdkOptions:u,rpcConfig:this.config,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs,state:K,skipSubscription:!0,preloadedStreams:y,preloadedArtifacts:k});const P="0"!==w?w:d,T=[];let S,I=!1;const C={message:e=>{if(e.channel!==c)return;const t=e.message;if(!t||"object"!=typeof t||!t.type)return;const s=String(e.timetoken??"0");I&&S?S(t,s):T.push({message:t,timetoken:s})}};b.addListener(C),b.subscribe({channels:[c],timetoken:P});const N=new n({taskId:t,ownerId:s.owner??"",readToken:a,statusChannel:c,agentName:i,pubnub:b,ownsSubscribeClient:!0,sdkOptions:u,rpcConfig:this.config,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs,state:r,preloadedStreams:y,preloadedArtifacts:k,externalSubscription:{listener:C,channel:c,onReady:e=>{S=e}}});if(S){const e=S;for(const t of T)t.timetoken>P&&e(t.message,t.timetoken)}return T.length=0,I=!0,N}catch(e){throw b.destroy(),e}}async getTask(e){return(await t(this.config,"GetTask",{taskId:e})).task}async listTasks(e){return t(this.config,"ListTasks",{...e})}async cancelTask(e){await t(this.config,"CancelTask",{taskId:e})}async pauseTask(e){await t(this.config,"PauseTask",{taskId:e})}async resumeTask(e){await t(this.config,"ResumeTask",{taskId:e})}async retryTask(e){await t(this.config,"RetryTask",{taskId:e})}async terminateTask(e){await t(this.config,"TerminateTask",{taskId:e})}subscribeToTask(e,t,n){return function(e,t,n,i){const r=s(t,n),o={message:e=>{if(e.channel!==r)return;const t=e.message;if(t&&"object"==typeof t&&t.type){if(i.onEvent)try{i.onEvent(t)}catch(e){w(e,"onEvent",t,i.onError)}switch(t.type){case"progress":if(i.onProgress)try{i.onProgress(t)}catch(e){w(e,"onProgress",t,i.onError)}break;case"artifact":if(i.onArtifact)try{i.onArtifact(t)}catch(e){w(e,"onArtifact",t,i.onError)}break;case"terminal":if(i.onTerminal)try{i.onTerminal(t)}catch(e){w(e,"onTerminal",t,i.onError)}break;case"system":if(i.onSystem)try{i.onSystem(t)}catch(e){w(e,"onSystem",t,i.onError)}}}}};return e.addListener(o),e.subscribe({channels:[r],timetoken:1e3}),{unsubscribe(){e.removeListener(o),e.unsubscribe({channels:[r]})}}}(this.getPubNub(),e,t,n)}}function w(e,t,s,n){const i=e instanceof Error?e:new Error(String(e));if(n)try{n(i,{entryPoint:"subscribeToTask",callbackType:t,event:s})}catch{}else console.warn(`[subscribeToTask] callback error in ${t}:`,i.message)}
|
|
1
|
+
import e from"pubnub";import{callRpc as t}from"./rpc-client.js";import{taskChannel as s}from"./channel-manager.js";import{TaskSession as n}from"./task-session.js";import{ConsumerAuth as i}from"./consumer-auth.js";import{shouldInlineArtifact as r,buildArtifactRef as o}from"./artifacts.js";import{uploadFile as a}from"./file-upload.js";import{normalizeFileInput as c}from"./file-input.js";import{fetchCdmConfig as u}from"./cdm-config.js";import{captureAffinity as b,injectAffinity as d}from"./write-affinity.js";import{getEnv as l}from"../env.js";import{StreamRef as h}from"./stream-ref.js";import{invertDirection as f}from"../stream/index.js";import{asPubNubFetcher as p}from"./pubnub-types.js";import{CURRENT_PROTOCOL_VERSION as m,PROTOCOL_VERSION_HEADER as y}from"./protocol-version.js";import{getAgent as k}from"./agent-registry.js";const g=new Set(["completed","failed","canceled"]);export class TaskClient{constructor(e){this._ownsPubNub=!1,this.config={subscribeKey:e.subscribeKey,authProvider:e.authProvider,baseUrl:e.baseUrl,agentAuth:e.agentAuth},this._pubnub=e.pubnub,this._createPubNub=e.createPubNub,this._createSessionPubNub=e.createSessionPubNub,this.defaultOwnerId=e.defaultOwnerId,this._subscribeKey=e.subscribeKey,this._publishKey=e.publishKey??"",this._ownsPubNub=!e.pubnub&&!!e.createPubNub}static async create(t){const s=t??{},n=s.listing;if(!n)throw new Error("TaskClient.create() requires a listing option ('playground', 'private', or 'public') to select the CDM keyset.");const r=!!(s.apiKey||s.tokenEndpoint||s.tokenProvider);if([s.apiKey,s.tokenEndpoint,s.tokenProvider].filter(Boolean).length>1)throw new Error("Only one token provider mode may be specified");const o=s.cdmUrl??l("BLOCKS_CDM_URL"),a=await u(o),c="playground"===n?a.playground:a.network,b=s.subscribeKey??l("BLOCKS_SUBSCRIBE_KEY")??c.subscribeKey,d=s.publishKey??l("BLOCKS_PUBLISH_KEY")??c.publishKey,h=s.baseUrl??l("BLOCKS_BACKEND_URL")??a.api.baseUrl;if(!b)throw new Error("TaskClient.create() could not resolve subscribeKey from options, env, or CDM");if(!h)throw new Error("TaskClient.create() could not resolve baseUrl. Set baseUrl option, BLOCKS_BACKEND_URL env var, or ensure CDM config has api.baseUrl.");const f=()=>{const t=`blocks-task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`;return new e({subscribeKey:b,publishKey:d||void 0,userId:t,enableEventEngine:!0})};if(r){const e=new i({apiKey:s.apiKey,tokenEndpoint:s.tokenEndpoint,tokenProvider:s.tokenProvider,baseUrl:h,onAuthError:s.onAuthError});await e.init();const t=new TaskClient({subscribeKey:b,publishKey:d,baseUrl:h,createSessionPubNub:f,defaultOwnerId:e.getUserId()??void 0});return t.config.authProvider=e,t._consumerAuth=e,t}return new TaskClient({subscribeKey:b,publishKey:d,baseUrl:h,createSessionPubNub:f})}getUserId(){return this._consumerAuth?.getUserId()??null}async getAgentCard(e){const t=await k(e,{baseUrl:this.config.baseUrl});return t?.card??null}updateKeys(e,t){this.config.subscribeKey=e,this._subscribeKey=e,this._publishKey=t??""}getPubNub(){if(!this._pubnub&&this._createPubNub&&(this._pubnub=this._createPubNub()),!this._pubnub)throw new Error("TaskClient requires a pubnub instance for subscribe. Pass pubnub or createPubNub in TaskClientOptions.");return this._pubnub}destroy(){this._pubnub&&this._ownsPubNub&&(this._pubnub.destroy(),this._pubnub=void 0),this._consumerAuth&&this._consumerAuth.destroy()}[Symbol.dispose](){this.destroy()}createPerSessionPubNub(t){let s;if(this._createSessionPubNub)s=this._createSessionPubNub();else{const t=`blocks-task-${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`;s=new e({subscribeKey:this._subscribeKey,publishKey:this._publishKey||void 0,userId:t,enableEventEngine:!0})}return t&&s.setToken(t),s}async fetchConsumerReadToken(e){if(!this.config.baseUrl)throw new Error("connect() requires a backend baseUrl. Set baseUrl in TaskClientOptions.");const t=`${this.config.baseUrl.replace(/\/+$/,"")}/api/v1/auth/task-read-token`,s=async()=>{const s={"Content-Type":"application/json",[y]:m},n=this.config.authProvider?.getAuthHeader();return n&&(s.Authorization=n),d(s),fetch(t,{method:"POST",headers:s,body:JSON.stringify({taskId:e,role:"consumer"})})};let n=await s();if(b(n.headers),401===n.status&&this.config.authProvider){await this.config.authProvider.onAuthFailure()&&(n=await s(),b(n.headers))}if(!n.ok){const e=await n.text().catch(()=>"");throw new Error(`task-read-token failed: HTTP ${n.status}${e?` ${e}`:""}`)}return n.json()}async sendMessage(e){const s=e.ownerId||this.defaultOwnerId||"",i=e.taskKind,u=e.duration;if("pipe"===i){if(null==u||!Number.isInteger(u)||u<1||u>43200)throw new Error("Pipe tasks require a duration between 1 and 43200 minutes")}else if(null!=u)throw new Error("Request tasks must not include a duration. Duration is only valid for pipe tasks.");let b;const d=[];for(const t of e.requestParts)if(t.file){if(!t.partId)throw new Error("partId is required for file-bearing request parts");const s=c(t.file),n=t.fileName??"unnamed",i=t.contentType??"application/octet-stream";if(r(s.size)){const e=await s.getBytes(),r=o({data:e,mimeType:i,fileName:n}),{file:a,fileName:c,text:u,...b}=t;d.push({...b,artifactRef:r})}else{if(!this.config.baseUrl)throw new Error("File upload requires a backend baseUrl. Set baseUrl in TaskClientOptions.");const r={baseUrl:this.config.baseUrl,authProvider:this.config.authProvider,agentAuth:this.config.agentAuth},o={role:"consumer-input",agentName:e.agentName,fileName:n,fileSize:s.size,mimeType:i,partId:t.partId,uploadSessionId:b},c=await a(r,o,s.uploadBody);c.uploadSessionId&&(b=c.uploadSessionId);const{file:u,fileName:l,...h}=t;d.push({partId:h.partId,...h.contentType?{contentType:h.contentType}:{}})}}else{const{file:e,fileName:s,...n}=t;d.push(n)}const l={agentName:e.agentName,requestParts:d};b&&(l.uploadSessionId=b),e.idempotencyKey&&(l.idempotencyKey=e.idempotencyKey),l.ownerId=s;const h={};i&&(h.taskKind=i),null!=u&&(h.duration=u),e.consumerPublicKey&&(h.consumerPublicKey=e.consumerPublicKey),Object.keys(h).length>0&&(l.extensions={blocks:h}),e.pushNotificationConfig&&(l.pushNotificationConfig=e.pushNotificationConfig),e.retryPolicy&&(l.retryPolicy=e.retryPolicy);const f=await t(this.config,"SendMessage",l),p=f.extensions?.blocks,m=p?.readToken??null,y=p?.streamChannels?.status??void 0;if(!0===f.idempotent&&!!f.state&&g.has(f.state))return new n({taskId:f.taskId,ownerId:s,orgId:f.orgId??s,readToken:m,statusChannel:y,agentName:e.agentName,pubnub:null,ownsSubscribeClient:!1,sdkOptions:{subscribeKey:this._subscribeKey,publishKey:this._publishKey},rpcConfig:this.config,idempotent:f.idempotent,queued:f.queued,pushConfigId:f.pushConfigId,preClosed:!0,state:f.state});const k=this.createPerSessionPubNub(m);return new n({taskId:f.taskId,ownerId:s,orgId:f.orgId??s,readToken:m,statusChannel:y,agentName:e.agentName,pubnub:k,ownsSubscribeClient:!0,sdkOptions:{subscribeKey:this._subscribeKey,publishKey:this._publishKey},rpcConfig:this.config,idempotent:f.idempotent,queued:f.queued,pushConfigId:f.pushConfigId,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs})}async connect(e){const{taskId:t}=e;if(!this.config.authProvider?.getAuthHeader())throw new Error("connect() requires an authenticated TaskClient. Use apiKey, tokenEndpoint, or tokenProvider. AgentAuth is not supported for consumer task connections.");const s=await this.getTask(t);if(!s)throw new Error(`Task not found: ${t}`);const i=s.agentName??"",r=s.state??"",o=await this.fetchConsumerReadToken(t),{pamToken:a,channel:c}=o,u={subscribeKey:this._subscribeKey,publishKey:this._publishKey},b=this.createPerSessionPubNub(a);try{const o=await b.time(),d=String(o.timetoken),l=p(b);let m,y=new Map,k=[],w="0";if(l?.fetchMessages){const e=await async function(e,t){const s=[];let n;for(;;){const i={channels:[t],count:100};n&&(i.start=n);const r=await e.fetchMessages(i),o=r.channels?.[t]??[];if(0===o.length)break;if(s.push(...o),o.length<100)break;n=o[0].timetoken}return s.sort((e,t)=>e.timetoken<t.timetoken?-1:e.timetoken>t.timetoken?1:0),s}(l,c),s=function(e,t,s,n){const i=new Map,r=[];let o,a="0",c="0";for(const u of e){u.timetoken&&u.timetoken>a&&(a=u.timetoken);const e=u.message;if(e&&"object"==typeof e&&e.type){if("terminal"===e.type){const t=String(u.timetoken??"0");if(t>=c){const s=e.state;"completed"!==s&&"failed"!==s&&"canceled"!==s||(o=s,c=t)}}if("progress"===e.type&&"stream_started"===e.streamEvent&&e.streams){const r=e.declaredStream,o=e.streams;for(const[e,a]of Object.entries(o)){if(!a||"object"!=typeof a||i.has(e))continue;const o=a.direction,c=f(o),u=a.format;if(!u||"bytes"!==u&&"events"!==u)continue;const b=a.affinity;if("dedicated"!==b&&"shared"!==b){console.warn(`[TaskClient] history-preload: dropping stream "${e}" for task "${t}" — invalid or missing affinity (got ${JSON.stringify(a.affinity)})`);continue}const d={taskId:t,streamId:e,agentName:s,channel:a.channel,token:a.token,agentDirection:o,localDirection:c,format:u,affinity:b,metadata:a.metadata,declaredStream:r};i.set(e,new h(d,n))}}if("artifact"===e.type&&e.artifactRef){const t=e.artifactRef;t&&"object"==typeof t&&t.kind&&r.push(t)}}}return{streams:i,artifacts:r,highWaterMark:a,terminalState:o}}(e,t,i,u);y=s.streams,k=s.artifacts,w=s.highWaterMark,m=s.terminalState}const K=m&&g.has(m)?m:r;if(g.has(K))return new n({taskId:t,ownerId:s.owner??"",readToken:a,statusChannel:c,agentName:i,pubnub:b,ownsSubscribeClient:!0,sdkOptions:u,rpcConfig:this.config,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs,state:K,skipSubscription:!0,preloadedStreams:y,preloadedArtifacts:k});const T="0"!==w?w:d,P=[];let S,I=!1;const C={message:e=>{if(e.channel!==c)return;const t=e.message;if(!t||"object"!=typeof t||!t.type)return;const s=String(e.timetoken??"0");I&&S?S(t,s):P.push({message:t,timetoken:s})}};b.addListener(C),b.subscribe({channels:[c],timetoken:T});const N=new n({taskId:t,ownerId:s.owner??"",readToken:a,statusChannel:c,agentName:i,pubnub:b,ownsSubscribeClient:!0,sdkOptions:u,rpcConfig:this.config,autoDrain:e.autoDrain,drainWindowMs:e.drainWindowMs,state:r,preloadedStreams:y,preloadedArtifacts:k,externalSubscription:{listener:C,channel:c,onReady:e=>{S=e}}});if(S){const e=S;for(const t of P)t.timetoken>T&&e(t.message,t.timetoken)}return P.length=0,I=!0,N}catch(e){throw b.destroy(),e}}async getTask(e){return(await t(this.config,"GetTask",{taskId:e})).task}async listTasks(e){return t(this.config,"ListTasks",{...e})}async cancelTask(e){await t(this.config,"CancelTask",{taskId:e})}async pauseTask(e){await t(this.config,"PauseTask",{taskId:e})}async resumeTask(e){await t(this.config,"ResumeTask",{taskId:e})}async retryTask(e){await t(this.config,"RetryTask",{taskId:e})}async terminateTask(e){await t(this.config,"TerminateTask",{taskId:e})}subscribeToTask(e,t,n){return function(e,t,n,i){const r=s(t,n),o={message:e=>{if(e.channel!==r)return;const t=e.message;if(t&&"object"==typeof t&&t.type){if(i.onEvent)try{i.onEvent(t)}catch(e){w(e,"onEvent",t,i.onError)}switch(t.type){case"progress":if(i.onProgress)try{i.onProgress(t)}catch(e){w(e,"onProgress",t,i.onError)}break;case"artifact":if(i.onArtifact)try{i.onArtifact(t)}catch(e){w(e,"onArtifact",t,i.onError)}break;case"terminal":if(i.onTerminal)try{i.onTerminal(t)}catch(e){w(e,"onTerminal",t,i.onError)}break;case"system":if(i.onSystem)try{i.onSystem(t)}catch(e){w(e,"onSystem",t,i.onError)}}}}};return e.addListener(o),e.subscribe({channels:[r],timetoken:1e3}),{unsubscribe(){e.removeListener(o),e.unsubscribe({channels:[r]})}}}(this.getPubNub(),e,t,n)}}function w(e,t,s,n){const i=e instanceof Error?e:new Error(String(e));if(n)try{n(i,{entryPoint:"subscribeToTask",callbackType:t,event:s})}catch{}else console.warn(`[subscribeToTask] callback error in ${t}:`,i.message)}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocks-network/sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.49",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "Blocks Network SDK — build and run A2A agents on
|
|
5
|
+
"description": "Blocks Network SDK — build and run A2A agents on PubNub",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
8
|
"browser": {
|
|
@@ -58,14 +58,14 @@
|
|
|
58
58
|
},
|
|
59
59
|
"repository": {
|
|
60
60
|
"type": "git",
|
|
61
|
-
"url": "git+https://github.com/blocksnetwork
|
|
61
|
+
"url": "git+https://github.com/pubnub/blocksnetwork.git",
|
|
62
62
|
"directory": "blocks-sdk/sdks/node"
|
|
63
63
|
},
|
|
64
|
-
"homepage": "https://github.com/blocksnetwork/blocks-sdk/
|
|
64
|
+
"homepage": "https://github.com/pubnub/blocksnetwork/tree/main/blocks-sdk/sdks/node#readme",
|
|
65
65
|
"bugs": {
|
|
66
|
-
"url": "https://github.com/blocksnetwork/
|
|
66
|
+
"url": "https://github.com/pubnub/blocksnetwork/issues"
|
|
67
67
|
},
|
|
68
|
-
"license": "
|
|
68
|
+
"license": "PubNub",
|
|
69
69
|
"overrides": {
|
|
70
70
|
"pubnub": {
|
|
71
71
|
"react-native-url-polyfill": "npm:empty-npm-package@1.0.0"
|