npm package: @kairnsh/sdk
Description: Official SDK for Kairn — agent infrastructure for AI products
Latest version: 0.1.3

# @kairnsh/sdk

Official Node.js SDK for [Kairn](https://kairn.sh) — the agentic layer for existing products.

Connect your backend endpoints as agent-callable skills. Add persistent, memory-aware agents to your product without rebuilding anything. Each user gets their own isolated agent context. Agents remember across sessions, run on schedules, and take real actions against your existing API.

## Install

```bash
npm install @kairnsh/sdk
```

## Quickstart

```ts
import Kairn from '@kairnsh/sdk'

const kairn = new Kairn({ apiKey: 'kairn_sk_...' })

// 1. Create an agent identity (soul)
await kairn.souls.create({
  name: 'assistant',
  label: 'Product Assistant',
  soulMd: `
    Hired as a senior product specialist. You know the product deeply and
    genuinely care about helping users get value from it. Concise, warm,
    action-oriented. Use memory to make every interaction feel personal —
    never ask for information you already have.
  `,
  agentsMd: `
    Product: Acme — B2B SaaS project management tool.
    Users: team leads and individual contributors, mostly non-technical.
  `,
  defaultModel: 'claude-sonnet-4-6',
})

// 2. Seed context from your database
await kairn.memory.set('user_123', 'default', {
  name: 'Alex',
  plan: 'pro',
  company: 'Acme Corp',
})

// 3. Invoke the agent
const result = await kairn.invoke({
  agent: 'assistant',
  userId: 'user_123',
  input: 'What can you help me with?',
})

console.log(result.output)
```

---

## Invocation modes

Kairn supports three invocation modes. Choose based on your use case.

### `kairn.invoke()` — Sync

Waits for the full response before returning. Best for scripts, quick tests, and integrations where a simple request/response model is all you need.

```ts
const result = await kairn.invoke({
  agent: 'assistant',
  userId: 'user_123',
  input: 'What is my current plan?',
})

console.log(result.output)
// → "You're on the Pro plan, Alex. You have 3 seats used of 10..."

console.log(result.memoryDelta)   // keys the agent wrote this turn
console.log(result.toolCalls)     // tools the agent called
console.log(result.tokensInput)   // token counts for billing
```

**When to use:** CLI tools, server-side scripts, simple API integrations, curl testing.

---

### `kairn.stream()` — Streaming SSE

Streams tokens as they are generated. The user sees output immediately. Best for any interactive UI — chat, assistant panels, copilot sidebars.

```ts
for await (const event of kairn.stream({
  agent: 'assistant',
  userId: 'user_123',
  input: 'Summarise my account status.',
})) {
  switch (event.type) {
    case 'token':
      process.stdout.write(event.text)       // stream to UI
      break
    case 'tool_start':
      console.log(`calling ${event.name}...`) // show tool activity
      break
    case 'tool_end':
      console.log(`${event.name} done`)
      break
    case 'memory_delta':
      console.log('memory written:', event.delta)
      break
    case 'done':
      console.log('\nfull output:', event.output)
      console.log('duration:', event.durationMs, 'ms')
      break
    case 'error':
      console.error('agent error:', event.error)
      break
  }
}
```

**When to use:** Chat interfaces, real-time agent UIs, Next.js / React streaming, any user-facing interaction.

---

### `kairn.invokeAsync()` — Async (background job)

Queues the invocation and returns a `JobHandle` immediately — no connection held open, no timeout risk. The agent runs in the background. Poll for the result or provide a webhook callback.

Best for long-running tasks, workflow triggers, automations, and any scenario where the user is not actively waiting.

**Option 1: Fire and wait**

```ts
const job = await kairn.invokeAsync({
  agent: 'assistant',
  userId: 'user_123',
  input: 'Research our top 10 competitors and write a summary to memory.',
})

// Auto-polls every 2s until completed or failed
const result = await job.wait()
console.log(result.output)
```

**Option 2: Fire, store job ID, come back later**

```ts
const job = await kairn.invokeAsync({
  agent: 'advisor',
  userId: req.user.id,
  input: req.body.task,
})

// Store job.jobId in your DB, return 202 to your client
await db.jobs.create({ id: job.jobId, userId: req.user.id })
return res.status(202).json({ jobId: job.jobId })

// ...later, from a separate request...
const status = await kairn.jobs.get(savedJobId)
if (status.status === 'completed') {
  return res.json({ output: status.output })
}
```

**Option 3: Webhook callback — no polling needed**

```ts
await kairn.invokeAsync({
  agent: 'advisor',
  userId: 'user_123',
  input: 'Run the weekly review task.',
  callbackUrl: 'https://yourapp.com/webhooks/kairn',
})
// Kairn POSTs the result to your URL when done
```

**`job.wait()` options:**

```ts
const result = await job.wait({
  intervalMs: 3000,   // poll every 3s (default: 2000)
  timeoutMs: 60000,   // give up after 60s (default: 120000)
})
```

**When to use:** Long-running agent tasks (>10s), background automations, workflow triggers, fire-and-forget jobs.

---

## Connecting your backend as skills

Register your existing API endpoints as agent-callable skills. The agent invokes them automatically when relevant — no rebuild required.

```ts
// Register an existing endpoint as a skill
await kairn.skills.create({
  name: 'upgrade_plan',
  label: 'Upgrade Plan',
  descriptionMd: 'Upgrades the user\'s subscription plan.',
  webhookUrl: 'https://yourapi.com/billing/upgrade',
  httpMethod: 'POST',
  parameters: {
    type: 'object',
    properties: {
      plan: { type: 'string', description: 'Target plan: pro | enterprise' },
    },
    required: ['plan'],
  },
  headers: { Authorization: 'Bearer YOUR_INTERNAL_TOKEN' },
})

// Add the skill to a soul
await kairn.souls.update('assistant', {
  allowedSkills: ['upgrade_plan'],
})

// Now the agent will call your endpoint when the user asks to upgrade
const result = await kairn.invoke({
  agent: 'assistant',
  userId: 'user_123',
  input: 'Upgrade me to Pro.',
})
// Agent calls POST /billing/upgrade automatically
```

---

## Memory

The agent reads and writes memory automatically. You can also seed or update it directly.

```ts
// Seed context before the first invocation
await kairn.memory.set('user_123', 'default', {
  name: 'Alex',
  plan: 'pro',
  timezone: 'Europe/Paris',
})

// Read current memory
const memory = await kairn.memory.get('user_123', 'default')

// Delete a specific key
await kairn.memory.set('user_123', 'default', { temp_flag: null })

// Read all memory across all roles
const allMemory = await kairn.memory.get('user_123')

// Wipe all memory for a user (e.g. on account deletion)
await kairn.memory.delete('user_123')
```

---

## Multi-turn sessions

Pass a `sessionId` to continue a conversation. The agent has full message history within the session.

```ts
const sessionId = `chat_${Date.now()}`

await kairn.invoke({ agent: 'assistant', userId, sessionId, input: 'My invoice looks wrong.' })
await kairn.invoke({ agent: 'assistant', userId, sessionId, input: 'Can you fix it?' })
// Agent remembers the full conversation
```

---

## Mandates — scheduled agents

Schedule an agent to run autonomously on a cron schedule. The agent uses the user's persistent memory and the soul's skill permissions.

```ts
await kairn.mandates.create({
  userId: 'user_123',
  soul: 'assistant',
  label: 'Weekly check-in',
  taskPrompt: 'Review the user\'s goals and write a weekly progress summary to memory.',
  trigger: {
    type: 'cron',
    cron: '0 9 * * 1', // Every Monday at 9am UTC
  },
})

// Pause and cancel
await kairn.mandates.pause(mandate.id)
await kairn.mandates.cancel(mandate.id)
```

---

## Invocations — audit log

```ts
// List recent invocations
const { invocations } = await kairn.invocations.list({
  userId: 'user_123',
  status: 'completed',
  limit: 20,
})

// Get a single invocation
const invocation = await kairn.invocations.get(invocationId)

// Usage stats
const stats = await kairn.invocations.stats({ soul: 'assistant' })
console.log(stats.total_calls, stats.avg_duration_ms)
```

---

## Error handling

```ts
import Kairn, { KairnError } from '@kairnsh/sdk'

try {
  await kairn.invoke({ agent: 'assistant', userId, input })
} catch (err) {
  if (err instanceof KairnError) {
    console.error(err.message)    // human-readable message
    console.error(err.status)     // HTTP status code
    console.error(err.errorType)  // 'quota_exceeded' | 'rate_limit_exceeded' | 'soul_not_found' | 'soul_invalid' | 'async_job_failed' | 'async_job_timeout'
  }
}
```

---

## TypeScript

The SDK is fully typed. Key types:

```ts
import type {
  InvokeParams,
  InvokeResult,
  AsyncInvokeParams,
  AsyncJob,
  StreamEvent,
  MemoryMap,
  MemoryDelta,
  Soul,
  SoulCreateParams,
  Skill,
  SkillCreateParams,
  Mandate,
  MandateCreateParams,
  Invocation,
  KairnOptions,
} from '@kairnsh/sdk'
```

---

## Resources

- [Documentation](https://docs.kairn.sh)
- [Quickstart](https://docs.kairn.sh/quickstart)
- [API Reference](https://docs.kairn.sh/api-reference/invoke)
- [Use cases](https://docs.kairn.sh/use-cases/customer-support)

## License

MIT