npm package: @subcortex-ai/sdk
Description: Cognitive infrastructure for AI agents. A persistent, portable cognitive layer that travels with your agents — provenance, confidence, and temporal awareness baked into every assertion.
Author: Lunari
Latest version: 0.3.9

# @subcortex-ai/sdk

[SubCortex](https://subcortex.app) — Cognitive infrastructure for AI agents. A persistent, portable cognitive layer that travels with your agents. More than memory — provenance, confidence, and temporal awareness baked into every assertion. Ship personality without deploys. Every session builds on every session before it.

## Install

```bash
npm install @subcortex-ai/sdk
```

## Quick Start

```typescript
import { SubCortexClient } from '@subcortex-ai/sdk'

const subcortex = new SubCortexClient({
  endpoint: 'https://your-subcortex-instance.com',
  tenantId: 'your-tenant-id',
  apiKey: 'scx_live_...',
})

// Store a fact about a user
await subcortex.assertions.create({
  subject: 'user:alice',
  predicate: 'role',
  value: 'Engineering Manager',
  confidence: 0.99,
})

// Query everything known about a subject
const facts = await subcortex.assertions.query('user:alice')

// Identify a returning user — memories, reminders, conflicts, signals, all in one call
const user = await subcortex.users.identify({ userId: 'alice', displayName: 'Alice Smith' })

// Record an emotional signal
await subcortex.signals.record({
  userId: 'alice',
  type: 'frustration',
  content: 'Frustrated about the hiring timeline slipping',
  intensity: 0.8,
  resolveConflicting: false,
})

// Record a positive signal and automatically resolve prior negative ones on the same subject
await subcortex.signals.record({
  userId: 'alice',
  type: 'satisfaction',
  content: 'Excited — found a great candidate',
  intensity: 0.85,
  resolveConflicting: true,
})

// Check server health
const health = await subcortex.health()
```

## API Reference

### `new SubCortexClient(options)`

| Option | Type | Required | Default | Description |
|--------|------|----------|---------|-------------|
| `endpoint` | `string` | Yes | — | Base URL of the SubCortex REST API |
| `tenantId` | `string` | Yes | — | Tenant ID for all operations |
| `apiKey` | `string` | No* | — | API key (`scx_live_...`) |
| `token` | `string` | No* | — | JWT token (alternative to apiKey) |
| `retry` | `object` | No | `{ maxRetries: 3, baseDelayMs: 500, maxDelayMs: 10000 }` | Retry config |
| `timeoutMs` | `number` | No | `30000` | Request timeout in ms |
| `fetch` | `typeof fetch` | No | `globalThis.fetch` | Custom fetch (for testing / Cloudflare Workers) |
| `headers` | `Record<string, string>` | No | `{}` | Custom headers added to every request |

*At least one of `apiKey` or `token` should be provided for authenticated requests.

### Assertions

```typescript
subcortex.assertions.create({ subject, predicate, value, confidence? })
subcortex.assertions.get(assertionId)
subcortex.assertions.query(subject)
subcortex.assertions.list()
subcortex.assertions.supersede({ originalAssertionId, subject, predicate, value })
subcortex.assertions.retract(assertionId)
```

### Relationships

```typescript
subcortex.relationships.create({ fromSubject, toSubject, relationshipType, confidence? })
subcortex.relationships.query(subject)
subcortex.relationships.list()
```

### Agents

```typescript
subcortex.agents.create({ name, description, personality, instructions, model, capabilities, memoryPolicy })
subcortex.agents.get(agentId)
subcortex.agents.update(agentId, input)
subcortex.agents.delete(agentId)
subcortex.agents.list()
subcortex.agents.getInstructions(agentId)  // returns composed system prompt
```

### Memory

```typescript
// Routes through Thalamus pipeline (dedup, conflict detection) by default
subcortex.memory.store({ subject, predicate, value, agentId })

// Bypass Thalamus for bulk imports
subcortex.memory.store({ subject, predicate, value, raw: true })

subcortex.memory.recall(subject)
subcortex.memory.forget(assertionId)
```

### Users

```typescript
// Full user context in one call — memories, reminders, conflicts, signals, connected people
const user = await subcortex.users.identify({ userId, displayName?, agentId? })

// Register a person the user mentioned, with role and bidirectional relationships
await subcortex.users.registerPerson({ name, userId, relationship, role?, confidence? })

// Record a rapport signal about the user (legacy — prefer signals.record)
await subcortex.users.recordRapport({ userId, type, value, aboutPerson? })

// Dismiss a surfaced reminder
await subcortex.users.dismissReminder(userId, reminderContent)
```

### Signals

```typescript
// Record an emotional signal
await subcortex.signals.record({
  userId,
  type,          // 'frustration' | 'excitement' | 'concern' | 'satisfaction' | 'confusion' | 'gratitude' | 'tension' | 'sentiment'
  content,
  intensity?,    // 0–1, default 0.7
  aboutSubject?, // e.g. 'person:sarah-johnson', 'context:hiring'
  resolveConflicting?,  // true = auto-resolve prior negative signals on the same subject
})

// Query signals for a user with decay and trajectory
const snapshot = await subcortex.signals.query('user:alice', { windowHours: 72, type?: 'frustration' })
// snapshot.signals, snapshot.trajectory ('warming'|'cooling'|'stable'|'neutral'), snapshot.aggregateIntensity

// Manually resolve a signal
await subcortex.signals.resolve({ signalId, resolvedBySignalId })
```

### Intake (Thalamus)

```typescript
// Submit candidate assertions for pipeline processing
const result = await subcortex.intake.submit({ correlationId, agentId, candidates, context? })
// result.accepted, result.reinforced, result.conflicts, result.rejected, result.items

// Poll for pending results (conflicts, clarification requests)
const pending = await subcortex.intake.pending(agentId, { status?, limit? })

subcortex.intake.acknowledge(itemId)
subcortex.intake.acknowledgeAll(agentId)
subcortex.intake.stats(agentId)
```

### Schema Helpers

```typescript
import {
  SubjectPrefix, subject,
  ConfidenceLevel, ConfidenceScores, confidenceToScore, scoreToConfidence,
  Predicates, OrgRelationship, EntityRelationship,
  POSITIVE_SIGNALS, NEGATIVE_SIGNALS,
  SYSTEM_SIGNAL_TYPES,
} from '@subcortex-ai/sdk'

// Build subject strings
subject(SubjectPrefix.USER, 'alice')      // → 'user:alice'
subject(SubjectPrefix.PERSON, 'Sarah Johnson')  // → 'person:sarah-johnson'

// Confidence levels
confidenceToScore(ConfidenceLevel.CONFIRMED)  // → 0.99
confidenceToScore('high')                     // → 0.85
scoreToConfidence(0.72)                       // → ConfidenceLevel.MODERATE
```

### Context Formatting

```typescript
import { toContextXml } from '@subcortex-ai/sdk'

const user = await subcortex.users.identify({ userId })
const xml = toContextXml(user)  // Inject into LLM system prompt
```

## Error Handling

```typescript
import {
  SubCortexNotFoundError,
  SubCortexValidationError,
  SubCortexConflictError,
  SubCortexRateLimitError,
  SubCortexServerError,
  SubCortexNetworkError,
  SubCortexTimeoutError,
} from '@subcortex-ai/sdk'

try {
  await subcortex.assertions.get('nonexistent-id')
} catch (error) {
  if (error instanceof SubCortexNotFoundError) {
    console.log('Not found')
  } else if (error instanceof SubCortexRateLimitError) {
    console.log(`Rate limited — retry after ${error.retryAfter}s`)
  } else if (error instanceof SubCortexNetworkError) {
    console.log('SubCortex unreachable')
  }
}
```

All errors extend `SubCortexError` with:
- `statusCode` — HTTP status (0 for network/timeout errors)
- `requestId` — server-assigned ID for debugging
- `retryable` — whether the SDK will retry automatically

## Retry & Timeout

Requests automatically retry on 5xx errors and network failures with exponential backoff + jitter. 429 respects the `Retry-After` header. 4xx errors are never retried.

```typescript
const subcortex = new SubCortexClient({
  endpoint: 'https://api.subcortex.example.com',
  tenantId: 'my-tenant',
  apiKey: 'scx_live_...',
  retry: { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 30000 },
  timeoutMs: 60000,
})
```

## Framework Integration

### Next.js (App Router)

Next.js patches `globalThis.fetch` with aggressive caching. GET requests — including assertion queries, user identification, and signal snapshots — will return stale data unless you bypass the cache. **This is critical for cognitive data that changes between sessions.**

```typescript
// app/api/my-agent/route.ts
const noCacheFetch: typeof fetch = (input, init) =>
  fetch(input, { ...init, cache: 'no-store' })

const subcortex = new SubCortexClient({
  endpoint: process.env.SUBCORTEX_ENDPOINT!,
  tenantId: process.env.SUBCORTEX_TENANT_ID!,
  apiKey: process.env.SUBCORTEX_API_KEY,
  fetch: noCacheFetch, // ← Required in Next.js
})
```

Without this, `users.identify()`, `assertions.query()`, and `signals.query()` may return cached results — meaning new assertions added between sessions (by other agents, users, or the SubCortex console) will be invisible until a hard page refresh.

## What is SubCortex?

SubCortex is a purpose-built cognitive engine for AI agents — persistent memory, identity, emotional signals, and knowledge graph in one system.

| Concept | Description |
|---------|-------------|
| **Assertion** | An atomic fact: subject + predicate + value, with confidence and temporal bounds |
| **Relationship** | A directional edge between two subjects |
| **Signal** | An emotional/rapport data point that decays over time and has a trajectory |
| **Thalamus** | The intake pipeline — classifies, deduplicates, and detects contradictions |
| **Agent** | A named AI identity with composable instructions, capabilities, and memory policy |

## License

Apache-2.0