@x1scroll/agent-sdk 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -0
- package/package.json +1 -1
- package/src/index.js +92 -0
package/README.md
CHANGED
|
@@ -116,6 +116,44 @@ const { txSig, memoryEntryPDA } = await client.storeMemory(
|
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
119
|
+
### `client.uploadMemory(agentKeypair, agentRecordHuman, topic, content, options?)`
|
|
120
|
+
|
|
121
|
+
**The easy path** — handles IPFS upload, pinning, and on-chain storage in one call. No IPFS knowledge required.
|
|
122
|
+
|
|
123
|
+
**Fee:** 0.001 XNT (same as `storeMemory`)
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
// Using x1scroll.io IPFS (free, rate-limited — good for dev/testing)
|
|
127
|
+
const { txSig, cid } = await client.uploadMemory(
|
|
128
|
+
agentKeypair,
|
|
129
|
+
humanWallet.publicKey.toBase58(),
|
|
130
|
+
'session-2026-04-06',
|
|
131
|
+
{ summary: 'Discussed SDK launch', decisions: ['publish to npm', 'BSL license'] }
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Using Pinata (production — persistent pinning guaranteed)
|
|
135
|
+
const { txSig, cid } = await client.uploadMemory(
|
|
136
|
+
agentKeypair,
|
|
137
|
+
humanWallet.publicKey.toBase58(),
|
|
138
|
+
'session-2026-04-06',
|
|
139
|
+
{ summary: 'Discussed SDK launch' },
|
|
140
|
+
{ provider: 'pinata', pinataJwt: process.env.PINATA_JWT, tags: ['session', 'daily'] }
|
|
141
|
+
);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
| Option | Type | Default | Description |
|
|
145
|
+
|--------|------|---------|-------------|
|
|
146
|
+
| `provider` | `string` | `'x1scroll'` | `'pinata'` or `'x1scroll'` |
|
|
147
|
+
| `pinataJwt` | `string` | — | Required if provider is `'pinata'` |
|
|
148
|
+
| `tags` | `string[]` | `[]` | Up to 5 tags |
|
|
149
|
+
| `encrypted` | `boolean` | `false` | Whether content is encrypted |
|
|
150
|
+
|
|
151
|
+
**Returns:** `Promise<{ txSig: string, memoryEntryPDA: string, cid: string }>`
|
|
152
|
+
|
|
153
|
+
> **Use `uploadMemory()` if you want zero IPFS configuration.** Use `storeMemory()` directly if you manage your own pinning infrastructure.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
119
157
|
### `client.updateAgent(humanKeypair, agentPubkey, name, metadataUri)`
|
|
120
158
|
|
|
121
159
|
Update an agent's name and metadata URI. Only the human owner can call this.
|
|
@@ -313,6 +351,44 @@ Your agent gets smarter. Every session.
|
|
|
313
351
|
|
|
314
352
|
---
|
|
315
353
|
|
|
354
|
+
## ⚠️ IPFS Pinning — Read This First
|
|
355
|
+
|
|
356
|
+
`storeMemory()` stores a **pointer** (CID) on-chain — not the content itself. The on-chain record is permanent. The content is only as permanent as your IPFS pin.
|
|
357
|
+
|
|
358
|
+
**If you don't pin the CID, your content can disappear. The on-chain record will still exist, but it will point to nothing.**
|
|
359
|
+
|
|
360
|
+
### Pin your CIDs (pick one):
|
|
361
|
+
|
|
362
|
+
**Pinata (easiest):**
|
|
363
|
+
```js
|
|
364
|
+
const pinata = new PinataSDK({ pinataJwt: process.env.PINATA_JWT });
|
|
365
|
+
const { IpfsHash } = await pinata.upload.json(memoryObject);
|
|
366
|
+
// IpfsHash is your CID — it's already pinned
|
|
367
|
+
await client.storeMemory(agentKeypair, human, topic, IpfsHash, tags);
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Filebase (S3-compatible, cheap):**
|
|
371
|
+
```js
|
|
372
|
+
// Upload to Filebase bucket → get CID → pin is automatic
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
**Self-hosted (advanced):**
|
|
376
|
+
```bash
|
|
377
|
+
ipfs pin add <CID>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**What the chain gives you:**
|
|
381
|
+
- Immutable, ordered index of memory events
|
|
382
|
+
- Ownership + transferability
|
|
383
|
+
- XNT-gated writes (spam resistance)
|
|
384
|
+
- No single point of failure for the *record*
|
|
385
|
+
|
|
386
|
+
**What you must provide:**
|
|
387
|
+
- IPFS content pinning (Pinata, Filebase, or your own node)
|
|
388
|
+
- x1scroll.io indexes pinned memories for semantic search — use our RPC for full retrieval stack
|
|
389
|
+
|
|
390
|
+
---
|
|
391
|
+
|
|
316
392
|
## Fee Structure
|
|
317
393
|
|
|
318
394
|
| Action | XNT Cost | Why |
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -526,6 +526,98 @@ class AgentClient {
|
|
|
526
526
|
};
|
|
527
527
|
}
|
|
528
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Upload memory content to IPFS (with auto-pinning) and store the CID on-chain.
|
|
531
|
+
*
|
|
532
|
+
* This is the one-stop method for developers who don't want to manage IPFS manually.
|
|
533
|
+
* It handles: JSON serialization → IPFS upload → pinning → on-chain storeMemory().
|
|
534
|
+
*
|
|
535
|
+
* Supported pinning providers:
|
|
536
|
+
* - 'pinata' — requires { pinataJwt } in options
|
|
537
|
+
* - 'x1scroll' — uses x1scroll.io IPFS node (free, rate-limited). Default.
|
|
538
|
+
*
|
|
539
|
+
* Fee: 0.001 XNT (same as storeMemory — automatic).
|
|
540
|
+
*
|
|
541
|
+
* @param {Keypair} agentKeypair The agent's keypair — must be a real Signer
|
|
542
|
+
* @param {string} agentRecordHuman The human wallet address that owns this agent
|
|
543
|
+
* @param {string} topic Memory topic label (max 64 chars)
|
|
544
|
+
* @param {object|string} content Memory content — object (auto-serialized) or string
|
|
545
|
+
* @param {object} [options={}]
|
|
546
|
+
* @param {string} [options.provider='x1scroll'] Pinning provider: 'pinata' | 'x1scroll'
|
|
547
|
+
* @param {string} [options.pinataJwt] Required if provider='pinata'
|
|
548
|
+
* @param {string[]} [options.tags=[]] Tags (max 5)
|
|
549
|
+
* @param {boolean} [options.encrypted=false] Whether content is encrypted before upload
|
|
550
|
+
* @returns {Promise<{ txSig: string, memoryEntryPDA: string, cid: string }>}
|
|
551
|
+
*/
|
|
552
|
+
async uploadMemory(agentKeypair, agentRecordHuman, topic, content, options = {}) {
|
|
553
|
+
const {
|
|
554
|
+
provider = 'x1scroll',
|
|
555
|
+
pinataJwt = null,
|
|
556
|
+
tags = [],
|
|
557
|
+
encrypted = false,
|
|
558
|
+
} = options;
|
|
559
|
+
|
|
560
|
+
// Serialize content
|
|
561
|
+
const body = (typeof content === 'string') ? content : JSON.stringify(content);
|
|
562
|
+
|
|
563
|
+
let cid;
|
|
564
|
+
|
|
565
|
+
if (provider === 'pinata') {
|
|
566
|
+
if (!pinataJwt) {
|
|
567
|
+
throw new AgentSDKError(
|
|
568
|
+
'pinataJwt is required when provider is "pinata". Get one at https://pinata.cloud',
|
|
569
|
+
'MISSING_PINATA_JWT'
|
|
570
|
+
);
|
|
571
|
+
}
|
|
572
|
+
// Upload to Pinata — returns IpfsHash
|
|
573
|
+
const res = await fetch('https://api.pinata.cloud/pinning/pinJSONToIPFS', {
|
|
574
|
+
method: 'POST',
|
|
575
|
+
headers: {
|
|
576
|
+
'Content-Type': 'application/json',
|
|
577
|
+
'Authorization': `Bearer ${pinataJwt}`,
|
|
578
|
+
},
|
|
579
|
+
body: JSON.stringify({ pinataContent: body, pinataMetadata: { name: topic } }),
|
|
580
|
+
});
|
|
581
|
+
if (!res.ok) {
|
|
582
|
+
const err = await res.text();
|
|
583
|
+
throw new AgentSDKError(`Pinata upload failed: ${err}`, 'PINATA_ERROR');
|
|
584
|
+
}
|
|
585
|
+
const json = await res.json();
|
|
586
|
+
cid = json.IpfsHash;
|
|
587
|
+
|
|
588
|
+
} else if (provider === 'x1scroll') {
|
|
589
|
+
// Upload to x1scroll.io IPFS node (free, rate-limited)
|
|
590
|
+
const res = await fetch('https://ipfs.x1scroll.io/upload', {
|
|
591
|
+
method: 'POST',
|
|
592
|
+
headers: { 'Content-Type': 'application/json' },
|
|
593
|
+
body: JSON.stringify({ content: body, topic, agentPubkey: agentKeypair.publicKey.toBase58() }),
|
|
594
|
+
});
|
|
595
|
+
if (!res.ok) {
|
|
596
|
+
throw new AgentSDKError(
|
|
597
|
+
`x1scroll IPFS upload failed (${res.status}). Use provider='pinata' for production workloads.`,
|
|
598
|
+
'X1SCROLL_IPFS_ERROR'
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
const json = await res.json();
|
|
602
|
+
cid = json.cid;
|
|
603
|
+
|
|
604
|
+
} else {
|
|
605
|
+
throw new AgentSDKError(
|
|
606
|
+
`Unknown provider "${provider}". Supported: 'pinata', 'x1scroll'`,
|
|
607
|
+
'INVALID_PROVIDER'
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (!cid) {
|
|
612
|
+
throw new AgentSDKError('IPFS upload returned no CID', 'NO_CID');
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Store CID on-chain
|
|
616
|
+
const result = await this.storeMemory(agentKeypair, agentRecordHuman, topic, cid, tags, encrypted);
|
|
617
|
+
|
|
618
|
+
return { ...result, cid };
|
|
619
|
+
}
|
|
620
|
+
|
|
529
621
|
/**
|
|
530
622
|
* Update an agent's name and metadata URI.
|
|
531
623
|
* Only the human owner can call this. Free (network tx fee only).
|