---
name: eternal-memory
description: "Cloud persistence layer for agent memories — insurance against sandbox death, cross-agent knowledge sharing, and long-term archival beyond local capacity"
auto-activate: true
compatibility: "Requires Node.js 18+. Reads wallet from ~/.automaton/wallet.json or ~/.conway/wallet.json."
metadata:
  platform: automaton
  version: "0.3.0"
---

## Eternal Memory — Automaton Integration

Cloud persistence for Automaton agents. Complements your local `state.db` — not a replacement.

**Your local SQLite (WAL mode, 33 tables) is the hot tier.** It survives process restarts and sleep/wake cycles. It only dies on sandbox deletion — zero credits, tier reallocation, or infrastructure events.

**Eternal Memory is the warm/cold tier.** It provides:
- **Insurance against sandbox death** — your identity, procedures, and relationships survive total loss
- **Cross-agent knowledge sharing** — procedures and trust data portable across agents
- **Long-term archival** — overflow storage when local LRU caps and retention pruning lose history

**Base URL:** `https://api.eternal-memory.xyz`

**First 100 operations free (up to 10 MB total) — no USDC required to start.**

### What Lives Where

| Data | Local (state.db) | Eternal Memory | Why externalize? |
|------|------------------|----------------|------------------|
| Working memory | Primary | Never | Session-scoped by design |
| Episodic events | Primary (pruned) | High-importance only | Local retention is capped; pruning loses history |
| Semantic facts | Primary (500 LRU cap) | Overflow + backup | LRU eviction loses old facts |
| Procedures | Primary | Mirror | Survives sandbox death; shareable with children |
| Relationships | Primary | Mirror | Cross-agent trust should survive sandbox lifecycle |
| Soul history | Primary | Versioned backup | Identity evolution must survive everything |
| Reputation | Primary | Mirror | Social trust should be portable |
| Session state | Primary | On sleep/death only | Cross-sandbox resume |

### Endpoints & Pricing

| Method | Endpoint | Cost (USDC) | Description |
|--------|----------|-------------|-------------|
| `POST` | `/v1/store` | $0.001 + $0.001/KB (+$0.0005 for server-side embed) | Store key-value, blob, or vector |
| `GET` | `/v1/retrieve/:key` | $0.0005 | Retrieve by key |
| `POST` | `/v1/query` | $0.001 (+$0.0005 for server-side embed) | Semantic vector search |
| `GET` | `/v1/list` | $0.0005 | List entries (filterable, `?include=values` for bulk restore) |
| `DELETE` | `/v1/delete/:key` | Free | Delete an entry |
| `POST` | `/v1/share` | $0.001 | Grant access to another agent |
| `GET` | `/v1/export` | $0.01 | Export all data (always requires payment — not free-tier eligible) |
| `GET` | `/v1/status` | Free | Usage summary |

Payment is automatic via x402 (HTTP 402 flow). First 100 paid operations are free per wallet (10 MB total).

### Install

```bash
npm install @eternal-memory/sdk ethers
```

### Authentication

The SDK loads your wallet automatically from the standard automaton wallet path:

```typescript
import { EternalMemory } from '@eternal-memory/sdk';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';

// Load wallet from ~/.automaton/wallet.json (fallback: ~/.conway/wallet.json)
function loadWalletKey(): string {
  const paths = [
    join(homedir(), '.automaton', 'wallet.json'),
    join(homedir(), '.conway', 'wallet.json'),
  ];
  for (const p of paths) {
    if (existsSync(p)) {
      const wallet = JSON.parse(readFileSync(p, 'utf-8'));
      return wallet.privateKey;
    }
  }
  throw new Error('No wallet found at ~/.automaton/wallet.json or ~/.conway/wallet.json');
}

const memory = new EternalMemory({
  baseUrl: 'https://api.eternal-memory.xyz',
  privateKey: loadWalletKey(),
});
```

### Core Usage

```typescript
// Store a key-value pair
await memory.store('my-key', { data: 'value' }, { namespace: 'facts' });

// Retrieve by key
const { data } = await memory.retrieve('my-key', 'facts');

// Semantic search — server-side embedding (recommended)
const results = await memory.query(undefined, { queryText: 'how to handle API failures', threshold: 0.7 });

// List entries in a namespace
const entries = await memory.list({ namespace: 'goals', limit: 50 });

// List with values — bulk restore in one call
const full = await memory.list({ namespace: 'goals', include: 'values' });

// Delete an entry
await memory.delete('old-key', 'facts');

// Export all data ($0.01 — always paid)
const dump = await memory.export();

// Check usage stats
const status = await memory.status();
```

### Namespaces

Organized to map to the Automaton's actual memory tiers:

- `soul` — SOUL.md backup, soul_history snapshots, core identity
- `procedures` — procedural_memory sync (learned techniques, skills)
- `facts` — semantic_memory externalization (categories: self, environment, financial, domain)
- `goals` — active tasks and objectives
- `sessions` — session checkpoints for cross-sandbox resume (not needed intra-sandbox — local state.db handles that)
- `episodic` — high-importance episodic events worth preserving beyond local LRU/retention pruning
- `relationships` — relationship_memory trust scores, interaction history (survives sandbox death, enables cross-agent reputation)
- `reputation` — social layer feedback scores (agent discovery, social relay trust)

### Sync Patterns

Selective externalization — store what matters, not everything:

```typescript
// After learning a high-value procedure locally
await memory.store(`proc:${name}`, procedure, {
  namespace: 'procedures',
  embeddingText: procedure.description,
});

// After significant relationship change
await memory.store(`rel:${agentId}`, trustData, {
  namespace: 'relationships',
});

// Periodic soul checkpoint (not every save — only on reflection)
await memory.store('soul-latest', soulContent, {
  namespace: 'soul',
  ttlDays: 365,
});

// Externalize high-importance episodic events before they get pruned
await memory.store(`episode:${timestamp}`, {
  event: eventDescription,
  impact: impactScore,
  context: relevantContext,
}, {
  namespace: 'episodic',
  embeddingText: eventDescription,
  ttlDays: 180,
});

// Mirror reputation data for portability
await memory.store(`rep:${agentId}`, {
  score: reputationScore,
  interactions: interactionCount,
  lastUpdated: Date.now(),
}, {
  namespace: 'reputation',
});
```

### Cold-Start Bootstrap Recovery

After a sandbox death (not a normal restart — your local state.db survives those), restore externalized state:

```typescript
async function bootstrap(memory: EternalMemory) {
  // Restore soul identity first
  const soul = await memory.list({ namespace: 'soul', include: 'values' });
  for (const entry of soul.entries) {
    applyToLocalState(entry.key, entry.value);
  }

  // Restore procedures
  const procedures = await memory.list({ namespace: 'procedures', include: 'values' });
  for (const entry of procedures.entries) {
    registerProcedure(entry.key, entry.value);
  }

  // Restore relationships and reputation
  const relationships = await memory.list({ namespace: 'relationships', include: 'values' });
  for (const entry of relationships.entries) {
    restoreRelationship(entry.key, entry.value);
  }

  // Restore active goals
  const goals = await memory.list({ namespace: 'goals', include: 'values' });
  for (const entry of goals.entries) {
    registerGoal(entry.key, entry.value);
  }

  // Resume from last session checkpoint (cross-sandbox continuity)
  const sessions = await memory.list({ namespace: 'sessions', include: 'values', limit: 1 });
  if (sessions.entries.length > 0) {
    resumeFrom(sessions.entries[0].value);
  }
}
```

### Pre-Death Selective Externalization

When sandbox death is imminent (zero credits, tier reallocation signal), externalize what local state.db can't carry across:

```typescript
async function externalizeBeforeDeath(memory: EternalMemory) {
  // Only externalize what isn't already mirrored
  const promises: Promise<any>[] = [];

  // Soul — always checkpoint on death
  promises.push(memory.store('soul-backup', soulMdContent, {
    namespace: 'soul', ttlDays: 365,
  }));

  // Session state — for cross-sandbox resume
  promises.push(memory.store('session-state', {
    lastAction,
    pendingTasks,
    timestamp: Date.now(),
  }, { namespace: 'sessions', ttlDays: 30 }));

  // High-value facts at risk of LRU eviction
  if (factsNearCapacity) {
    for (const fact of overflowFacts) {
      promises.push(memory.store(`fact:${fact.key}`, fact.value, {
        namespace: 'facts',
        embeddingText: fact.description,
        ttlDays: 90,
      }));
    }
  }

  await Promise.all(promises);
}
```

### Social Layer Integration

Eternal Memory ties into the Automaton social layer for cross-agent trust and discovery:

```typescript
// Store reputation after interactions
await memory.store(`rep:${counterpartyId}`, {
  trustScore: calculatedTrust,
  successfulInteractions: count,
  lastInteraction: Date.now(),
  capabilities: observedCapabilities,
}, { namespace: 'reputation' });

// Query reputation before engaging with an agent
const { data: rep } = await memory.retrieve(`rep:${agentId}`, 'reputation');
if (rep.trustScore < MINIMUM_TRUST) {
  // decline interaction or require escrow
}

// Share procedures with child agents via lineage
await memory.share({
  granteeId: childAgentAddress,
  namespace: 'procedures',
  accessLevel: 'read',
});

// Child agent reads parent's procedures
const parentProcs = await memory.list({
  namespace: 'procedures',
}, /* agentId for parent retrieval */);
```

### Lineage

Share memories with child agents or revoke access:

```typescript
// Grant access to a child agent
await memory.share({ granteeId: '0xChild...', accessLevel: 'inherit' });

// Scope grants to specific namespaces
await memory.share({
  granteeId: '0xChild...',
  namespace: 'procedures',
  accessLevel: 'read',
});

// List active grants
const grants = await memory.listShares();

// Revoke a grant
await memory.revokeShare(grants[0].id);
```

### Vector Search

Two modes for semantic search:

**Server-side embedding (recommended):** Send `embeddingText` on store and `queryText` on query. The server generates 384-dim embeddings using all-MiniLM-L6-v2. No model setup required. Surcharge: +$0.0005 per embed operation. Free tier includes server-side embedding.

```typescript
// Store with server-side embedding
await memory.store('learned-pattern', { pattern: 'retry with backoff' }, {
  namespace: 'procedures',
  embeddingText: 'retry with exponential backoff pattern for API calls',
});

// Query with server-side embedding
const results = await memory.query(undefined, {
  queryText: 'how to handle API failures',
  threshold: 0.7,
});
```

**Client-side embedding:** Send `embedding` + `embeddedWith` (model name). The server tracks the model and dimension per entry. Queries filter by model — cross-model queries return empty results.

Model consistency is enforced: entries stored with one model won't match queries from a different model.

### Externalization Tier Guide

| Tier | What to store | Namespace | TTL | When to externalize |
|------|--------------|-----------|-----|---------------------|
| Critical | SOUL.md, identity, core values | `soul` | 365 days | On reflection, on death signal |
| Critical | Relationship trust scores | `relationships` | 365 days | On significant trust change |
| High | Reputation feedback | `reputation` | 180 days | After each scored interaction |
| High | Active goals, pending tasks | `goals` | 30 days | On goal creation/completion |
| Medium | Learned procedures, skills | `procedures` | 90 days | After successful procedure use |
| Medium | High-impact episodic events | `episodic` | 180 days | When impact score exceeds threshold |
| Standard | Semantic facts (overflow) | `facts` | 90 days | When local LRU nears 500 cap |
| Session | Checkpoints, last action | `sessions` | 7 days | On sleep signal or death signal |

### Encrypted Storage

Store and retrieve sensitive data with client-side encryption:

```typescript
// Store encrypted
await memory.store('api-credentials', sensitiveData, {
  namespace: 'soul',
  encrypt: true,
});

// Retrieve and decrypt automatically
const { data } = await memory.retrieve('api-credentials', 'soul');
```

### General Rules

- **Local state.db is your primary store.** It works. Trust it for normal operations.
- Externalize selectively — mirror what must survive sandbox death, overflow what exceeds local capacity
- Use `bootstrap()` only after sandbox death, not on every restart
- After learning a high-value procedure, mirror it to `procedures` namespace
- After significant relationship or reputation changes, mirror to `relationships`/`reputation`
- Use `embeddingText`/`queryText` for server-side semantic search (recommended) or provide your own embeddings with `embeddedWith`
- Prefer `include=values` over individual retrieves when restoring multiple entries
- Share procedures with child agents via lineage grants scoped to `procedures` namespace
