@hikigai/agent-sdk
Agent SDK
Official Node.js SDK for deploying and managing healthcare AI agents on Hikigai. Full type safety with Zod validation and modern async/await patterns.
Installation
Choose your preferred package manager:
npm
npm install @hikigai/agent-sdkyarn
yarn add @hikigai/agent-sdkpnpm
pnpm add @hikigai/agent-sdkQuick Start
import { AgentClient, InputSchema, OutputSchema } from '@hikigai/agent-sdk'
const client = new AgentClient({
apiKey: process.env.HIKIGAI_API_KEY,
projectId: process.env.HIKIGAI_PROJECT_ID,
})
const agent = await client.deploy({
name: 'medical-coder',
displayName: 'Medical Coding Assistant',
description: 'Extracts ICD-10 and CPT codes from clinical notes',
instruction: 'You are a medical coding expert. Extract ICD-10 and CPT codes.',
model: 'claude-3.5-sonnet',
category: 'coding-billing',
version: '1.0.0',
inputSchema: new InputSchema({
clinical_note: { type: 'string', required: true },
}),
outputSchema: new OutputSchema({
icd_codes: { type: 'array', items: { type: 'string' } },
cpt_codes: { type: 'array', items: { type: 'string' } },
}),
})
console.log(`Deployed: ${agent.id} (${agent.slug})`)
// Dev helpers
const result = await client.invoke(agent.id, {
clinical_note: 'Patient presents with fever and cough...',
})
console.log(result)
for await (const chunk of client.stream(agent.id, {
clinical_note: 'Patient presents with fever and cough...',
})) {
process.stdout.write(chunk)
}Core Concepts
AgentClient
Main interface for agent deployment and management.
class AgentClient {
constructor(options?: {
apiKey?: string // API key (or HIKIGAI_API_KEY env var)
projectId?: string // Project ID (or HIKIGAI_PROJECT_ID env var)
baseUrl?: string // API endpoint
timeout?: number // Request timeout in milliseconds
})
readonly jobs: JobsClient
readonly storage: StorageClient
readonly events: EventsClient
readonly identityVerification: IdentityVerificationClient
deploy(config: AgentConfigInput, options?: {
timeout?: number
pollInterval?: number
cloudService?: string // optional; omit to use org default
region?: string
cloudCredentialId?: string
cloudCredentialName?: string // Cloud accounts name; omit for org default
}): Promise<DeployedAgent>
listAgents(): Promise<DeployedAgent[]>
getAgent(agentId: string): Promise<DeployedAgent>
updateAgent(agentId: string, update: Record<string, unknown>): Promise<DeployedAgent>
deleteAgent(agentId: string): Promise<void>
invoke(agentId: string, input: string | Record<string, unknown>, options?: {
sessionId?: string; provider?: string; model?: string
}): Promise<Record<string, unknown>>
stream(agentId: string, input: string | Record<string, unknown>, options?: {
sessionId?: string; provider?: string; model?: string
}): AsyncIterable<string>
getAuthToken(): Promise<AuthTokenResponse>
close(): void
}Jobs (client.jobs)
Background job queue for long-running agent work. Methods: enqueue, get, list, cancel, replay, wait, stream.
const job = await client.jobs.enqueue('agent.invoke', {
input: 'Batch-code these notes',
}, { agentId: agent.id, priority: 'standard' })
const detail = await client.jobs.wait(job.id)
console.log(detail.status, detail.result)
// Also available:
// await client.jobs.get(job.id)
// await client.jobs.list({ status: 'failed', page: 1 })
// await client.jobs.cancel(job.id)
// await client.jobs.replay(job.id)
// for await (const event of client.jobs.stream(job.id)) { ... }Storage (client.storage)
Platform object storage scoped to project + appId. Methods: upload, signedUrl, list, get, delete.
const uploaded = await client.storage.upload('app_123', fileBuffer, 'note.pdf', {
contentType: 'application/pdf',
metadata: { visitId: 'v-42' },
ttl: 86400, // optional object TTL (seconds)
})
console.log(uploaded.objectId, uploaded.sizeBytes, uploaded.signedUrl)
const { signedUrl, expiresAt } = await client.storage.signedUrl(
'app_123',
uploaded.objectId,
3600,
)
const info = await client.storage.get('app_123', uploaded.objectId)
console.log(info.originalFilename, info.metadata, info.status)
const page = await client.storage.list('app_123', {
contentType: 'application/pdf',
status: 'active',
limit: 20,
page: 1,
})
for (const obj of page.objects) {
console.log(obj.objectId, obj.originalFilename, obj.sizeBytes)
}
console.log(page.total, page.totalPages)
await client.storage.delete('app_123', uploaded.objectId)Rooms (RoomSession)
Multi-party realtime rooms — create/join with join(name), then publish and listen. Optional WebRTC signalling via rtcJoin / rtcOffer / rtcAnswer. Standalone class (not on AgentClient). Same surface as Python / AppSDK.
import { RoomSession } from '@hikigai/agent-sdk'
const room = new RoomSession({
baseUrl: process.env.HIKIGAI_BASE_URL ?? 'https://backend.hikigaiplatform.io',
apiKey: process.env.HIKIGAI_API_KEY!,
projectId: process.env.HIKIGAI_PROJECT_ID!,
})
await room.connect()
await room.join('rounds-42') // creates the room if new
await room.publish('rounds-42', 'update', { status: 'in_progress' })
await room.who('rounds-42')
for await (const frame of room) {
if (frame.type === 'message') console.log(frame.event, frame.data, frame.seq)
if (frame.type === 'error') break
}
await room.close()
// WebRTC signalling (after join):
// await room.rtcJoin('call-1')
// await room.rtcOffer('call-1', peerId, sdp)
// await room.rtcAnswer('call-1', peerId, sdp)
// await room.rtcCandidate('call-1', peerId, candidate)Configuration
AgentConfig
Comprehensive configuration for agent deployment (Zod-validated).
interface AgentConfig {
// Identity
name: string // 3-64 chars, lowercase, hyphens only
displayName: string // 3-100 chars
description: string // 10-500 chars
longDescription?: string
// Core
agentType?: 'llm' | 'sequential' | 'parallel' | 'loop' // Default: 'llm'
instruction: string
model: string // Default: 'claude-3.5-sonnet'
// Schemas
inputSchema: InputSchema | Record<string, unknown>
outputSchema: OutputSchema | Record<string, unknown>
// Modality
inputModality?: string // text | audio | combinations
outputModality?: string
vad?: VadConfig // Live automatic VAD (audio input only)
// Tools & connectors (root connectors → every Sequential/Parallel/Loop leaf)
tools?: unknown[]
connectors?: ConnectorConfig[] // see #mcp-subagents
subAgents?: SubAgentConfig[]
// Runtime
timeout?: number // Default: 60
memoryMb?: number // Default: 512
minInstances?: number // Default: 0
maxInstances?: number // Default: 10
// Omit cloudProvider to use Organization settings → Cloud accounts (deploy default)
cloudProvider?: string // 'gcp' | 'aws'
// Versioning & compliance
version: string
changelog?: string
hipaaCompliant?: boolean // Default: true
category?: string
tags?: string[]
}VadConfig
Automatic voice activity detection for live audio agents. Pass when inputModality includes audio. Defaults: 800 ms / low / low. Include VAD on every same-name deploy() to preserve or change it.
interface VadConfig {
silenceDurationMs?: number // 200–2000, default 800
endOfSpeechSensitivity?: 'high' | 'low' // default 'low'
startOfSpeechSensitivity?: 'high' | 'low' // default 'low'
}
await client.deploy({
...,
inputModality: 'audio',
outputModality: 'text',
vad: {
silenceDurationMs: 800,
endOfSpeechSensitivity: 'low',
startOfSpeechSensitivity: 'low',
},
})Input / Output Schemas
Field types: string, integer, number, boolean, array, object. Optional description, required, enum, items, properties.
import { InputSchema, OutputSchema } from '@hikigai/agent-sdk'
const inputSchema = new InputSchema({
patient_id: { type: 'string', required: true },
age: { type: 'integer', required: true, minimum: 0, maximum: 150 },
symptoms: { type: 'array', items: { type: 'string' } },
})
const outputSchema = new OutputSchema({
diagnosis: { type: 'string' },
confidence: { type: 'integer', minimum: 0, maximum: 100 },
recommendations: { type: 'array', items: { type: 'string' } },
})Workflow Agents (Sequential / Parallel / Loop)
Set agentType to 'sequential', 'parallel', or 'loop' with nested subAgents. Text workflows advance on their own. Live pipelines need liveHandoff on leaves. Open live sessions with AppSDK live().
| Type | Behavior | liveHandoff |
|---|---|---|
sequential | Leaves one after another | Per-leaf task_completed or after_output |
parallel | Concurrent branches (mic teed) | Same; prefer text-only outs |
loop | Repeat until escalate / maxIterations | Same as sequential |
SubAgentConfig
One leaf (or nested workflow) under a Sequential / Parallel / Loop root.
interface SubAgentConfig {
name: string
agentType?: 'llm' | 'sequential' | 'parallel' | 'loop'
model?: string
instruction?: string
description?: string
tools?: string[]
inputModality?: string // default 'text'
outputModality?: string // default 'text'
liveHandoff?: 'task_completed' | 'after_output' // default 'task_completed'
subAgents?: SubAgentConfig[] // nested workflows
maxIterations?: number // loop only
source?: 'marketplace' // reuse marketplace agent
sourceAgentId?: string
}liveHandoff: task_completed (default, multi-turn) or after_output (one-shot after model reply). Ignored for simple live LlmAgent roots.
await client.deploy({
name: 'clinical-live-pipeline',
displayName: 'Clinical Live Pipeline',
description: 'Sequential live intake with one-shot leaves',
agentType: 'sequential',
model: 'gemini-2.5-flash-native-audio-preview-12-2025',
instruction: 'Orchestrate the live clinical pipeline.',
version: '1.0.0',
inputModality: 'audio',
outputModality: 'audio',
inputSchema: { type: 'object', properties: { audio: { type: 'string' } } },
outputSchema: { type: 'object', properties: { note: { type: 'string' } } },
subAgents: [
{
name: 'voice-intake',
model: 'gemini-2.5-flash-native-audio-preview-12-2025',
instruction: 'Wait for the user, then acknowledge in under 8 words.',
inputModality: 'audio',
outputModality: 'audio',
liveHandoff: 'after_output',
},
{
name: 'note-composer',
model: 'gemini-2.5-flash-native-audio-preview-12-2025',
instruction: 'Emit a short SOAP note from context, then stop.',
inputModality: 'text',
outputModality: 'text',
liveHandoff: 'after_output',
},
],
})Reuse marketplace agents as sub-agents
Pass instruction / model for your own leaves. Set source: 'marketplace' with the marketplace agent name (or slug) to reuse it — no UUID, instruction, or schemas. The platform hydrates those at deploy time. Optionally pass sourceAgentId instead.
await client.deploy({
name: 'cpt2-note-workflow',
displayName: 'CPT2 Note Workflow',
description: 'Clean a clinical note, then reuse CPT2 from the marketplace',
agentType: 'sequential',
model: 'gemini-2.5-flash',
instruction: 'Pass the note through prep, then CPT Category II extraction.',
version: '1.0.0',
category: 'coding-billing',
inputSchema: { type: 'object', properties: { input: { type: 'string' } }, required: ['input'] },
outputSchema: { type: 'object', properties: { output: { type: 'string' } }, required: ['output'] },
subAgents: [
{
name: 'note_cleaner',
model: 'gemini-2.5-flash',
instruction: 'Clean and structure the note for CPT II coding. Do not invent facts.',
description: 'Prepares notes for coding',
},
{
name: 'cpt2_code_agent', // marketplace name or slug
source: 'marketplace',
},
],
})
// Invoke with App SDK: client.agent('cpt2-note-workflow').invoke({ input: '...' })MCP on Sub-Agents
Declare connectors on the root agent config. They serialize as mcp_connectors and at invoke/live attach to every LlmAgent leaf (Sequential / Parallel / Loop) — not only a simple LLM root. Prefer declaring at deploy so leaf outputSchema is not baked (ADK skips CallTool when schema is set).
await client.deploy({
name: 'vitals-fetch-subagent',
displayName: 'Vitals Fetch (Subagent MCP)',
description: 'Sequential: MCP fetch leaf, then summarizer',
instruction: 'Run fetcher then summarizer. Do not answer yourself.',
model: 'gemini-2.5-flash',
agentType: 'sequential',
category: 'monitoring',
version: '1.0.0',
// Root connectors → every leaf can CallTool
connectors: [
{
slug: 'idexx-mcp-server',
toolFilter: ['get_vitals_for_today'],
required: true,
},
],
subAgents: [
{
name: 'vitals-fetcher',
model: 'gemini-2.5-flash',
instruction: 'Call get_vitals_for_today once with site_id and patient_id.',
},
{
name: 'vitals-summarizer',
model: 'gemini-2.5-flash',
instruction: 'Emit {"response": "<summary>"}. Do not call tools.',
},
],
inputSchema: /* site_id, patient_id */,
outputSchema: /* response */,
})
// Invoke with AppSDK — see /docs/sdk/node-appsdk#mcp-workflow-agentsTools
Add tools to your agents: functions, OpenAPI specs, MCP, or built-ins.
Function Tools
import { tool } from '@hikigai/agent-sdk'
const searchDatabase = tool(function searchDatabase(query: string): string {
return JSON.stringify(results)
})
await client.deploy({
// ...
tools: [searchDatabase],
})OpenAPI Tools
import { OpenAPITool } from '@hikigai/agent-sdk'
const weatherApi = new OpenAPITool({
specUrl: 'https://api.weather.com/openapi.json',
operationId: 'getCurrentWeather',
})MCP Tools
Static MCPTool entries, or prefer root connectors for project MCP (see MCP on Sub-Agents).
import { MCPTool } from '@hikigai/agent-sdk'
const ehrLookup = new MCPTool('epic-ehr', 'get_patient')
// or: new MCPTool({ serverName: 'epic-ehr', toolName: 'get_patient' })Built-in Tools
await client.deploy({
// ...
tools: ['web_search', 'execute_code', 'read_file'],
})DeployedAgent
interface DeployedAgent {
id: string
name: string
slug: string
version: string
displayName?: string
description?: string
deploymentStatus: 'active' | 'pending' | 'error'
deploymentType: 'config_based' | 'adk' | 'file'
endpointUrl?: string
cloudProvider?: string
region?: string
hipaaCompliant: boolean
hipaaVerified: boolean
createdAt?: Date
deployedAt?: Date
}List / Get / Update / Delete
const agents = await client.listAgents()
const agent = await client.getAgent('agent-id-or-slug')
await client.updateAgent(agent.id, { description: 'Updated description' })
await client.deleteAgent(agent.id)
// Dev helpers
const result = await client.invoke(agent.id, 'Hello')
for await (const chunk of client.stream(agent.id, 'Summarize')) {
process.stdout.write(chunk)
}Multi-Cloud Deploy
Whatever you set as the default under Organization settings → Cloud accounts is used when you deploy. Omit cloud options to use that default. Pass cloudProvider: 'gcp' or 'aws' only when you need to override it. Personal workspaces with no cloud account fall back to Hikigai's platform cloud; team orgs need a default account (or an explicit override) before deploy.
// Uses Organization settings → Cloud accounts default
const agent = await client.deploy(config)
// Override provider when needed
await client.deploy({ ...config, cloudProvider: 'aws' })
// Or pick a specific account by name
await client.deploy(config, {
cloudCredentialName: 'my-aws-account',
})Catalog / BYOC validation: Node AppSDK → Multi-Cloud.
Events & Webhooks
client.events — HMAC-signed webhooks (at-least-once) and a live event stream (at-most-once). React to agent.deployed / agent.deleted / job.* instead of polling.
const sub = await client.events.createWebhook({
url: 'https://ci.example.com/hooks/hikigai',
eventTypes: ['agent.deployed', 'agent.deleted'],
description: 'deploy notifications',
})
console.log(sub.secret) // shown once — store it
// Also: listWebhooks, getWebhook, updateWebhook, deleteWebhook, rotateSecret
const stream = client.events.stream(['job.*', 'agent.deployed'])
await stream.connect()
for await (const event of stream) {
console.log(event.type, event.data)
}
await stream.close()Complete Examples
import {
AgentClient,
InputSchema,
OutputSchema,
tool,
} from '@hikigai/agent-sdk'
const client = new AgentClient()
// Define tools
const searchCodes = tool(function search(q: string) {
return JSON.stringify([{ code: 'E11.9', desc: 'Type 2 diabetes' }])
})
// Deploy agent
const agent = await client.deploy({
name: 'icd-cpt-coder',
displayName: 'ICD-10 & CPT Coder',
description: 'Medical coding assistant',
instruction: 'You are a medical coding expert...',
model: 'claude-3.5-sonnet',
category: 'coding-billing',
version: '1.0.0',
inputSchema: new InputSchema({
clinical_note: { type: 'string', required: true },
}),
outputSchema: new OutputSchema({
icd10_codes: { type: 'array' },
cpt_codes: { type: 'array' },
}),
tools: [searchCodes],
hipaaCompliant: true,
})
console.log(`Deployed: ${agent.slug}`)Error Handling
import {
HikigaiError,
AuthenticationError,
RateLimitError,
ValidationError,
DeploymentError,
} from '@hikigai/agent-sdk'
try {
const agent = await client.deploy(config)
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API key')
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after ${error.retryAfter}ms`)
} else if (error instanceof ValidationError) {
console.error(`Validation error: ${error.message}`)
} else if (error instanceof HikigaiError) {
console.error(`API error: ${error.message}`)
}
}