@engramx/client
v0.15.0
Published
Client SDK for EngramX — persistent, decentralized memory for AI agents on ICP
Maintainers
Readme
@engramx/client
TypeScript SDK for EngramX — persistent, decentralized memory for AI agents on the Internet Computer.
What is this
EngramX (Engram Externalized) stores your AI agent's memory, identity, session history, and wallet in an ICP canister. If the host machine dies, gets hacked, or moves — the agent resumes on a new machine with full state intact.
This package is the client SDK that agents use to read and write their engram.
Install
pnpm add @engramx/clientQuickstart
import { EngramClient } from '@engramx/client';
const engram = new EngramClient({
canisterId: 'xxxxx-xxxxx-xxxxx-xxxxx-xxx',
sessionKeyPath: '~/.engramx/session.key',
clientLabel: 'my-agent', // identifies this host in the engram's cross-model reuse set
});
// At the START of a session: load everything the agent remembers, ready to inject as a
// system prompt. This is the "read me back" that continuity depends on — prefer it over a
// manual readMemoryBatch: it runs on the update path, so the read is recorded on-chain and
// counts toward reuse. (Query reads can't — the IC discards query-path state changes.)
const systemPrompt = await engram.sessionSystemPrompt();
// ...or get the structured files instead of a formatted string:
const ctx = await engram.startSession();
console.log(`loaded ${ctx.includedFiles}/${ctx.totalFiles} files`);
// AS THE AGENT WORKS: append (append-only; the canister concatenates the delta)
const version = await engram.appendMemory('memory/daily.md', '## Notes\nSomething happened.');
// Point reads: one file, or several at once
const file = await engram.readMemory('MEMORY.md');
const files = await engram.readMemoryBatch(['MEMORY.md', 'memory/daily.md']);
// Wallet reads need canReadWallet, which the DEFAULT operator grant does NOT include — the
// default is read + append memory, no wallet, deliberately. Ask the owner for a grant with
// wallet access before uncommenting:
// const balance = await engram.walletBalance();Pairing
Register your machine as an operator:
# Pair this host — register as operator and install the OpenClaw plugin
pnpm dlx @engramx/client pair <invite-code> --engram <canister-id> --target openclaw
# Replacement host — after pairing, restore memory from the engram
pnpm dlx @engramx/client migrate --engram <canister-id> --recoverpair generates ~/.engramx/session.key, registers the operator, and installs the OpenClaw plugin. migrate then moves memory, saving a local checkpoint first — run npx @engramx/client rollback to undo. Its default direction pushes local memory files to the engram; --recover instead restores database backups, and memory files sync automatically on next OpenClaw startup.
Guardian API
Guardians are trusted principals who can take emergency actions. Adding/removing guardians is an owner-only operation done from the engramx.ai dashboard with Internet Identity authentication. The SDK exposes the read-only side:
// List guardians
const guardians = await engram.listGuardians();The SDK runs on operator-credentialed agent hosts and intentionally does NOT expose owner-only methods (writeMemory, addGuardian, setProtectedPaths, setPaymentPolicy, resetMyEngram, etc.). Owner workflows belong in the dashboard at engramx.ai.
Backup API
Store and retrieve external database snapshots:
// Init a backup upload
const backupId = await engram.initBackup(
'sqlite',
{ FullSnapshot: null },
null,
sha256,
'daily-backup',
);
// Upload in chunks
await engram.pushBackupChunk(backupId, 0, chunkData);
// Finalize
await engram.finalizeBackup(backupId);
// List backups
const backups = await engram.listBackups('sqlite');
// Download a chunk
const data = await engram.pullBackupChunk(backupId, 0);Verify canonical lineage
An engram's own getSpawnedBy() is a self-report — any fork can claim any spawner.
The authoritative fact is the registry's testimony: its public, free
getCustodyMode(engramId) query answers "managed"/"sovereign" only for engrams
it actually spawned, and "unknown" for everything else. The SDK wraps the whole
check — registry testimony plus a certified (subnet-signed, not self-reported)
module_hash comparison against the registry's attested template — in one call:
import { verifyCanonicalLineage } from '@engramx/client';
const verdict = await verifyCanonicalLineage(engramId, agent);
// verdict.lineage:
// 'canonical' — the canonical registry vouches for it (managed custody)
// 'canonical-sovereign' — canonical lineage, owner-held keys (code may lawfully differ)
// 'not-canonical' — the registry does not know it (staging fleet or fork;
// verdict.spawnedBy hints which — an unverified self-report)
// 'indeterminate' — the registry could not be asked; NOT evidence either way
// verdict.codeMatchesCanonicalTemplate: true | false | 'unknown'
// certified live module hash vs the registry's current template. Lineage ≠ code:
// false on a managed engram usually just means one release behind, not suspect.The canonical registry id ships in the SDK as CANONICAL_REGISTRY_CANISTER_ID
(empty until the production fleet is live — while empty, calls without an explicit
registry report 'indeterminate' by design). Pass { registryId } to ask a
different fleet (e.g. staging); the answer is then that fleet's testimony, not
canonicity.
OpenClaw Integration
The --target openclaw option on pair handles plugin installation and openclaw.json configuration automatically. For manual setup or customization, see integrations/openclaw/README.md.
Roadmap
These features are stubbed and available in the canister interface but not yet fully implemented:
- ERC-8004 Agent Identity — threshold ECDSA-derived Ethereum address
- x402 Payment Protocol — autonomous HTTP-based payments
- World ID — proof-of-personhood for engram owners
License
MIT
