import { AgentProfile } from '@tangle-network/sandbox'; /** Core types for the tcloud SDK */ interface TCloudConfig { /** API base URL (default: https://router.tangle.tools/v1) */ baseURL?: string; /** Platform API URL for billing/keys (default: https://id.tangle.tools) */ platformURL?: string; /** API key for standard (non-private) mode */ apiKey?: string; /** Default model */ model?: string; /** Operator routing preferences */ routing?: RoutingConfig; /** Enable shielded (private) mode */ shielded?: ShieldedConfig | boolean; /** Privacy proxy configuration for IP hiding */ privacy?: PrivacyConfig; /** Spending limits and metering */ limits?: SpendingLimits; /** Retry configuration for transient failures */ retry?: RetryConfig | false; /** Default request timeout in ms (default: 60000). Set 0 to disable. */ timeout?: number; } interface RetryConfig { /** Max retry attempts (default: 3) */ maxRetries?: number; /** Initial backoff in ms (default: 500) */ initialBackoffMs?: number; /** Max backoff in ms (default: 30000) */ maxBackoffMs?: number; /** Backoff multiplier (default: 2) */ multiplier?: number; /** HTTP status codes that trigger retry (default: [429, 500, 502, 503, 504]) */ retryableStatuses?: number[]; } interface SpendingLimits { /** Max USD to spend per request. Rejects if estimated cost exceeds this. */ maxCostPerRequest?: number; /** Max USD to spend across all requests in this client's lifetime. Stops at limit. */ maxTotalSpend?: number; /** Max requests allowed. Stops at limit. */ maxRequests?: number; /** Callback when a limit is approached (80% threshold) */ onLimitWarning?: (info: { type: 'cost' | 'total' | 'requests'; current: number; limit: number; }) => void; /** Callback when a limit is hit (request blocked) */ onLimitReached?: (info: { type: 'cost' | 'total' | 'requests'; current: number; limit: number; }) => void; } interface RoutingConfig { /** Routing mode: 'operator' (Tangle operators only), 'provider' (direct APIs only), 'auto' (try operators, fall back to providers) */ mode?: 'operator' | 'provider' | 'auto'; /** Preferred operator slug or address */ prefer?: string; /** Blueprint ID — route to operators under this Blueprint */ blueprintId?: string; /** Service instance ID — route to a specific service instance */ serviceId?: string; /** Routing strategy */ strategy?: 'lowest-latency' | 'lowest-price' | 'highest-reputation' | 'round-robin'; /** Region filter */ region?: string; /** Fallback operator slugs (tried in order) */ fallback?: string[]; } interface EmbeddingOptions { model?: string; input: string | string[]; } interface EmbeddingResponse { object: string; data: { object: string; embedding: number[]; index: number; }[]; model: string; usage: { prompt_tokens: number; total_tokens: number; }; } interface ImageGenerateOptions { model?: string; prompt: string; n?: number; size?: string; quality?: string; response_format?: 'url' | 'b64_json'; } /** * OpenAI-compatible /v1/images/edits request. Accepts one or more * reference images + a prompt describing how to transform them. * * For gpt-image-2, the supplied images are passed as `image[]` (the * model supports multi-image composition); for dall-e-2 only a single * image is honored. The optional `mask` is the OpenAI inpainting mask * (PNG with alpha) used to constrain where edits are applied. * * Carrying both shapes here lets callers point at the same `imagesEdit` * method regardless of upstream model; cli-bridge / tangle-router * decide what to forward. */ interface ImageEditOptions { model?: string; prompt: string; /** One or more reference images. Pass as Blob (browser/Node 22+), * ArrayBuffer (will be wrapped in a Blob), or `{data, mediaType}` * for base64 + explicit mime. The first form is preferred. */ image: ImageEditAttachment | ImageEditAttachment[]; /** Optional inpainting mask — PNG with transparent pixels marking * the editable region (OpenAI dall-e-2 / gpt-image-2 inpaint mode). */ mask?: ImageEditAttachment; n?: number; size?: string; quality?: string; response_format?: 'url' | 'b64_json'; } type ImageEditAttachment = Blob | ArrayBuffer | { data: string /** base64 */; mediaType: string; filename?: string; }; interface ImageResponse { created: number; data: { url?: string; b64_json?: string; revised_prompt?: string; }[]; } interface RerankOptions { model?: string; query: string; documents: string[]; top_n?: number; } interface RerankResponse { results: { index: number; relevance_score: number; }[]; } type SearchProvider = 'perplexity' | 'exa' | 'you' | 'parallel' | 'tavily' | 'brave'; type SearchRecency = 'day' | 'week' | 'month' | 'year'; interface SearchOptions { query: string; provider?: SearchProvider; /** Alias accepted by the Router for provider-compatible clients. */ model?: SearchProvider; maxResults?: number; searchRecency?: SearchRecency; includeDomains?: string[]; excludeDomains?: string[]; } interface SearchHit { title: string; url: string; snippet?: string; publishedAt?: string; score?: number; source?: string; } interface SearchResponse { id: string; object: 'search.result' | string; provider: SearchProvider; model: string; query: string; data: SearchHit[]; citations: string[]; usage?: { upstream_cost?: number; billed_cost?: number; gross_margin?: number; markup?: number; billing_units?: Record; }; } /** Providers served by the router's research API (POST /v1/research). Mirrors * SearchProvider minus `brave` (no research API). Each has its own `effort` * vocabulary — see ResearchOptions.effort. */ type ResearchProvider = 'perplexity' | 'exa' | 'you' | 'parallel' | 'tavily'; interface ResearchOptions { query: string; provider?: ResearchProvider; /** Alias accepted by the Router for provider-compatible clients. */ model?: ResearchProvider; /** Depth/cost dial, provider-specific: * perplexity minimal|low|medium|high · you lite|standard|deep|exhaustive · * exa deep-lite|deep|deep-reasoning · tavily mini|pro|auto · * parallel lite|base|core|pro|ultra. Omit for the provider default. */ effort?: string; maxResults?: number; searchRecency?: SearchRecency; includeDomains?: string[]; excludeDomains?: string[]; /** Optional JSON schema requesting structured output from the provider. */ outputSchema?: unknown; } interface ResearchHit { title: string; url: string; snippet?: string; publishedAt?: string; source?: string; } interface ResearchResponse { id: string; object: 'research.result' | string; provider: ResearchProvider; query: string; /** The synthesized multi-step research answer. */ answer: string; /** Supporting sources behind the answer. */ results: ResearchHit[]; citations: string[]; /** Present when an outputSchema was requested and the provider honored it. */ structured?: unknown; usage?: { upstream_cost?: number; billed_cost?: number; gross_margin?: number; markup?: number; billing_units?: Record; }; } interface WebSearchPlugin { id: 'web'; engine?: 'native' | 'exa' | 'parallel' | 'firecrawl'; provider?: SearchProvider; maxResults?: number; max_results?: number; searchPrompt?: string; search_prompt?: string; includeDomains?: string[]; include_domains?: string[]; excludeDomains?: string[]; exclude_domains?: string[]; searchRecency?: SearchRecency; search_recency?: SearchRecency; } type ChatPlugin = WebSearchPlugin | ({ id: string; } & Record); interface CompletionOptions { model?: string; prompt: string; temperature?: number; maxTokens?: number; stop?: string | string[]; topP?: number; } interface CompletionResponse { id: string; object: string; created: number; model: string; choices: { text: string; index: number; finish_reason: string; }[]; usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number; }; } interface TranscriptionResponse { text: string; } interface FineTuningJobOptions { model: string; training_file: string; hyperparameters?: { n_epochs?: number | 'auto'; batch_size?: number | 'auto'; learning_rate_multiplier?: number | 'auto'; }; suffix?: string; } interface FineTuningJob { id: string; object: string; model: string; status: string; created_at: number; finished_at: number | null; fine_tuned_model: string | null; error: { code: string; message: string; } | null; } interface BatchRequest { model: string; messages: ChatMessage[]; temperature?: number; max_tokens?: number; } interface BatchJobResponse { id: string; status: 'pending' | 'processing' | 'completed' | 'failed'; total_items: number; completed: number; failed: number; results: ({ status: 'fulfilled'; data: ChatCompletion; } | { status: 'rejected'; error: string; })[] | null; error: string | null; created_at: string; completed_at: string | null; } interface VideoGenerateOptions { model?: string; provider?: string | Record; prompt: string; duration?: number; resolution?: string; aspect_ratio?: string; size?: string; image_url?: string; frame_images?: Array>; input_references?: Array>; generate_audio?: boolean; seed?: number; callback_url?: string; } interface VideoResponse { id: string; status: string; url?: string; error?: string; } /** Request body for POST /v1/avatar/generate */ interface AvatarGenerateRequest { /** URL to narration audio (wav/mp3) */ audio_url: string; /** URL to face image, OR omit and use avatar_id */ image_url?: string; /** Preset avatar identifier (provider-specific) */ avatar_id?: string; /** Target duration in seconds (capped by operator's max_duration_seconds) */ duration_seconds?: number; /** Output format (default: "mp4") */ output_format?: string; } /** Response from POST /v1/avatar/generate (202 Accepted) */ interface AvatarGenerateResponse { job_id: string; status: 'queued' | 'processing' | 'completed' | 'failed'; result?: AvatarResult; error?: string; } /** Result payload within a completed avatar job */ interface AvatarResult { video_url: string; duration_seconds: number; format: string; } /** Response from GET /v1/avatar/jobs/:id */ interface AvatarJobStatus { job_id: string; status: 'queued' | 'processing' | 'completed' | 'failed'; result?: AvatarResult; error?: string; } interface PrivacyConfig { /** 'direct' — no proxy (default). 'relayer' — route through tcloud-relayer. 'socks5' — route through SOCKS5 proxy (e.g. Tor). */ mode: 'direct' | 'relayer' | 'socks5'; /** Relayer URL for 'relayer' mode (e.g. 'http://localhost:3030') */ relayerUrl?: string; /** * SOCKS5 proxy URL for 'socks5' mode (e.g. 'socks5://127.0.0.1:9050' for Tor). * Requires `socks-proxy-agent` as an optional peer dependency. */ socksProxy?: string; } interface ShieldedConfig { /** Pre-existing spending private key (hex). If not set, generates ephemeral. */ spendingKey?: string; /** Pre-existing commitment. If not set, derives from key. */ commitment?: string; /** Chain ID (default: 3799 for Tangle testnet) */ chainId?: number; /** ShieldedCredits contract address */ creditsAddress?: string; /** Service ID for the blueprint */ serviceId?: bigint; /** Privacy proxy configuration for IP hiding */ privacy?: PrivacyConfig; } interface ChatMessage { role: 'system' | 'user' | 'assistant' | 'tool'; content: string; name?: string; } /** Gateway-level options for routing, compliance, and inference strategies. */ interface GatewayOptions { /** BYOK: per-request provider credentials. Zero markup. */ byok?: Record>; /** Route only through ZDR-verified providers. */ zeroDataRetention?: boolean; /** Route only through providers that don't train on prompts. */ disallowPromptTraining?: boolean; /** Inject cache_control markers for providers that need them. */ caching?: 'auto' | false; /** Provider priority order. */ order?: string[]; /** Restrict to these providers only. */ only?: string[]; /** Fallback model list tried in order. */ models?: string[]; /** Per-provider or global timeout (ms, clamped 1s–120s). */ timeout?: number | Record; /** Smart routing hint. 'quality' auto-enables RSA. */ optimize?: 'cost' | 'latency' | 'quality'; /** Disable response cache for this request. */ cache?: boolean; /** Enable or configure router-managed web search for chat completions. */ webSearch?: boolean | { provider?: SearchProvider; engine?: 'native' | 'exa' | 'parallel' | 'firecrawl'; maxResults?: number; searchPrompt?: string; includeDomains?: string[]; excludeDomains?: string[]; searchRecency?: SearchRecency; }; /** * RSA / MoA: population-based quality amplification. * Spawns N parallel calls, aggregates K at a time, refines over T rounds. * Add `models` for Mixture-of-Agents (diverse models per slot). */ rsa?: { n?: number; k?: number; t?: number; /** MoA: diverse models for generation (round-robin). Aggregation uses primary model. */ models?: string[]; }; /** * Best-of-N: generate N candidates, score, return the winner. * Scorer: webhook (your HTTP endpoint) or llm (LLM-as-judge). */ bestOfN?: { n?: number; /** Diverse models for generation (round-robin). */ models?: string[]; scorer: { type: 'webhook'; url: string; timeout?: number; } | { type: 'llm'; model: string; prompt: string; }; }; } /** * Bridge options — route a single chat call through the Tangle Router's * cli-bridge short-circuit. The bridge drives subscription-backed CLIs * (Claude Code, Codex, Kimi Code, opencode) as OpenAI-compatible * harnesses with persistent session resume. * * When `bridge` is set, the client: * 1. Rewrites `model` to `bridge//` (or `bridge/` * if no model is given — uses the harness default) * 2. Injects `X-Bridge-Unlock` with the caller's unlock token * 3. Injects `X-Resume` so follow-up calls with the same id resume * the CLI's native session (no re-tokenizing prior turns) * 4. Optionally injects BYOB headers `X-Bridge-Url` + `X-Bridge-Bearer` * if the caller wants to target their own cli-bridge instance * (requires the router to be deployed with CLI_BRIDGE_BYOB_ENABLED) */ interface BridgeOptions { /** Which harness to drive. Picks the backend on the bridge. */ harness: 'claude-code' | 'claudish' | 'codex' | 'opencode' | 'kimi-code' | 'sandbox' | 'openai' | 'anthropic' | 'moonshot' | 'zai'; /** Model id inside the harness (e.g. `sonnet`, `kimi-for-coding`, `gpt-5-codex`). Omit for harness default. */ model?: string; /** Router-issued unlock token. Required for router-mediated bridge calls; unused by direct cli-bridge clients. */ unlock?: string; /** Stable caller-owned id for session resume. Map one id per logical conversation. */ resume?: string; /** BYOB: point at your own cli-bridge instance. Router must have BYOB enabled. */ bridgeUrl?: string; /** BYOB: bearer your cli-bridge expects. */ bridgeBearer?: string; } interface SandboxChatOptions { /** Inline sandbox AgentProfile. Serialized as cli-bridge/sandbox-api `agent_profile`. */ agentProfile?: AgentProfile; /** Direct sandbox or cli-bridge session id. Serialized as `session_id`. */ sessionId?: string; } interface ChatOptions { /** Model to use */ model?: string; /** Messages */ messages: ChatMessage[]; /** Temperature (0-2) */ temperature?: number; /** Max tokens to generate */ maxTokens?: number; /** Stream response */ stream?: boolean; /** Stop sequences */ stop?: string | string[]; /** Top-p sampling */ topP?: number; /** Frequency penalty */ frequencyPenalty?: number; /** Presence penalty */ presencePenalty?: number; /** JSON mode */ responseFormat?: { type: 'text' | 'json_object'; }; /** Tools / function calling */ tools?: any[]; /** Tool choice strategy or specific tool */ toolChoice?: 'none' | 'auto' | 'required' | { type: 'function'; function: { name: string; }; }; /** OpenRouter-compatible plugins, e.g. `[{ id: 'web', max_results: 5 }]`. */ plugins?: ChatPlugin[]; /** Shorthand for `gateway.webSearch`; explicit `gateway.webSearch` wins when both are set. */ webSearch?: GatewayOptions['webSearch']; /** * Gateway options: routing, compliance, inference strategies (RSA/MoA/Best-of-N). * Sent as `body.gateway` to the Router. */ gateway?: GatewayOptions; /** * Provider-specific parameters passed through to the upstream API. * Protected OpenAI/Tangle fields cannot be overridden from this escape hatch. * Example: `{ thinking: { type: 'enabled', budget_tokens: 8000 } }` */ providerOptions?: Record; /** * Typed sandbox/cli-bridge extensions. Use this instead of smuggling * `agent_profile` or `session_id` through providerOptions. */ sandbox?: SandboxChatOptions; /** * Route this call through the Tangle Router's cli-bridge short-circuit. * See {@link BridgeOptions}. When set, `model` is rewritten to * `bridge//` and bridge headers are injected. */ bridge?: BridgeOptions; } interface ChatCompletion { id: string; object: string; created: number; model: string; choices: { index: number; message: ChatMessage; finish_reason: string; }[]; usage?: { prompt_tokens: number; completion_tokens: number; total_tokens: number; }; } interface ChatCompletionChunk { id: string; object: string; created: number; model: string; choices: { index: number; delta: Partial; finish_reason: string | null; }[]; } interface Model { id: string; name: string; description?: string; context_length: number; pricing: { prompt: string; completion: string; }; _provider?: string; architecture?: { input_modalities?: string[]; output_modalities?: string[]; }; } interface Operator { id: string; slug: string; name: string; description?: string; status: string; endpointUrl: string; blueprintType: string; reputationScore: number; uptimePercent: number; avgLatencyMs: number; totalRequests: number; stakeTnt: number; /** GPU model name (e.g. "A100", "H100") */ gpuModel?: string; /** Number of GPUs available */ gpuCount?: number; /** Total VRAM across all GPUs in MiB */ totalVramMib?: number; /** Whether this operator is TEE-attested */ teeAttested?: boolean; /** TEE provider if attested (e.g. "aws_nitro") */ teeProvider?: string; models: { modelId: string; inputPrice: number; outputPrice: number; }[]; } interface CreditBalance { balance: number; transactions: { id: string; amount: number; type: string; description: string; createdAt: string; }[]; } interface CreateKeyOptions { name: string; /** Explicit parent key ID. When omitted and calling with an API key, * the new key is auto-parented to the calling key. */ parentKeyId?: string; product?: 'router' | 'sandbox' | 'evals' | 'blueprint-agent'; projectId?: string; budgetUsd?: number; allowedModels?: string[]; rpmLimit?: number; /** ISO 8601 datetime. Must be in the future. */ expiresAt?: string; } interface CreatedKey { id: string; key: string; prefix: string; name: string; product: string | null; budgetUsd: number | null; budgetRemaining: number | null; } interface ApiKeyInfo { id: string; keyPrefix: string; name: string; parentKeyId: string | null; product: string | null; projectId: string | null; budgetUsd: number | null; budgetSpent: number; allowedModels: string[] | null; rpmLimit: number | null; expiresAt: string | null; lastUsedAt: string | null; revokedAt: string | null; createdAt: string; } interface UpdateKeyOptions { name?: string; budgetUsd?: number; allowedModels?: string[]; rpmLimit?: number | null; expiresAt?: string | null; } /** Status event from an async job SSE stream */ interface JobEvent { status: 'queued' | 'processing' | 'completed' | 'failed' | 'cancelled'; progress?: number; result?: Record; error?: string; timestamp: number; } /** Options for watchJob() */ interface WatchJobOptions { /** Operator endpoint URL (if not using default routing) */ operatorUrl?: string; /** Callback for each event (useful for progress tracking) */ onEvent?: (event: JobEvent) => void; /** Timeout in ms (default: 5 minutes) */ timeout?: number; /** Model to route to (for operator discovery) */ model?: string; /** SSE bearer token (replaces API key for operator SSE auth) */ sseToken?: string; } interface SpendAuth { commitment: string; serviceId: string; jobIndex: number; amount: string; operator: string; nonce: string; expiry: string; signature: string; } /** * Private Router — operator rotation strategies for privacy-preserving inference. * * Each strategy determines how requests are distributed across operators * to minimize the information any single operator can gather about a user's * conversation patterns. */ interface OperatorInfo { slug: string; endpointUrl: string; region: string; reputationScore: number; avgLatencyMs: number; models: string[]; } type RoutingStrategy = 'round-robin' | 'random' | 'geo-distributed' | 'min-exposure' | 'latency-aware'; interface PrivateRouterConfig { strategy: RoutingStrategy; /** Max requests to same operator before forced rotation */ maxRequestsPerOperator: number; /** Minimum number of distinct operators to use */ minOperators: number; /** Region preferences (operators in these regions preferred) */ preferRegions?: string[]; /** Exclude specific operators */ excludeOperators?: string[]; /** Enable context summarization between operator switches (reduces info leakage) */ summarizeOnSwitch: boolean; } declare class PrivateRouter { private config; private operators; private usage; private currentIndex; private totalRequests; /** Slug of the most-recently-selected operator. Populated on each selectOperator() hit. */ private _lastSelectedSlug; constructor(config?: Partial); /** Set the available operator pool */ setOperators(operators: OperatorInfo[]): void; /** Select the next operator for a request */ selectOperator(model: string): OperatorInfo | null; /** Should we summarize context before this request? (operator is changing) */ shouldSummarize(model: string): boolean; /** Get privacy stats */ getStats(): { totalRequests: number; operatorsUsed: number; operatorBreakdown: { slug: string; requests: number; lastUsed: number; }[]; strategy: RoutingStrategy; }; private roundRobin; private random; private geoDistributed; private minExposure; private latencyAware; private recordUsage; /** Slug of the most-recently-selected operator (null before the first call). */ get lastSelectedSlug(): string | null; private getLastUsedOperator; private peekNextOperator; } /** * Core HTTP client for Tangle AI Cloud. * Shared between CLI and SDK. */ /** Rotation knobs for {@link TCloudClient.rotating}. */ interface RotatingRoutingConfig { /** Router strategy. Defaults to `'min-exposure'`. */ strategy?: Extract; /** * Pre-seed the operator pool. When omitted the client fetches * `/api/operators` on the first call (TTL-cached). */ pool?: OperatorInfo[]; /** Minimum distinct operators required before routing proceeds. */ minOperators?: number; /** Max requests per operator before forced rotation. */ maxRequestsPerOperator?: number; /** Exclude specific operator slugs. */ excludeOperators?: string[]; /** Prefer specific regions (others kept as fallback). */ preferRegions?: string[]; } /** Configuration accepted by {@link TCloudClient.rotating}. */ type RotatingClientConfig = Omit & { routing?: RotatingRoutingConfig; }; /** Rotation stats surfaced by {@link TCloudClient.getRotationStats}. */ interface RotationStats { /** Per-operator call counter. */ callsByOperator: Record; /** Slug of the most-recently-selected operator, if any. */ currentOperator: string | null; } declare class TCloudClient { readonly baseURL: string; readonly platformURL: string; readonly apiKey?: string; readonly model: string; private headers; private spendAuthFn?; private privacy?; private limits?; private retryConfig; private timeoutMs; private _totalSpent; private _requestCount; readonly privateRouter?: PrivateRouter; private _cachedOperators; private _operatorsCachedAt; private static readonly OPERATORS_TTL_MS; /** * Build a client pointed directly at a cli-bridge instance — skips the * Tangle Router entirely. cli-bridge serves the OpenAI-compatible * `/v1/chat/completions` endpoint natively, so chat() / ask() / * chatStream() work as-is against any local or remote bridge. * * Use this when you have your own cli-bridge running (locally or on * your own VPS) and don't need router-side gating, billing, or * observability — your CLI subscriptions on the bridge box pay for * the LLM tokens directly. * * Wire form: model id is `/` (e.g. `claude-code/sonnet`, * `kimi-code/kimi-for-coding`) — no `bridge/` prefix needed in direct * mode; cli-bridge accepts the harness id as the first path segment. * * ```ts * const client = TCloudClient.fromCliBridge({ * url: 'http://127.0.0.1:3344', * bearer: process.env.CLI_BRIDGE_BEARER!, * }) * const reply = await client.ask('explain X', 'claude-code/sonnet') * ``` * * For session-resumable agentic dispatches, use `client.bridge(...)` on * the returned direct client. `resume` is serialized to cli-bridge's * `session_id` body field and the model wire format stays * `/` without the router-only `bridge/` prefix. */ static fromCliBridge(opts: { /** cli-bridge base URL — `http://127.0.0.1:3344` for default local; can be any reachable URL. */ url: string; /** BRIDGE_BEARER from the cli-bridge's `.env.local`. */ bearer: string; /** Optional config passthrough (timeout, retry, etc). */ config?: Omit; }): TCloudClient; /** * Build a client that rotates which operator serves each call. Mirrors * {@link TCloudClient.shielded} in shape: returns a standard `TCloudClient` * that behaves identically for the OpenAI-compatible surface but * dispatches each chat/completions/embeddings request through a * {@link PrivateRouter} — different operator per call per the chosen * strategy. * * ```ts * const tcloud = TCloudClient.rotating({ * apiKey: process.env.TANGLE_API_KEY, * routing: { strategy: 'min-exposure' }, * }) * await tcloud.ask('hello') * tcloud.getRotationStats() // { callsByOperator: { ... }, currentOperator: '…' } * ``` * * Rotation is meaningful only for stateless calls. Sandbox-harness * sessions bind to a single operator for the lifetime of the session; * `rotating()` clients refuse to dispatch them — see {@link bridge}. */ static rotating(config?: RotatingClientConfig): TCloudClient; constructor(config?: TCloudConfig); /** Set the SpendAuth signer for private mode */ setSpendAuthSigner(fn: () => Promise): void; /** Current metering stats */ get usage(): { totalSpent: number; requestCount: number; limits: { maxCostPerRequest?: number; maxTotalSpend?: number; maxRequests?: number; onLimitWarning?: (info: { type: "cost" | "total" | "requests"; current: number; limit: number; }) => void; onLimitReached?: (info: { type: "cost" | "total" | "requests"; current: number; limit: number; }) => void; } | undefined; }; /** Check spending limits before a request. Throws TCloudError if blocked. */ private checkLimits; /** Ensure the private router has operators loaded (with TTL-based caching) */ private ensureRouterOperators; /** Track cost after a response, using actual pricing from response headers when available */ private trackCost; /** * Core fetch with retry + timeout. All helpers build on this. * Retries on retryable status codes with exponential backoff + jitter. */ private _doFetch; /** * Shared request helper for billable JSON API calls. * Enforces: checkLimits → fetch with retry/timeout → error parsing → requestCount. */ private _request; /** * Shared request helper for read-only/non-billable JSON API calls. * No limits check, no request counting. */ private _fetch; /** * Shared request helper for billable calls that return non-JSON (e.g. ArrayBuffer). */ private _requestRaw; /** * Prepare headers for chat requests — operator routing + SpendAuth + * bridge short-circuit headers when `options.bridge` is set. * Shared between chat() and chatStream() to eliminate duplication. */ private _prepareChatRequest; /** * Resolve the effective model string. When a bridge is set, rewrite to * `bridge//` (or `bridge/` if no model). */ private _effectiveModel; /** Build the chat completions request body */ private _chatBody; /** Chat completion (non-streaming) */ chat(options: ChatOptions): Promise; /** Chat completion (streaming) — returns an async iterator of chunks */ chatStream(options: ChatOptions): AsyncGenerator; /** * Bridge — scoped helper for a subscription-backed CLI harness behind * the Tangle Router's cli-bridge. Returns a mini-client bound to * (harness, unlock, resume) so you don't thread those through every * call. * * ```ts * const kimi = tcloud.bridge({ harness: 'kimi-code', model: 'kimi-for-coding', unlock: UNLOCK, resume: 'pr-42' }) * await kimi.ask('review this diff…') * for await (const chunk of kimi.stream('continue…')) process.stdout.write(chunk) * ``` * * Sessions persist across process restarts — use the same `resume` id * to land on the same CLI conversation (context intact, no replay tax). * * Guard: clients built via {@link TCloudClient.rotating} cannot dispatch * sandbox-harness sessions (rotation rotates per call; a sandbox session * binds to one operator). Attempting `bridge({ harness: 'sandbox' })` on * a rotating client throws. */ bridge(cfg: BridgeOptions): BridgeSession; private _isDirectCliBridge; /** * Rotation stats — populated only on clients created via * {@link TCloudClient.rotating}. Non-rotating clients return an empty * counter and `currentOperator: null`. */ getRotationStats(): RotationStats; /** Convenience: send a single message and get the text response */ ask(message: string, modelOrOptions?: string | Partial): Promise; /** Convenience: send a single message and get the full completion (with usage) */ askFull(message: string, modelOrOptions?: string | Partial): Promise; /** Convenience: stream a single message and yield text chunks */ askStream(message: string, modelOrOptions?: string | Partial): AsyncGenerator; /** List available models */ models(): Promise; /** List active operators */ operators(): Promise<{ operators: Operator[]; stats: any; }>; /** Get credit balance */ credits(): Promise; /** Add credits via Stripe checkout. Returns the checkout URL. */ addCredits(amount: number): Promise<{ url: string; }>; /** Get transaction history */ transactions(limit?: number): Promise<{ id: string; amount: number; type: string; product: string | null; description: string | null; createdAt: string; }[]>; /** * Create a new API key. * When called with an API key (not session), the new key is automatically * a child of the calling key — enabling hierarchical key delegation. * * Pass `parentKeyId` explicitly to create a child of a specific key. * Child keys inherit the parent's product scope, allowedModels, and rpmLimit * if not specified. Budget cannot exceed the parent's remaining budget. */ createKey(opts: CreateKeyOptions): Promise; /** Get a single API key by ID */ getKey(id: string): Promise; /** * List API keys. * Pass `children: true` to list child keys of the calling API key. */ keys(opts?: { children?: boolean; }): Promise; /** * Update an API key's limits. * Can adjust budget, allowedModels, rpmLimit, expiresAt, and name. */ updateKey(id: string, updates: UpdateKeyOptions): Promise; /** Revoke an API key. If the key has children, they are also revoked recursively. */ revokeKey(id: string): Promise; /** Rotate an API key — creates new key with same config, revokes old */ rotateKey(id: string): Promise<{ newKey: CreatedKey; revokedKeyId: string; }>; /** Create a project for usage attribution */ createProject(name: string, product?: string): Promise<{ id: string; name: string; }>; /** List projects */ projects(): Promise<{ id: string; name: string; product: string | null; createdAt: string; }[]>; /** Generate embeddings */ embeddings(options: EmbeddingOptions): Promise; /** Generate images */ imageGenerate(options: ImageGenerateOptions): Promise; /** * Edit / inpaint / variate an existing image with a text prompt. * Sibling to `imageGenerate`; routes to `/v1/images/edits` via * multipart/form-data per the OpenAI spec. * * Reference image attachments may be passed as `Blob`, `ArrayBuffer`, * or `{data: base64, mediaType, filename?}`. Multi-image composition * (e.g. gpt-image-2 with two reference frames + a prompt that fuses * them) is supported by passing an array; for legacy models only the * first image is honored upstream. */ imagesEdit(options: ImageEditOptions): Promise; /** Rerank documents by relevance to a query */ rerank(options: RerankOptions): Promise; /** Search the web through Tangle Router billing and provider routing. */ search(options: SearchOptions): Promise; /** Run a multi-step deep-research task through Tangle Router billing and * provider routing (POST /v1/research). Slower and costlier than `search` — * the provider synthesizes an answer over many fetches. Pick depth with * `effort` (provider-specific vocabulary; see {@link ResearchOptions.effort}). */ research(options: ResearchOptions): Promise; /** Text-to-speech */ speech(options: { model?: string; input: string; voice?: string; }): Promise; /** Legacy completions endpoint */ completions(options: CompletionOptions): Promise; /** Audio transcription (speech-to-text) */ transcribe(file: Blob, options?: { model?: string; language?: string; prompt?: string; }): Promise; /** Create a fine-tuning job */ fineTuneCreate(options: FineTuningJobOptions): Promise; /** List fine-tuning jobs */ fineTuneList(): Promise<{ data: FineTuningJob[]; }>; /** Submit a batch of chat requests */ batch(requests: BatchRequest[]): Promise; /** Get batch job status */ batchStatus(jobId: string): Promise; /** Generate video */ videoGenerate(options: VideoGenerateOptions): Promise; /** Get video generation status */ videoStatus(id: string): Promise; /** Generate an avatar video (lip-synced talking head from audio + face image). * Returns 202 with a job_id for async polling via avatarJobStatus(). */ avatarGenerate(options: AvatarGenerateRequest): Promise; /** Poll an avatar generation job by ID. */ avatarJobStatus(jobId: string): Promise; /** Poll an avatar job until it reaches a terminal state (completed/failed). * Returns the final job status. Throws on failure. */ pollAvatarJob(jobId: string, options?: { intervalMs?: number; timeoutMs?: number; }): Promise; /** * Watch an async job via SSE until it reaches a terminal state. * Works with avatar, video, and training blueprint operators. * * @param jobId - The job ID returned by the creation endpoint * @param options - Optional: operatorUrl override, onEvent callback * @returns The final JobEvent (completed/failed/cancelled) */ watchJob(jobId: string, options?: WatchJobOptions): Promise; /** Create a vector collection on the operator's vector store */ createCollection(options: { name: string; dimensions: number; distance_metric?: string; }): Promise; /** List collections on the operator's vector store */ listCollections(): Promise; /** Upsert vectors into a collection */ upsertVectors(collection: string, vectors: Array<{ id: string; vector: number[]; metadata?: Record; }>): Promise; /** Similarity search in a collection */ queryVectors(collection: string, options: { vector: number[]; top_k?: number; filter?: Record; }): Promise; /** RAG query — embed text + search collection in one call */ ragQuery(options: { query: string; collection: string; top_k?: number; embedding_model?: string; }): Promise; /** Search models by name, provider, or capability */ searchModels(query: string): Promise; /** Estimate cost for a request (without sending it) */ estimateCost(options: { model?: string; inputTokens: number; outputTokens: number; }): Promise<{ inputCost: number; outputCost: number; total: number; }>; /** * Get a pricing spectrum across resource tiers for a model. * * Uses REAL per-operator pricing from `operator.models[].inputPrice`. * Each tier filters operators by GPU count and TEE capability, then * reports the cheapest and most expensive operator for that config. * * @param options.model - Model ID to price (falls back to client default) * @param options.tiers - Number of tiers (1-7, default 5) */ pricingSpectrum(options: { model?: string; tiers?: number; }): Promise; private get _apiRoot(); eval(opts: { models: string[]; scenarios: Array<{ id: string; prompt: string; rubric?: string; category?: string; expectedContains?: string[]; maxLatencyMs?: number; }>; judge?: string; iterations?: number; systemPrompt?: string; }): Promise<{ results: Array<{ model: string; summary: any; scenarios: any[]; }>; }>; createSuite(opts: { name: string; scenarios: Array<{ id: string; prompt: string; rubric?: string; }>; models: string[]; judge?: string; iterations?: number; tags?: string[]; }): Promise<{ suite: { id: string; name: string; }; }>; listSuites(): Promise<{ suites: Array<{ id: string; name: string; models: string[]; }>; }>; runSuite(suiteId: string, opts?: { baseline?: boolean; concurrency?: number; }): Promise; listRuns(suiteId: string): Promise; getRun(runId: string): Promise; setBaseline(runId: string): Promise; sandboxPricing(opts?: { cpu?: number; ram?: number; disk?: number; }): Promise<{ pricing: { hourlyRate: number; perMinuteRate: number; }; plan: string; limits: { maxCpu: number; maxRamGb: number; maxDiskGb: number; }; balance: number; canAfford: { minutes: number; hours: number; }; }>; sandboxStatus(): Promise<{ linked: boolean; keyPrefix?: string; gatewayUrl?: string; }>; sandboxProvision(): Promise<{ provisioned: boolean; minutesRemaining?: number; }>; sandboxCreate(opts: { model?: string; harness?: 'claude-code' | 'codex' | 'opencode' | 'amp' | 'factory'; cpu?: number; ram?: number; storage?: number; gitUrl?: string; systemPrompt?: string; }): Promise<{ sessionId: string; harness: string; model: string; minutesRemaining?: number; }>; sandboxList(): Promise<{ sessions: Array<{ id: string; status: string; model: string; harness: string; }>; }>; sandboxStats(sandboxId: string): Promise<{ config: { cpu: number; ramGb: number; diskGb: number; }; uptime: number; computeMinutes: number; live?: { cpuPercent: number; memoryUsedMb: number; memoryTotalMb: number; }; }>; sandboxDestroy(sessionId: string): Promise<{ deleted: boolean; }>; userInfo(): Promise<{ user: { id: string; email: string; name?: string; }; balance: number; subscription: { plan: string; status: string; } | null; usage: Record; }>; } /** Select N evenly-spaced items, always including first and last. */ /** * BridgeSession — a chat client scoped to one bridge configuration. * * Instead of threading `{ harness, unlock, resume }` through every * `chat()` call, create a session once and call `ask` / `stream` / `chat` * on it. The session's `resume` id is stable across calls so follow-up * turns land on the same CLI conversation. * * ```ts * const tcloud = new TCloudClient({ apiKey, baseURL: 'https://router.tangle.tools/api' }) * const kimi = tcloud.bridge({ harness: 'kimi-code', model: 'kimi-for-coding', unlock: UNLOCK, resume: 'pr-42' }) * * // one-shot * const reply = await kimi.ask('summarize this diff') * * // streaming * for await (const chunk of kimi.stream('continue…')) process.stdout.write(chunk) * * // full OpenAI-shaped request * const completion = await kimi.chat({ messages, temperature: 0.2 }) * * // new resume id for a different logical conversation * const kimiOther = kimi.withResume('ticket-123') * ``` */ declare class BridgeSession { private readonly client; private readonly cfg; private readonly direct; constructor(client: TCloudClient, cfg: BridgeOptions, direct?: boolean); /** Full chat completion (non-streaming). */ chat(options: Omit): Promise; /** Stream OpenAI chat.completion.chunks. */ chatStream(options: Omit): AsyncGenerator; /** One-shot: send a string, get the assistant text. */ ask(message: string, extra?: Omit, 'bridge' | 'messages'>): Promise; /** One-shot: send a string, stream text deltas. */ stream(message: string, extra?: Omit, 'bridge' | 'messages'>): AsyncGenerator; /** Turn-based: send full message history, get assistant text. */ turn(messages: ChatMessage[], extra?: Omit, 'bridge' | 'messages'>): Promise; /** Clone with a new resume id — same harness, different logical conversation. */ withResume(resume: string): BridgeSession; /** Clone with a different model inside the same harness. */ withModel(model: string): BridgeSession; /** The effective model id that will land on the router (`bridge//`). */ get model(): string; /** The resume id currently bound to this session, if any. */ get resume(): string | undefined; } interface TierConfig { name: string; cpu: number; ramGb: number; gpu: number; tee: boolean; } interface PricingTier { tier: string; config: TierConfig; /** Raw cheapest per-input-token price (for programmatic use) */ cheapestPrice?: number; /** Raw priciest per-input-token price (undefined if same as cheapest) */ priciestPrice?: number; /** Formatted cheapest price */ cheapest: string; /** Formatted priciest price (undefined if only one price point) */ priciest?: string; /** Operators matching GPU/TEE requirements */ availableOperators: number; /** Operators that also serve the requested model at a listed price */ operatorsWithModel: number; } declare class TCloudError extends Error { status: number; constructor(status: number, message: string); } export { type SearchProvider as $, type ApiKeyInfo as A, type BatchJobResponse as B, type ChatCompletion as C, type RerankOptions as D, type EmbeddingOptions as E, type FineTuningJob as F, type GatewayOptions as G, type RerankResponse as H, type ImageEditAttachment as I, type JobEvent as J, type ResearchHit as K, type ResearchOptions as L, type Model as M, type ResearchProvider as N, type Operator as O, type PricingTier as P, type ResearchResponse as Q, type RotatingClientConfig as R, type RetryConfig as S, type TCloudConfig as T, type RotatingRoutingConfig as U, type RotationStats as V, type RoutingConfig as W, type RoutingStrategy as X, type SandboxChatOptions as Y, type SearchHit as Z, type SearchOptions as _, TCloudClient as a, type SearchRecency as a0, type SearchResponse as a1, type ShieldedConfig as a2, type SpendAuth as a3, type SpendingLimits as a4, TCloudError as a5, type TierConfig as a6, type TranscriptionResponse as a7, type UpdateKeyOptions as a8, type VideoGenerateOptions as a9, type VideoResponse as aa, type WatchJobOptions as ab, type WebSearchPlugin as ac, type AvatarGenerateRequest as b, type AvatarGenerateResponse as c, type AvatarJobStatus as d, type AvatarResult as e, type BatchRequest as f, type BridgeOptions as g, BridgeSession as h, type ChatCompletionChunk as i, type ChatMessage as j, type ChatOptions as k, type ChatPlugin as l, type CompletionOptions as m, type CompletionResponse as n, type CreateKeyOptions as o, type CreatedKey as p, type CreditBalance as q, type EmbeddingResponse as r, type FineTuningJobOptions as s, type ImageEditOptions as t, type ImageGenerateOptions as u, type ImageResponse as v, type OperatorInfo as w, type PrivacyConfig as x, PrivateRouter as y, type PrivateRouterConfig as z };