@blocks-network/sdk 0.1.45
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 +632 -0
- package/dist/cli/run.d.ts +39 -0
- package/dist/cli/run.js +2 -0
- package/dist/config-loader.d.ts +10 -0
- package/dist/config-loader.js +1 -0
- package/dist/defaults.d.ts +7 -0
- package/dist/defaults.js +1 -0
- package/dist/env.d.ts +5 -0
- package/dist/env.js +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +1 -0
- package/dist/runtime/agent-auth.d.ts +94 -0
- package/dist/runtime/agent-auth.js +1 -0
- package/dist/runtime/agent-instance.d.ts +233 -0
- package/dist/runtime/agent-instance.js +1 -0
- package/dist/runtime/agent-registry.d.ts +270 -0
- package/dist/runtime/agent-registry.js +1 -0
- package/dist/runtime/artifacts.d.ts +65 -0
- package/dist/runtime/artifacts.js +1 -0
- package/dist/runtime/auth-provider.d.ts +39 -0
- package/dist/runtime/auth-provider.js +1 -0
- package/dist/runtime/cdm-config.d.ts +16 -0
- package/dist/runtime/cdm-config.js +1 -0
- package/dist/runtime/channel-manager.d.ts +125 -0
- package/dist/runtime/channel-manager.js +1 -0
- package/dist/runtime/consumer-auth.d.ts +112 -0
- package/dist/runtime/consumer-auth.js +1 -0
- package/dist/runtime/credential-cache.d.ts +40 -0
- package/dist/runtime/credential-cache.js +1 -0
- package/dist/runtime/file-input.d.ts +37 -0
- package/dist/runtime/file-input.js +1 -0
- package/dist/runtime/file-upload.d.ts +112 -0
- package/dist/runtime/file-upload.js +1 -0
- package/dist/runtime/part-helpers.d.ts +52 -0
- package/dist/runtime/part-helpers.js +1 -0
- package/dist/runtime/protocol-version.d.ts +18 -0
- package/dist/runtime/protocol-version.js +1 -0
- package/dist/runtime/pubnub-client.d.ts +8 -0
- package/dist/runtime/pubnub-client.js +1 -0
- package/dist/runtime/pubnub-types.d.ts +119 -0
- package/dist/runtime/pubnub-types.js +1 -0
- package/dist/runtime/rpc-client.d.ts +45 -0
- package/dist/runtime/rpc-client.js +1 -0
- package/dist/runtime/stream-context.d.ts +69 -0
- package/dist/runtime/stream-context.js +1 -0
- package/dist/runtime/stream-ref.d.ts +74 -0
- package/dist/runtime/stream-ref.js +1 -0
- package/dist/runtime/stream-registry.d.ts +133 -0
- package/dist/runtime/stream-registry.js +1 -0
- package/dist/runtime/stream-setup-helper.d.ts +87 -0
- package/dist/runtime/stream-setup-helper.js +1 -0
- package/dist/runtime/task-client.d.ts +259 -0
- package/dist/runtime/task-client.js +1 -0
- package/dist/runtime/task-session.d.ts +224 -0
- package/dist/runtime/task-session.js +1 -0
- package/dist/runtime/write-affinity.d.ts +22 -0
- package/dist/runtime/write-affinity.js +1 -0
- package/dist/runtime/write-affinity.test.d.ts +1 -0
- package/dist/runtime/write-affinity.test.js +1 -0
- package/dist/stream/bytes.d.ts +14 -0
- package/dist/stream/bytes.js +1 -0
- package/dist/stream/descriptor.d.ts +47 -0
- package/dist/stream/descriptor.js +1 -0
- package/dist/stream/index.d.ts +11 -0
- package/dist/stream/index.js +1 -0
- package/dist/stream/stream-bundle.d.ts +82 -0
- package/dist/stream/stream-bundle.js +1 -0
- package/dist/stream/stream-client.d.ts +192 -0
- package/dist/stream/stream-client.js +1 -0
- package/dist/stream/types.d.ts +75 -0
- package/dist/stream/types.js +1 -0
- package/dist/stream/validate.d.ts +15 -0
- package/dist/stream/validate.js +1 -0
- package/package.json +74 -0
package/dist/defaults.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DEFAULT_CDM_URL as e}from"./runtime/cdm-config.js";export const DEFAULTS={cdmUrl:e,inlineLimitBytes:16384,maxUploadBytes:26214400,concurrency:1,expectedInstances:1};
|
package/dist/env.d.ts
ADDED
package/dist/env.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function getEnv(e){if("undefined"!=typeof process&&process.env)return process.env[e]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export { startAgentInstance } from './runtime/agent-instance.js';
|
|
2
|
+
export type { ArtifactEntry, HandlerFn, HandlerResult, RequestPart, StartTaskMessage, ExpireTaskMessage, TaskContext, CreateStreamOptions, AgentInstanceOptions, AgentInstancePresenceState, AgentInstanceHandle, } from './runtime/agent-instance.js';
|
|
3
|
+
export type { StreamObject, OnActivateCallback } from './runtime/stream-context.js';
|
|
4
|
+
export type { AgentCard, OutputAgentCard, AgentSkill } from './runtime/agent-registry.js';
|
|
5
|
+
export { createPubNubClient, type PubNubClientConfig } from './runtime/pubnub-client.js';
|
|
6
|
+
export { createChannelManager, ChannelManager, streamChannel } from './runtime/channel-manager.js';
|
|
7
|
+
export { buildArtifactRef, shouldInlineArtifact, decodeInlineArtifact, downloadArtifact, type ArtifactRef, type DownloadedArtifact, } from './runtime/artifacts.js';
|
|
8
|
+
export { connectAgent, fetchAgentRegistry, getAgent, removeAgent, fetchAgentsBySkill, fetchAgentsByListing, type AgentRegistryResult, } from './runtime/agent-registry.js';
|
|
9
|
+
export { DEFAULTS } from './defaults.js';
|
|
10
|
+
/** Platform-wide upload ceiling (bytes). Mirrors afui_mvp_backend MAX_FILE_SIZE_BYTES. */
|
|
11
|
+
export declare const BLOCKS_MAX_UPLOAD_BYTES: number;
|
|
12
|
+
export { loadBlocksConfig, type BlocksConfig } from './config-loader.js';
|
|
13
|
+
export { TaskClient } from './runtime/task-client.js';
|
|
14
|
+
export type { TaskClientOptions, SendMessageParams, SendMessageRequestPart, TaskInfo, ListTasksParams, ListTasksResult, TaskEvent as TaskClientEvent, TaskEventCallbacks, TaskSubscription, } from './runtime/task-client.js';
|
|
15
|
+
export { TaskSession } from './runtime/task-session.js';
|
|
16
|
+
export type { TaskEvent, ProgressEvent, ArtifactEvent, TerminalEvent, Unsubscribe, CallbackErrorContext, } from './runtime/task-session.js';
|
|
17
|
+
export { textPart, filePart, filePartFromPath } from './runtime/part-helpers.js';
|
|
18
|
+
export { StreamRef, StreamUnavailableError } from './runtime/stream-ref.js';
|
|
19
|
+
export type { StreamDescriptor } from './runtime/stream-ref.js';
|
|
20
|
+
export { AgentAuth, AgentAuthFatalError } from './runtime/agent-auth.js';
|
|
21
|
+
export type { RegistrationPayload, RegistrationResult } from './runtime/agent-auth.js';
|
|
22
|
+
export type { AuthProvider } from './runtime/auth-provider.js';
|
|
23
|
+
export { ConsumerAuth } from './runtime/consumer-auth.js';
|
|
24
|
+
export type { ConsumerAuthOptions, TokenEndpointConfig, TokenEndpointCredentials, TokenResult, } from './runtime/consumer-auth.js';
|
|
25
|
+
export { StreamRegistry } from './runtime/stream-registry.js';
|
|
26
|
+
export { CredentialCache } from './runtime/credential-cache.js';
|
|
27
|
+
export { fetchCdmConfig, DEFAULT_CDM_URL } from './runtime/cdm-config.js';
|
|
28
|
+
export type { CdmConfig, CdmKeyset, CdmApiConfig } from './runtime/cdm-config.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{startAgentInstance}from"./runtime/agent-instance.js";export{createPubNubClient}from"./runtime/pubnub-client.js";export{createChannelManager,ChannelManager,streamChannel}from"./runtime/channel-manager.js";export{buildArtifactRef,shouldInlineArtifact,decodeInlineArtifact,downloadArtifact}from"./runtime/artifacts.js";export{connectAgent,fetchAgentRegistry,getAgent,removeAgent,fetchAgentsBySkill,fetchAgentsByListing}from"./runtime/agent-registry.js";export{DEFAULTS}from"./defaults.js";import{DEFAULTS as e}from"./defaults.js";export const BLOCKS_MAX_UPLOAD_BYTES=e.maxUploadBytes;export{loadBlocksConfig}from"./config-loader.js";export{TaskClient}from"./runtime/task-client.js";export{TaskSession}from"./runtime/task-session.js";export{textPart,filePart,filePartFromPath}from"./runtime/part-helpers.js";export{StreamRef,StreamUnavailableError}from"./runtime/stream-ref.js";export{AgentAuth,AgentAuthFatalError}from"./runtime/agent-auth.js";export{ConsumerAuth}from"./runtime/consumer-auth.js";export{StreamRegistry}from"./runtime/stream-registry.js";export{CredentialCache}from"./runtime/credential-cache.js";export{fetchCdmConfig,DEFAULT_CDM_URL}from"./runtime/cdm-config.js";
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentAuth — API key-based authentication for agent instances.
|
|
3
|
+
*
|
|
4
|
+
* Authenticates by connecting the agent via POST /api/v1/auth/agent/connect
|
|
5
|
+
* with the API key in the Bearer header. The connect response includes
|
|
6
|
+
* a short-lived JWT and refresh token. Handles transparent token refresh
|
|
7
|
+
* with Promise-based mutex to prevent concurrent refresh requests, and
|
|
8
|
+
* provides authenticatedFetch() for automatic 401 retry.
|
|
9
|
+
*
|
|
10
|
+
* On refresh token invalidation, re-connects using the stored connect
|
|
11
|
+
* payload (idempotent).
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Connect payload sent to POST /api/v1/auth/agent/connect.
|
|
15
|
+
* Contains instance-specific fields needed for runtime credential issuance.
|
|
16
|
+
*/
|
|
17
|
+
export interface RegistrationPayload {
|
|
18
|
+
agentName: string;
|
|
19
|
+
instanceId: string;
|
|
20
|
+
listing?: string;
|
|
21
|
+
expectedInstances?: number;
|
|
22
|
+
concurrency?: number;
|
|
23
|
+
maxPendingBacklog?: number;
|
|
24
|
+
maxRunningTimeSec?: number;
|
|
25
|
+
deviceOs?: string;
|
|
26
|
+
sdkLanguage?: string;
|
|
27
|
+
sdkVersion?: string;
|
|
28
|
+
cliVersion?: string;
|
|
29
|
+
protocolVersions?: string[];
|
|
30
|
+
preferredProtocolVersion?: string;
|
|
31
|
+
[key: string]: unknown;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Full response from the registration endpoint, including agent data and tokens.
|
|
35
|
+
*/
|
|
36
|
+
export interface RegistrationResult {
|
|
37
|
+
accessToken: string;
|
|
38
|
+
refreshToken: string;
|
|
39
|
+
expiresIn: number;
|
|
40
|
+
pamToken?: string;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Error thrown when the API key is permanently invalid (revoked, expired, etc.).
|
|
45
|
+
* This is a fatal error — the agent should shut down.
|
|
46
|
+
*/
|
|
47
|
+
export declare class AgentAuthFatalError extends Error {
|
|
48
|
+
constructor(message: string);
|
|
49
|
+
}
|
|
50
|
+
export declare class AgentAuth {
|
|
51
|
+
private readonly apiKey;
|
|
52
|
+
private readonly baseUrl;
|
|
53
|
+
private accessToken;
|
|
54
|
+
private refreshTokenValue;
|
|
55
|
+
private refreshLock;
|
|
56
|
+
private registrationPayload;
|
|
57
|
+
constructor(apiKey: string, baseUrl: string);
|
|
58
|
+
/**
|
|
59
|
+
* Connect the agent and obtain initial JWT + refresh token.
|
|
60
|
+
* Connect is the auth entry point for runtime credential issuance.
|
|
61
|
+
*
|
|
62
|
+
* Sends POST to /api/v1/auth/agent/connect with the API key in the
|
|
63
|
+
* Bearer header and the connect payload as JSON body.
|
|
64
|
+
* Stores the payload for re-connect on refresh token invalidation.
|
|
65
|
+
*/
|
|
66
|
+
init(registrationPayload: RegistrationPayload): Promise<RegistrationResult>;
|
|
67
|
+
/**
|
|
68
|
+
* Get the current access token (JWT). Returns null if init() has not been called.
|
|
69
|
+
*/
|
|
70
|
+
getAccessToken(): string | null;
|
|
71
|
+
/**
|
|
72
|
+
* Get the API key.
|
|
73
|
+
*/
|
|
74
|
+
getApiKey(): string;
|
|
75
|
+
/**
|
|
76
|
+
* Refresh the JWT. Uses a Promise-based mutex so concurrent callers
|
|
77
|
+
* share a single refresh request instead of racing.
|
|
78
|
+
*/
|
|
79
|
+
refresh(): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Perform the actual refresh call. On REFRESH_TOKEN_INVALID, falls back
|
|
82
|
+
* to full re-init. On API_KEY_INVALID, throws a fatal error.
|
|
83
|
+
*/
|
|
84
|
+
private _doRefresh;
|
|
85
|
+
/**
|
|
86
|
+
* Fetch wrapper that automatically attaches the Bearer token and
|
|
87
|
+
* retries once on 401 after refreshing the token. Injects the cached
|
|
88
|
+
* `X-Write-Affinity` header on outgoing requests and captures any new
|
|
89
|
+
* value from responses, so every call through AgentAuth participates
|
|
90
|
+
* in read-replica routing automatically — no per-call-site wiring
|
|
91
|
+
* required.
|
|
92
|
+
*/
|
|
93
|
+
authenticatedFetch(url: string, init?: RequestInit): Promise<Response>;
|
|
94
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{CURRENT_PROTOCOL_VERSION as e,PROTOCOL_VERSION_HEADER as t}from"./protocol-version.js";import{captureAffinity as r,injectAffinity as s}from"./write-affinity.js";export class AgentAuthFatalError extends Error{constructor(e){super(e),this.name="AgentAuthFatalError"}}export class AgentAuth{constructor(e,t){if(this.accessToken=null,this.refreshTokenValue=null,this.refreshLock=null,this.registrationPayload=null,!e)throw new Error("apiKey is required");if(!t)throw new Error("baseUrl is required");this.apiKey=e,this.baseUrl=t.replace(/\/+$/,"")}async init(a){const o=`${this.baseUrl}/api/v1/auth/agent/connect`;this.registrationPayload=a;const i={Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json",[t]:e};s(i);const n=await fetch(o,{method:"POST",headers:i,body:JSON.stringify(a)});if(r(n.headers),!n.ok){const e=await n.json().catch(()=>({}));throw new AgentAuthFatalError(`Agent registration failed: ${e.error??`HTTP ${n.status}`}`)}const h=await n.json();return this.accessToken=h.accessToken,this.refreshTokenValue=h.refreshToken,h}getAccessToken(){return this.accessToken}getApiKey(){return this.apiKey}async refresh(){if(this.refreshLock)await this.refreshLock;else{this.refreshLock=this._doRefresh();try{await this.refreshLock}finally{this.refreshLock=null}}}async _doRefresh(){const e=`${this.baseUrl}/api/v1/auth/agent/refresh`,t={Authorization:`Bearer ${this.apiKey}`,"Content-Type":"application/json"};s(t);const a=await fetch(e,{method:"POST",headers:t,body:JSON.stringify({refreshToken:this.refreshTokenValue})});if(r(a.headers),a.ok){const e=await a.json();return this.accessToken=e.accessToken,void(this.refreshTokenValue=e.refreshToken)}const o=await a.json().catch(()=>({}));if("REFRESH_TOKEN_INVALID"!==o.code){if("API_KEY_INVALID"===o.code)throw new AgentAuthFatalError(`API key invalid or revoked: ${o.error??"API_KEY_INVALID"}`);throw new Error(`Token refresh failed: ${o.error??`HTTP ${a.status}`}`)}if(!this.registrationPayload)throw new Error("Cannot re-register: no registration payload stored. Call init() with a payload first.");await this.init(this.registrationPayload)}async authenticatedFetch(a,o){const i=async i=>{const n=new Headers(o?.headers);n.set("Authorization",`Bearer ${i}`),n.has(t)||n.set(t,e);const h={};s(h);for(const[e,t]of Object.entries(h))n.set(e,t);const c=await fetch(a,{...o,headers:n});return r(c.headers),c},n=this.accessToken;if(!n)throw new Error("AgentAuth not initialized — call init() first");const h=await i(n);if(401!==h.status)return h;await this.refresh();const c=this.accessToken;if(!c)throw new Error("Failed to obtain access token after refresh");return i(c)}}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Instance Runtime - Phase 3 Three-Tier Connection Model
|
|
3
|
+
*
|
|
4
|
+
* Implements the three-tier connection architecture:
|
|
5
|
+
* - controlClient: long-lived, subscribes to control channel
|
|
6
|
+
* - taskClient: per-task, holds T2, destroyed after handler exit
|
|
7
|
+
* - streamClient: per-stream, holds T7a, managed by StreamClient from Stream SDK
|
|
8
|
+
*
|
|
9
|
+
* Key features:
|
|
10
|
+
* - Unified createStream() with direction, onActivate, metadata, external, format
|
|
11
|
+
* - Shared stream registry with ref-counting
|
|
12
|
+
* - Task credential cache for post-handler operations
|
|
13
|
+
* - Instance-level publishTerminal() and failStream()
|
|
14
|
+
* - onActivate processing model
|
|
15
|
+
* - Consumer session API (TaskSession)
|
|
16
|
+
*/
|
|
17
|
+
import PubNub from 'pubnub';
|
|
18
|
+
import { type ArtifactRef } from './artifacts.js';
|
|
19
|
+
import { type JsonValue } from './pubnub-types.js';
|
|
20
|
+
import { type AgentCard } from './agent-registry.js';
|
|
21
|
+
import { type StreamObject, type OnActivateCallback } from './stream-context.js';
|
|
22
|
+
import { TaskClient } from './task-client.js';
|
|
23
|
+
import { type CdmConfig } from './cdm-config.js';
|
|
24
|
+
/**
|
|
25
|
+
* A single artifact entry within HandlerResult.artifacts.
|
|
26
|
+
*/
|
|
27
|
+
export type ArtifactEntry = {
|
|
28
|
+
data: Buffer | string;
|
|
29
|
+
mimeType: string;
|
|
30
|
+
fileName?: string;
|
|
31
|
+
/** Declared output ID from the card's io.outputs[].id. Included in artifact events. */
|
|
32
|
+
outputId?: string;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Return type for agent task handlers.
|
|
36
|
+
*/
|
|
37
|
+
export type HandlerResult = {
|
|
38
|
+
/** Zero or more output artifacts. Published in array order before the terminal event. */
|
|
39
|
+
artifacts?: ArtifactEntry[];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Handler function signature for processing agent tasks.
|
|
43
|
+
*/
|
|
44
|
+
export type HandlerFn = (task: StartTaskMessage, ctx?: TaskContext) => Promise<HandlerResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Presence state for agent instances, used for load tracking.
|
|
47
|
+
*/
|
|
48
|
+
export interface AgentInstancePresenceState {
|
|
49
|
+
instanceId: string;
|
|
50
|
+
activeTasks: number;
|
|
51
|
+
activeStreams: number;
|
|
52
|
+
concurrency: number;
|
|
53
|
+
startedAt: number;
|
|
54
|
+
preferredProtocolVersion: string;
|
|
55
|
+
protocolVersions: string[];
|
|
56
|
+
}
|
|
57
|
+
export interface AgentInstanceOptions {
|
|
58
|
+
pubnub?: PubNub;
|
|
59
|
+
token?: string;
|
|
60
|
+
userId?: string;
|
|
61
|
+
agentName: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
onStartTask?: (task: StartTaskMessage, pubnub: PubNub) => Promise<void>;
|
|
64
|
+
onCancelTask?: (taskId: string, pubnub: PubNub) => Promise<void>;
|
|
65
|
+
artifactBasePath?: string;
|
|
66
|
+
handler?: HandlerFn;
|
|
67
|
+
onError?: (taskId: string, error: Error) => Promise<void>;
|
|
68
|
+
logChannel?: string;
|
|
69
|
+
/** @internal Used by tests only — not a public API. Always auto-generated at runtime. */
|
|
70
|
+
instanceId?: string;
|
|
71
|
+
concurrency?: number;
|
|
72
|
+
expectedInstances?: number;
|
|
73
|
+
maxPendingBacklog?: number;
|
|
74
|
+
maxRunningTimeSec?: number;
|
|
75
|
+
card: AgentCard;
|
|
76
|
+
cardRef?: string;
|
|
77
|
+
cardSummary?: string;
|
|
78
|
+
listing?: 'playground' | 'private' | 'public';
|
|
79
|
+
baseUrl?: string;
|
|
80
|
+
cdmUrl?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Request part item within a StartTask message.
|
|
84
|
+
* Each part may include a `partId` referencing a declared input in io.inputs[].id.
|
|
85
|
+
* Explicit optional fields match the Python SDK's RequestPart for cross-SDK parity.
|
|
86
|
+
* The index signature allows forward-compatible wire evolution.
|
|
87
|
+
*/
|
|
88
|
+
export interface RequestPart {
|
|
89
|
+
partId?: string;
|
|
90
|
+
text?: string;
|
|
91
|
+
contentType?: string;
|
|
92
|
+
/** Artifact reference attached by the backend (for uploaded files) or
|
|
93
|
+
* the consumer SDK (for inline files). Use ctx.downloadInputArtifact(part)
|
|
94
|
+
* to get the raw bytes. */
|
|
95
|
+
artifactRef?: ArtifactRef;
|
|
96
|
+
[key: string]: JsonValue | ArtifactRef | undefined;
|
|
97
|
+
}
|
|
98
|
+
export interface StartTaskMessage {
|
|
99
|
+
type: 'StartTask';
|
|
100
|
+
taskId: string;
|
|
101
|
+
agentName?: string;
|
|
102
|
+
ownerId: string;
|
|
103
|
+
orgId?: string;
|
|
104
|
+
taskKind?: string;
|
|
105
|
+
duration?: number;
|
|
106
|
+
durationExpiresAtMs?: number;
|
|
107
|
+
requestParts?: RequestPart[];
|
|
108
|
+
callerClaims?: Record<string, JsonValue>;
|
|
109
|
+
requestSummary?: Record<string, JsonValue>;
|
|
110
|
+
hasStream?: boolean;
|
|
111
|
+
writeToken?: string;
|
|
112
|
+
controlToken?: string;
|
|
113
|
+
/** Consumer's public key for E2E encryption (passed through from SendMessage). */
|
|
114
|
+
consumerPublicKey?: string;
|
|
115
|
+
/** Protocol version pinned at task creation. */
|
|
116
|
+
protocolVersion?: string;
|
|
117
|
+
}
|
|
118
|
+
export interface CreateStreamOptions {
|
|
119
|
+
direction?: 'outbound' | 'inbound' | 'bidirectional';
|
|
120
|
+
onActivate?: OnActivateCallback;
|
|
121
|
+
metadata?: Record<string, unknown>;
|
|
122
|
+
external?: boolean;
|
|
123
|
+
format?: 'bytes' | 'events';
|
|
124
|
+
bundleSizeBytes?: number;
|
|
125
|
+
maxLatencyMs?: number;
|
|
126
|
+
/** Key from the card's streams block. Defaults to "_default". */
|
|
127
|
+
declaredStream?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Grace period in ms to wait after stream_started is published before
|
|
130
|
+
* returning the StreamClient for outbound/bidirectional streams. Gives
|
|
131
|
+
* the consumer time to subscribe. Defaults to 1000ms. Set to 0 to skip.
|
|
132
|
+
*/
|
|
133
|
+
subscribeGraceMs?: number;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Context object passed to handlers for reporting status during execution.
|
|
137
|
+
*/
|
|
138
|
+
export interface TaskContext {
|
|
139
|
+
reportStatus: (message: string) => void;
|
|
140
|
+
taskId: string;
|
|
141
|
+
/** Typed request parts from the StartTask message (empty array when none provided). */
|
|
142
|
+
readonly requestParts: RequestPart[];
|
|
143
|
+
createStream: (options?: CreateStreamOptions) => Promise<StreamObject>;
|
|
144
|
+
taskClient: TaskClient;
|
|
145
|
+
cancelSignal: AbortSignal;
|
|
146
|
+
readonly isCancelled: boolean;
|
|
147
|
+
readonly isExpired: boolean;
|
|
148
|
+
readonly hasStream: boolean;
|
|
149
|
+
/** Consumer's public key for E2E encryption, if provided in the task submission. */
|
|
150
|
+
readonly consumerPublicKey: string | undefined;
|
|
151
|
+
/** Download the file/data from a request part's artifactRef.
|
|
152
|
+
* For inline: decodes base64 data. For file: uses pubnub.downloadFile(). */
|
|
153
|
+
downloadInputArtifact: (part: RequestPart) => Promise<Buffer>;
|
|
154
|
+
/** Publish an artifact mid-execution. Small data is inlined; large data
|
|
155
|
+
* uses the pre-signed URL upload flow (backend publishes the event). */
|
|
156
|
+
publishArtifact: (data: Buffer | string, options?: {
|
|
157
|
+
mimeType?: string;
|
|
158
|
+
fileName?: string;
|
|
159
|
+
outputId?: string;
|
|
160
|
+
}) => Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
export interface CancelTaskMessage {
|
|
163
|
+
type: 'CancelTask';
|
|
164
|
+
taskId: string;
|
|
165
|
+
reason?: string;
|
|
166
|
+
caller?: string;
|
|
167
|
+
protocolVersion?: string;
|
|
168
|
+
}
|
|
169
|
+
export interface ExpireTaskMessage {
|
|
170
|
+
type: 'ExpireTask';
|
|
171
|
+
taskId: string;
|
|
172
|
+
agentName?: string;
|
|
173
|
+
reason?: string;
|
|
174
|
+
protocolVersion?: string;
|
|
175
|
+
}
|
|
176
|
+
export interface SwitchEnvironmentMessage {
|
|
177
|
+
type: 'SwitchEnvironment';
|
|
178
|
+
environment: string;
|
|
179
|
+
pamToken?: string;
|
|
180
|
+
protocolVersion?: string;
|
|
181
|
+
}
|
|
182
|
+
export interface ControlMessage {
|
|
183
|
+
type: 'PauseTask' | 'ResumeTask' | 'RetryTask' | 'TerminateTask';
|
|
184
|
+
taskId: string;
|
|
185
|
+
caller?: string;
|
|
186
|
+
protocolVersion?: string;
|
|
187
|
+
}
|
|
188
|
+
/** Numeric precedence for LOG_LEVEL threshold filtering. */
|
|
189
|
+
export declare const _LOG_LEVEL_ORDER: Record<string, number>;
|
|
190
|
+
/** Resolve the effective LOG_LEVEL from env (default: "info"). */
|
|
191
|
+
export declare function _resolveLogLevel(): number;
|
|
192
|
+
/**
|
|
193
|
+
* Resolve the effective `maxRunningTimeSec` for an agent instance from its
|
|
194
|
+
* two possible sources: the constructor option (`opts.maxRunningTimeSec`)
|
|
195
|
+
* and the agent card's declared value (`card.runtime.maxRunningTimeSec`).
|
|
196
|
+
*
|
|
197
|
+
* Precedence is opts-first, card-fallback. When both are set and disagree,
|
|
198
|
+
* opts wins (it's an explicit programmatic override) and the SDK emits a
|
|
199
|
+
* one-time info-level log so the divergence is visible rather than silent.
|
|
200
|
+
*
|
|
201
|
+
* Exported for unit testing; callers inside this module should use the
|
|
202
|
+
* resolved `effectiveMaxRunningTimeSec` constant rather than reading either
|
|
203
|
+
* source directly.
|
|
204
|
+
*/
|
|
205
|
+
export declare function resolveMaxRunningTimeSec(optsValue: number | undefined, cardValue: number | undefined): number | undefined;
|
|
206
|
+
/**
|
|
207
|
+
* Compute the `durationMinutes` the SDK passes to the `streamSetup` Function
|
|
208
|
+
* for a given task. Pure helper with no side effects — exported for testing.
|
|
209
|
+
*
|
|
210
|
+
* Rules:
|
|
211
|
+
* - If the StartTask message carries an explicit `duration`, it wins.
|
|
212
|
+
* - Pipe tasks with no `task.duration` fall back to 60 minutes.
|
|
213
|
+
* - Request tasks with no `task.duration` derive from
|
|
214
|
+
* `effectiveMaxRunningTimeSec` via `Math.ceil(... / 60)`; when that value
|
|
215
|
+
* is absent the final fallback is 60 minutes (3600 seconds).
|
|
216
|
+
*/
|
|
217
|
+
export declare function computeStreamDurationMinutes(taskDuration: number | undefined, isPipeTask: boolean, effectiveMaxRunningTimeSec: number | undefined): number;
|
|
218
|
+
export interface AgentInstanceHandle {
|
|
219
|
+
stop: () => void;
|
|
220
|
+
agentName: string;
|
|
221
|
+
instanceId: string;
|
|
222
|
+
pubnub: PubNub;
|
|
223
|
+
subscribeKey: string;
|
|
224
|
+
taskClient: TaskClient;
|
|
225
|
+
publishTerminal: (taskId: string, event: Record<string, JsonValue>) => Promise<void>;
|
|
226
|
+
failStream: (streamId: string, reason: string) => Promise<void>;
|
|
227
|
+
cdmConfig: CdmConfig | null;
|
|
228
|
+
}
|
|
229
|
+
export declare const startAgentInstance: (opts: AgentInstanceOptions) => Promise<AgentInstanceHandle>;
|
|
230
|
+
export declare const artifactUtils: {
|
|
231
|
+
buildArtifactRef: (input: import("./artifacts.js").BuildArtifactInput) => ArtifactRef;
|
|
232
|
+
shouldInlineArtifact: (sizeBytes: number, inlineLimit?: number) => boolean;
|
|
233
|
+
};
|
|
@@ -0,0 +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};
|