diff --git a/src/resources/agents/agents.ts b/src/resources/agents/agents.ts index 74d3329..eb18b98 100644 --- a/src/resources/agents/agents.ts +++ b/src/resources/agents/agents.ts @@ -106,6 +106,7 @@ import { import { APIPromise } from '../../core/api-promise'; import { RequestOptions } from '../../internal/request-options'; import { path } from '../../internal/utils/path'; +import { waitForAgentReady } from './wait-for-agent'; export class Agents extends APIResource { apiKeys: APIKeysAPI.APIKeys = new APIKeysAPI.APIKeys(this._client); @@ -266,6 +267,23 @@ export class Agents extends APIResource { ...options, }); } + + /** + * Polls for agent readiness. + * + * @example + * ```typescript + * const agent = await waitForAgentReady(client, 'agent-123', { + * timeout: 60000, // 1 minute + * }); + * ``` + */ + waitForReady( + uuid: string, + options: import('./wait-for-agent').WaitForAgentOptions, + ): Promise { + return waitForAgentReady(this._client, uuid, options); + } } /** @@ -1067,6 +1085,16 @@ export interface AgentRetrieveResponse { agent?: APIAgent; } +/** + * One Agent + */ +export interface AgentReadinessResponse { + /** + * An Agent + */ + agent?: APIAgent; +} + /** * Information about an updated agent */ @@ -1758,6 +1786,7 @@ export declare namespace Agents { type APIWorkspace as APIWorkspace, type AgentCreateResponse as AgentCreateResponse, type AgentRetrieveResponse as AgentRetrieveResponse, + type AgentReadinessResponse as AgentReadinessResponse, type AgentUpdateResponse as AgentUpdateResponse, type AgentListResponse as AgentListResponse, type AgentDeleteResponse as AgentDeleteResponse, diff --git a/src/resources/agents/wait-for-agent.ts b/src/resources/agents/wait-for-agent.ts new file mode 100644 index 0000000..693d31f --- /dev/null +++ b/src/resources/agents/wait-for-agent.ts @@ -0,0 +1,82 @@ +import { Gradient } from '../../client'; +import { RequestOptions } from '../../internal/request-options'; +import { GradientError } from '../../core/error'; +import { sleep } from '../../internal/utils'; +import { AgentReadinessResponse, APIAgent } from './agents'; + +type AgentStatus = NonNullable; + +export interface WaitForAgentOptions extends RequestOptions { + /** Check interval in ms (default: 3000) */ + interval?: number; +} + +export class AgentTimeoutError extends GradientError { + constructor( + public agentId: string, + public timeoutMs: number, + ) { + super(`Agent ${agentId} did not become ready within ${timeoutMs}ms`); + this.name = 'AgentTimeoutError'; + } +} + +export class AgentDeploymentError extends GradientError { + constructor( + public agentId: string, + public status: AgentStatus, + ) { + super(`Agent ${agentId} deployment failed with status: ${status}`); + this.name = 'AgentDeploymentError'; + } +} + +/** + * Polls an agent until it reaches STATUS_RUNNING. + * + * @example + * ```typescript + * const agent = await waitForAgentReady(client, 'agent-123', { + * timeout: 60000, // 1 minute + * }); + * ``` + * + * @param client - Gradient API client instance + * @param uuid - UUID of the agent to monitor + * @param options - Polling configuration + * @returns The agent object once STATUS_RUNNING is reached + * @throws {AgentTimeoutError} When polling exceeds timeout duration + * @throws {AgentDeploymentError} When agent enters a failure state + */ +export async function waitForAgentReady( + client: Gradient, + uuid: string, + options: WaitForAgentOptions = {}, +): Promise { + const { interval = 3000, timeout = 180000, signal } = options; + const start = Date.now(); + + while (true) { + signal?.throwIfAborted(); + + const elapsed = Date.now() - start; + + // ⏰ Check timeout BEFORE making the API call + if (elapsed > timeout) { + throw new AgentTimeoutError(uuid, timeout); + } + + const agent = await client.agents.retrieve(uuid, options); + const status = (agent.agent?.deployment?.status as AgentStatus) || 'STATUS_UNKNOWN'; + + if (status === 'STATUS_RUNNING') { + return agent; + } + + if (status === 'STATUS_FAILED' || status === 'STATUS_UNDEPLOYMENT_FAILED') { + throw new AgentDeploymentError(uuid, status); + } + + await sleep(interval); + } +}