npm package: @memoryintelligence/react
Description: React hooks for Memory Intelligence SDK
Author: Memory Intelligence
Latest version: 2.0.1

# @memoryintelligence/react

Official React hooks for [Memory Intelligence](https://memoryintelligence.io) SDK.

## Installation

```bash
npm install @memoryintelligence/react @memoryintelligence/sdk
# or
yarn add @memoryintelligence/react @memoryintelligence/sdk
```

## Quick Start

### 1. Setup Provider

Wrap your app with `MemoryClientProvider`:

```tsx
import { MemoryClientProvider } from '@memoryintelligence/react';

function App() {
  return (
    <MemoryClientProvider config={{ apiKey: process.env.REACT_APP_MI_API_KEY }}>
      <YourApp />
    </MemoryClientProvider>
  );
}
```

### 2. Use Hooks

```tsx
import { useMemorySearch, useDriftSubscription } from '@memoryintelligence/react';

function SearchPage() {
  const { search, data, loading } = useMemorySearch();

  return (
    <div>
      <button onClick={() => search('project planning')}>Search</button>
      {loading && <p>Searching...</p>}
      {data && <Results results={data.results} />}
    </div>
  );
}
```

## API Reference

### MemoryClientProvider

Provides Memory Intelligence client to child components.

```tsx
<MemoryClientProvider 
  config={{
    apiKey: string;
    baseUrl?: string;
    wsUrl?: string;
    userUlid?: string;
    orgUlid?: string;
  }}
>
  {children}
</MemoryClientProvider>
```

### useMemoryClient

Access the Memory Intelligence client directly.

```tsx
const client = useMemoryClient();
const umo = await client.umo.process('content');
```

### useMemorySearch

Search memories with reactive state management.

```tsx
const { search, data, loading, error, reset } = useMemorySearch();

// Trigger search
await search('query', { limit: 10, scope: Scope.USER });

// Access results
console.log(data?.results);
```

### useMemoryProcess

Process content into Meaning Objects.

```tsx
const { process, data, loading, error, reset } = useMemoryProcess();

// Process content
await process('Meeting notes about Q2 strategy', {
  source: 'web_form',
  metadata: { category: 'meetings' }
});

// Access created UMO
console.log(data?.umo_id);
```

### useDriftSubscription

Subscribe to real-time entity meaning changes.

```tsx
const { updates, latestUpdate, connected, error, clearUpdates } = useDriftSubscription({
  entityUlids: ['01ARZ3NDEV5ARZ3ND']
});

// Access updates
updates.forEach(update => {
  console.log(update.canonicalName, update.driftEpoch);
});
```

**Options:**
- `options.entityUlids` - Array of entity ULIDs to watch
- `options.orgUlid` - Organization filter
- `maxUpdates` - Maximum updates to retain (default: 50)

**Returns:**
- `updates` - Array of drift updates (newest first)
- `latestUpdate` - Most recent update
- `connected` - WebSocket connection status
- `error` - Error if any
- `clearUpdates()` - Clear update history

### useComputeProgress

Track long-running compute operations with progress.

```tsx
const { progress, result, isComplete, error, reset } = useComputeProgress(
  'analyze_decision_process',
  { narrative_ulid: '01ARZ3NDFQ69G5FAVSV4RRFF' }
);

// Access progress
if (progress) {
  console.log(`${progress.progressPercent}%: ${progress.message}`);
}

// Access result when complete
if (isComplete && result) {
  console.log(result.result);
}
```

**Options:**
- `enabled` - Whether to start subscription (default: true)

**Returns:**
- `progress` - Current progress update
- `result` - Final result when complete
- `connected` - WebSocket connection status
- `error` - Error if any
- `isComplete` - Whether operation finished
- `reset()` - Reset state

## Examples

### Drift Monitor Component

```tsx
import { useDriftSubscription } from '@memoryintelligence/react';

function DriftMonitor({ entityUlid }) {
  const { updates, connected, error } = useDriftSubscription({
    entityUlids: [entityUlid]
  });

  if (error) return <div>Error: {error.message}</div>;

  return (
    <div>
      <div className="status">
        {connected ? '🟢 Live' : '⚪ Disconnected'}
      </div>

      <div className="updates">
        {updates.map((update, i) => (
          <div key={i} className={!update.meaningStable ? 'warning' : ''}>
            <h4>{update.canonicalName}</h4>
            <p>Epoch: {update.driftEpoch} | Magnitude: {update.driftMagnitude.toFixed(3)}</p>
            
            {!update.meaningStable && (
              <div className="alert">
                <strong>Meaning Changed</strong>
                <p>{update.explainHuman.driftSummary}</p>
                <ul>
                  {update.explainHuman.whatChanged.changes.map((change, j) => (
                    <li key={j}>{change}</li>
                  ))}
                </ul>
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
}
```

### Search with Form

```tsx
import { useState } from 'react';
import { useMemorySearch } from '@memoryintelligence/react';

function SearchBox() {
  const [query, setQuery] = useState('');
  const { search, data, loading, error } = useMemorySearch();

  const handleSubmit = async (e) => {
    e.preventDefault();
    await search(query, { limit: 20 });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search memories..."
      />
      <button type="submit" disabled={loading}>
        {loading ? 'Searching...' : 'Search'}
      </button>

      {error && <div className="error">{error.message}</div>}

      {data && (
        <div className="results">
          <p>Found {data.count} results</p>
          <ul>
            {data.results.map((result) => (
              <li key={result.umo_id}>
                <h3>{result.summary}</h3>
                <p>Score: {result.score.toFixed(3)}</p>
              </li>
            ))}
          </ul>
        </div>
      )}
    </form>
  );
}
```

### Capture Form

```tsx
import { useState } from 'react';
import { useMemoryProcess } from '@memoryintelligence/react';

function CaptureForm() {
  const [content, setContent] = useState('');
  const { process, data, loading, error, reset } = useMemoryProcess();

  const handleSubmit = async (e) => {
    e.preventDefault();
    await process(content, { source: 'web_form' });
    setContent(''); // Clear on success
  };

  return (
    <form onSubmit={handleSubmit}>
      <textarea
        value={content}
        onChange={(e) => setContent(e.target.value)}
        placeholder="Enter your memory..."
        rows={6}
      />
      <button type="submit" disabled={loading || !content}>
        {loading ? 'Processing...' : 'Capture Memory'}
      </button>

      {error && <div className="error">{error.message}</div>}
      {data && <div className="success">Created: {data.umo_id}</div>}
    </form>
  );
}
```

### Compute Progress

```tsx
import { useComputeProgress } from '@memoryintelligence/react';

function AnalysisProgress({ narrativeUlid }) {
  const { progress, result, isComplete, error } = useComputeProgress(
    'analyze_decision_process',
    { narrative_ulid: narrativeUlid }
  );

  if (error) return <div>Error: {error.message}</div>;

  if (isComplete && result) {
    return (
      <div className="complete">
        <h3>Analysis Complete</h3>
        <p>Execution time: {result.executionTimeMs}ms</p>
        <pre>{JSON.stringify(result.result, null, 2)}</pre>
      </div>
    );
  }

  return (
    <div className="progress">
      <h3>Analyzing...</h3>
      {progress && (
        <>
          <progress value={progress.progressPercent} max={100} />
          <p>{progress.progressPercent}%: {progress.message}</p>
        </>
      )}
    </div>
  );
}
```

## TypeScript Support

Fully typed with TypeScript. All hooks include proper type inference:

```tsx
import type { SearchResponse, MeaningObject } from '@memoryintelligence/react';

const { data } = useMemorySearch(); // data: SearchResponse | null
const { data: umo } = useMemoryProcess(); // umo: MeaningObject | null
```

## License

MIT