Hikigai

@hikigai/app-sdk

Application SDK

Official Node.js SDK for invoking healthcare AI agents in your applications. Full streaming, WebSocket, and session support with type-safe responses.

v0.1.6Node.js 18+StreamingWebSocketRooms & Identity

Installation

Choose your preferred package manager:

npm

npm install @hikigai/app-sdk

yarn

yarn add @hikigai/app-sdk

pnpm

pnpm add @hikigai/app-sdk

Quick Start

import { AppClient } from '@hikigai/app-sdk'

// Initialize client
const client = new AppClient({
  apiKey: process.env.HIKIGAI_API_KEY,
  projectId: process.env.HIKIGAI_PROJECT_ID,
})

// Get an agent
const agent = await client.agent('medical-coder')

// Basic invocation
const response = await agent.invoke('Patient presents with fever and cough...')
console.log(response.content)

// Streaming responses
for await (const chunk of agent.stream('Tell me about type-2 diabetes')) {
  process.stdout.write(chunk)
}

// Session-based conversations
const sessionAgent = agent.withSession('patient-123')
await sessionAgent.invoke('What is the treatment?')
await sessionAgent.invoke('Any contraindications?') // Remembers context

Core Concepts

AppClient

Main interface for agent invocation and management.

class AppClient {
  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
    sonaUrl?: string         // Optional SONA service URL
    sonaApiKey?: string      // Optional SONA API key
  })

  readonly storage: StorageClient
  readonly identity: IdentityClient
  readonly sona: SONAClient

  agent(agentId: string): Promise<RuntimeAgent>
  listAgents(options?: {
    category?: string; tags?: string[]; search?: string
  }): Promise<RuntimeAgent[]>
  invoke(agentId: string, input: string | Record<string, unknown>,
    options?: InvokeOptions): Promise<InvokeResponse>
  stream(agentId: string, input: string | Record<string, unknown>,
    options?: StreamOptions): AsyncIterable<string>
  live(agentId: string, sessionId?: string, userId?: string,
    context?: Record<string, unknown>): StreamSession
  createSession(options?: {
    externalUserRef?: string; ttlSeconds?: number;
    appId?: string; metadata?: Record<string, unknown>
  }): Promise<SessionContext>
  session(sessionId: string, options?: { appId?: string }): SessionContext
  listSessions(options?: {
    userRef?: string; limit?: number; cursor?: string
  }): Promise<{ sessions: unknown[]; nextCursor?: string }>
  getAuthToken(): Promise<AuthTokenResponse>
  close(): void
}

RuntimeAgent

Represents a deployed agent ready for invocation.

interface RuntimeAgent {
  id: string
  name: string
  slug: string
  version?: string
  description?: string
  status: 'active' | 'pending' | string
  endpoint?: string

  invoke(input: string | Record<string, unknown>,
    options?: InvokeOptions): Promise<InvokeResponse>
  stream(input: string | Record<string, unknown>,
    options?: StreamOptions): AsyncIterable<string>
  withSession(sessionId: string): RuntimeAgent
  liveSession(sessionId?: string): StreamSession
}

// InvokeOptions / StreamOptions
// sessionId?, provider?, model?, timeout?, connectors?, pluginContext? (invoke only)

Invocation Methods

Multiple ways to interact with agents.

Basic Invocation

const response = await agent.invoke(
  'Clinical note to analyze',
  {
    sessionId: 'patient-123',
    timeout: 60000,
  }
)

console.log(response.content)           // Agent response
console.log(response.metadata.latencyMs) // Performance info

MCP Connectors

Pass linked MCP connector slugs in connectors. The platform injects apiKey and projectId from your AppClient credentials automatically.

import type { McpConnectors } from '@hikigai/app-sdk'

const connectors: McpConnectors = {
  'idexx-mcp-server': {},  // platform auth injected automatically
}

const response = await agent.invoke('Prepare patient chart', { connectors })

// Optional HTTP headers (Claude Desktop style)
const withHeaders: McpConnectors = {
  'idexx-mcp-server': {
    headers: {
      'X-API-Key': process.env.HIKIGAI_API_KEY!,
      'X-Project-Id': process.env.HIKIGAI_PROJECT_ID!,
    },
  },
}

Agent Discovery

// Get specific agent
const agent = await client.agent('medical-coder')

// List all agents
const agents = await client.listAgents()

// Search agents
const filtered = await client.listAgents({
  category: 'medical-coding',
  tags: ['icd-10'],
  search: 'coding',
})

Streaming Responses

Process responses in real-time as they arrive.

// Stream text responses
for await (const chunk of agent.stream('Analyze this report...')) {
  process.stdout.write(chunk)
}

// Collect streamed data
let fullResponse = ''
for await (const chunk of agent.stream(input)) {
  fullResponse += chunk
}

console.log('Complete response:', fullResponse)

Session Management

Multi-turn conversations with persistent context via withSession / sessionId.

const sessionAgent = agent.withSession('patient-789')

// First turn - establish context
await sessionAgent.invoke('I have been experiencing chest pain')

// Second turn - knows about chest pain
await sessionAgent.invoke('Is this serious?')

// Third turn - maintains full conversation history
await sessionAgent.invoke('What tests should I get?')

Session Context Store

Server-side key/value context (SessionContext), separate from conversation memory on invoke.

const ctx = await client.createSession({
  externalUserRef: 'dr-smith',
  ttlSeconds: 3600,
})
await ctx.set('patient_id', 'MRN-12345')
console.log(await ctx.get('patient_id'))
console.log(await ctx.getAll())

const again = client.session(ctx.sessionId)
await again.update({ chief_complaint: 'chest pain' })
await again.touch(7200)

WebSocket Live Sessions

Real-time bidirectional communication with agents via StreamSession.

const session = agent.liveSession()

await session.connect()
await session.start('text') // or 'audio'
await session.sendText('Hello, I need help')
// await session.sendAudio(pcmBuffer, 'audio/pcm;rate=16000')

for await (const event of session) {
  console.log(event)
  if (event.type === 'turn_complete') break
}

await session.end()
await session.close()

Rooms

Multi-party realtime rooms via RoomSession (presence, publish, sequence tracking).

import { RoomSession } from '@hikigai/app-sdk'

const room = new RoomSession({
  baseUrl: 'https://api.hikigai.com',
  apiKey: process.env.HIKIGAI_API_KEY!,
  projectId: process.env.HIKIGAI_PROJECT_ID!,
})

await room.connect()
await room.join('care-team-7')
await room.publish('care-team-7', 'note', { text: 'Labs ready' })

for await (const frame of room) {
  console.log(frame)
}

await room.close()

Storage

const uploaded = await client.storage.upload(
  'app_123',
  fileBuffer,
  'audio.wav',
  { contentType: 'audio/wav' },
)
const { signedUrl } = await client.storage.signedUrl('app_123', uploaded.objectId, 3600)
await client.storage.list('app_123')
await client.storage.delete('app_123', uploaded.objectId)

Response Models

interface InvokeResponse {
  content: string
  agentId: string
  agentVersion?: string
  sessionId?: string
  status: string
  output?: Record<string, unknown>
  confidence?: ClinicalConfidence      // score: 0–1
  safetyFlags: SafetyFlag[]            // severity: low|medium|high|critical
  citations: ClinicalCitation[]
  metadata: InvocationMetadata
  message?: string
  plugins?: Record<string, unknown>    // e.g. plugins.sona
}

interface InvocationMetadata {
  invocationId: string
  latencyMs?: number
  timestamp: Date
  status: string
  tokensUsed?: number
  toolsCalled: string[]
  phiRedacted: boolean
  traceId?: string
}

interface SafetyFlag {
  category: string
  severity: 'low' | 'medium' | 'high' | 'critical'
  message: string
  mitigation?: string
}

End-User Identity

Add signup, login, MFA, QR badge login, and SSO for your app's end users. Each app is backed by a dedicated AWS Cognito pool the platform provisions for you; login returns standard OIDC tokens. Reach it all through client.identity.

IdentityClient

interface IdentityClient {
  // Accounts & auth
  signup(appId: string, email: string, password: string,
    opts?: { firstName?: string; lastName?: string; role?: string; attributes?: Record<string, string>; metadata?: Record<string, unknown> }): Promise<Record<string, unknown>>
  confirm(appId: string, email: string, code: string): Promise<Record<string, unknown>>
  login(appId: string, email: string, password: string): Promise<TokenResult>
  refresh(appId: string, refreshToken: string): Promise<TokenResult>
  logout(appId: string, email: string): Promise<Record<string, unknown>>
  forgotPassword(appId: string, email: string): Promise<Record<string, unknown>>
  resetPassword(appId: string, email: string, code: string, newPassword: string): Promise<Record<string, unknown>>
  changePassword(appId: string, accessToken: string, currentPassword: string, newPassword: string): Promise<Record<string, unknown>>
  updateUser(userId: string, fields: Record<string, unknown>): Promise<Record<string, unknown>>
  listAppUsers(appId: string, opts?: { limit?: number; offset?: number }): Promise<Record<string, unknown>[]>
  deleteAppUser(appId: string, email: string): Promise<void>

  // MFA (TOTP)
  mfaAssociate(appId: string, accessToken: string): Promise<Record<string, unknown>>
  mfaVerify(appId: string, accessToken: string, code: string, opts?: { deviceName?: string }): Promise<Record<string, unknown>>
  mfaSetPreference(appId: string, accessToken: string, opts?: { enabled?: boolean }): Promise<Record<string, unknown>>
  mfaRespond(appId: string, email: string, session: string, code: string): Promise<TokenResult>

  // QR badge & SSO
  issueQrLogin(appId: string, email: string): Promise<Record<string, unknown>>
  qrLogin(appId: string, qrPayload: string): Promise<TokenResult>
  configureSso(appId: string, opts: { providerName: string; providerType: string; /* ... */ }): Promise<Record<string, unknown>>
  ssoAuthorizeUrl(appId: string, redirectUri: string, opts?: { provider?: string }): Promise<Record<string, unknown>>
  ssoExchange(appId: string, code: string, redirectUri: string): Promise<TokenResult>

  // Setup (one-time, per app)
  enableIdentity(appId: string, opts?: { mode?: string; enabledMethods?: string[] }): Promise<Record<string, unknown>>
  getIdentityConfig(appId: string): Promise<Record<string, unknown>>
  byoSetup(): Promise<Record<string, unknown>>
  byoTest(roleArn: string, opts?: { region?: string }): Promise<Record<string, unknown>>
}

Sign up, confirm, log in

attributes are Cognito profile fields (string values — e.g. phone_number in E.164). metadata is opaque app JSON stored on the platform EndUser (and as custom:metadata when supported). They are not interchangeable.

import { AppClient } from '@hikigai/app-sdk'

const client = new AppClient({
  apiKey: process.env.HIKIGAI_API_KEY,
  projectId: process.env.HIKIGAI_PROJECT_ID,
})

// 1. Register the end user
await client.identity.signup('app_123', 'clinician@hospital.com', 'Temp#Pass2026', {
  firstName: 'Asha',
  lastName: 'Mehta',
  role: 'clinician',
  // attributes → Cognito fields (strings only; e.g. phone_number in E.164)
  attributes: { phone_number: '+14155552671' },
  // metadata → opaque app JSON on EndUser (and custom:metadata when supported)
  metadata: { doctorID: '6278383837' },
})

// 2. Confirm (skip if the pool auto-confirms)
await client.identity.confirm('app_123', 'clinician@hospital.com', '123456')

// 3. Log in -> OIDC tokens
const tokens = await client.identity.login('app_123', 'clinician@hospital.com', 'Temp#Pass2026')
console.log(tokens.accessToken)

Forgot password

await client.identity.forgotPassword('app_123', 'clinician@hospital.com')
await client.identity.resetPassword('app_123', 'clinician@hospital.com', '123456', 'NewTemp#Pass2026')

Change password (logged in)

const tokens = await client.identity.login('app_123', 'clinician@hospital.com', 'Temp#Pass2026')
await client.identity.changePassword(
  'app_123', tokens.accessToken!, 'Temp#Pass2026', 'NewTemp#Pass2026',
)

Update role / profile

await client.identity.updateUser('eu_abc123', { role: 'doctor' })

Multi-factor login

const result = await client.identity.login('app_123', 'clinician@hospital.com', 'Temp#Pass2026')

if (result.status === 'challenge') {
  const code = await promptForTotpCode()
  const tokens = await client.identity.mfaRespond(
    'app_123', 'clinician@hospital.com', result.session!, code,
  )
  console.log(tokens.accessToken)
}

QR badge login

// Admin: issue a badge for the user (payload shown once)
const badge = await client.identity.issueQrLogin('app_123', 'clinician@hospital.com')
console.log(badge.qr_payload)   // encode into the printed badge

// Kiosk: scan the badge, exchange it for tokens
const tokens = await client.identity.qrLogin('app_123', scannedPayload)

Complete Example

import { AppClient } from '@hikigai/app-sdk'

const client = new AppClient()

async function clinicalWorkflow() {
  const diagnosisAgent = await client.agent('diagnosis-assistant')
  const codingAgent = await client.agent('clinical-coder')

  const sessionId = 'patient-dr-smith-2026'

  // Initial assessment
  const assessment = await diagnosisAgent.invoke(
    'Fever 38.5°C, cough, shortness of breath for 3 days',
    { sessionId }
  )
  console.log('Assessment:', assessment.content)

  // Follow-up question - remembers context
  const followUp = await diagnosisAgent.invoke(
    'Any chest pain or sputum production?',
    { sessionId }
  )
  console.log('Follow-up:', followUp.content)

  // Extract medical codes
  const coding = await codingAgent.invoke(assessment.content)
  console.log('Codes:', coding.content)

  // Stream treatment plan
  for await (const chunk of diagnosisAgent.stream(
    'What is the recommended treatment?',
    { sessionId }
  )) {
    process.stdout.write(chunk)
  }
}

Error Handling

import {
  HikigaiError,
  AuthenticationError,
  RateLimitError,
  AgentNotFoundError,
  InvocationError,
} from '@hikigai/app-sdk'

try {
  const response = await agent.invoke('...')
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid credentials')
  } else if (error instanceof RateLimitError) {
    console.error(`Rate limited. Retry after ${error.retryAfter}ms`)
  } else if (error instanceof AgentNotFoundError) {
    console.error('Agent not found')
  } else if (error instanceof InvocationError) {
    console.error(`Invocation failed: ${error.message}`)
  } else if (error instanceof HikigaiError) {
    console.error(`API error: ${error.message}`)
  }
}