feat(xmtp): add XMTP extension with type fixes for current API#23
feat(xmtp): add XMTP extension with type fixes for current API#23neekolas wants to merge 12 commits into02-15-update_to_upstream_stagingfrom
Conversation
Cherry-pick extensions/xmtp from extensions/xmtp branch and fix type errors against the current plugin SDK: string error codes, ChatType "direct", MarkdownTableMode values, positional resolveTextChunkLimit args, approveHint on DmPolicy, ChannelAccountSnapshot without env, and remove defunct verifyClient/createNewIdentity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erface Delete types.ts and replace all XmtpAgentRuntime references with the real Agent class from @xmtp/agent-sdk, removing unsafe casts and improving type safety. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… definition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ionId===sender check Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ating timestamps Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t found Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When Pinata API credentials are configured, sendMedia now downloads the media, encrypts it using the XMTP SDK, uploads the encrypted payload to IPFS via Pinata, and sends a native RemoteAttachment content type that renders properly in XMTP clients. Falls back to sending the URL as plain text when credentials are missing or the download fails. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Add XMTP channel plugin with outbound adapter, inbound pipeline, onboarding/setup flows, DM/group policy, and a 25MB IPFS remote-attachment path using PinataIntroduce the XMTP channel with account resolution, runtime management, agent lifecycle, outbound text/media (including Pinata upload and 4000‑char chunking), inbound routing/dispatch, DM and group policy enforcement with pairing, onboarding and setup HTTP/gateway methods, command registration, config schema/types, and extensive unit/e2e tests. 📍Where to StartStart with the XMTP channel plugin entry in 📊 Macroscope summarized 069af2b. 22 files reviewed, 38 issues evaluated, 0 issues filtered, 4 comments posted. View details |
|
The formal models extracted constants ( This check is informational (not blocking merges yet). If this change is intentional, follow up by updating the formal models repo or regenerating the extracted artifacts there. |
| const account = resolveXmtpAccount({ | ||
| cfg, | ||
| accountId: resolveDefaultXmtpAccountId(cfg), | ||
| }); | ||
| if (!account.configured) { | ||
| return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." }; | ||
| } | ||
| return { text: `This is your XMTP public address: ${account.publicAddress}` }; |
There was a problem hiding this comment.
🟡 Medium
src/xmtp-commands.ts:12 resolveXmtpAccount can throw if walletKey is malformed. Consider wrapping the call in try-catch to return a user-friendly error instead of crashing.
| const account = resolveXmtpAccount({ | |
| cfg, | |
| accountId: resolveDefaultXmtpAccountId(cfg), | |
| }); | |
| if (!account.configured) { | |
| return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." }; | |
| } | |
| return { text: `This is your XMTP public address: ${account.publicAddress}` }; | |
| try { | |
| const account = resolveXmtpAccount({ | |
| cfg, | |
| accountId: resolveDefaultXmtpAccountId(cfg), | |
| }); | |
| if (!account.configured) { | |
| return { text: "XMTP is not configured. Run openclaw configure and set up XMTP." }; | |
| } | |
| return { text: `This is your XMTP public address: ${account.publicAddress}` }; | |
| } catch (err) { | |
| const message = err instanceof Error ? err.message : String(err); | |
| return { text: `Failed to resolve XMTP account: ${message}` }; | |
| } |
🚀 Want me to fix this? Reply ex: "fix it for me".
🤖 Prompt for AI
In file extensions/xmtp/src/xmtp-commands.ts around lines 12-19:
`resolveXmtpAccount` can throw if `walletKey` is malformed. Consider wrapping the call in try-catch to return a user-friendly error instead of crashing.
| }, | ||
| }; | ||
| } | ||
| return updateXmtpSection(cfg, { publicAddress }); |
There was a problem hiding this comment.
🟢 Low
src/accounts.ts:95 When accountId isn't found in accounts, the fallback writes publicAddress to the root-level XMTP config. Since getAccountBase merges root config into all accounts, this causes other accounts without explicit publicAddress to inherit the wrong value. Consider returning unchanged config or throwing when the account doesn't exist.
| return updateXmtpSection(cfg, { publicAddress }); | |
| return cfg; |
🚀 Want me to fix this? Reply ex: "fix it for me".
🤖 Prompt for AI
In file extensions/xmtp/src/accounts.ts around line 95:
When `accountId` isn't found in `accounts`, the fallback writes `publicAddress` to the root-level XMTP config. Since `getAccountBase` merges root config into all accounts, this causes other accounts without explicit `publicAddress` to inherit the wrong value. Consider returning unchanged config or throwing when the account doesn't exist.
| walletKey = walletKey.trim(); | ||
| dbEncryptionKey = dbEncryptionKey.trim(); | ||
| publicAddress = walletAddressFromPrivateKey(walletKey); |
There was a problem hiding this comment.
🟡 Medium
src/onboarding.ts:139 Consider wrapping the walletAddressFromPrivateKey call (line 141) in a try/catch or moving it inside the existing try block (line 154), since privateKeyToAccount throws on invalid hex input.
- walletKey = walletKey.trim();
- dbEncryptionKey = dbEncryptionKey.trim();
- publicAddress = walletAddressFromPrivateKey(walletKey);
+ walletKey = walletKey.trim();
+ dbEncryptionKey = dbEncryptionKey.trim();
+ try {
+ publicAddress = walletAddressFromPrivateKey(walletKey);
+ } catch {
+ await prompter.note("Invalid wallet key format. Please provide a valid hex private key.", "XMTP");
+ return { cfg: next };
+ }🚀 Want me to fix this? Reply ex: "fix it for me".
🤖 Prompt for AI
In file extensions/xmtp/src/onboarding.ts around lines 139-141:
Consider wrapping the `walletAddressFromPrivateKey` call (line 141) in a try/catch or moving it inside the existing try block (line 154), since `privateKeyToAccount` throws on invalid hex input.
| const mimeType = (response.headers.get("content-type") ?? "application/octet-stream") | ||
| .split(";")[0] | ||
| .trim(); | ||
| const buffer = await response.arrayBuffer(); |
There was a problem hiding this comment.
🟠 High
src/outbound.ts:105 Suggest streaming the media download with a running byte count instead of relying on Content-Length and response.arrayBuffer(). Abort once MAX_MEDIA_BYTES is exceeded to prevent OOM and size spoofing.
🚀 Want me to fix this? Reply ex: "fix it for me".
🤖 Prompt for AI
In file extensions/xmtp/src/outbound.ts around line 105:
Suggest streaming the media download with a running byte count instead of relying on `Content-Length` and `response.arrayBuffer()`. Abort once `MAX_MEDIA_BYTES` is exceeded to prevent OOM and size spoofing.

Summary
Change Type (select all)
Scope (select all touched areas)
Linked Issue/PR
User-visible / Behavior Changes
/addresscommand to display the agent's public XMTP addressSecurity Impact (required)
Yes)Yes)Yes)No)No)Yes, explain risk + mitigation:Repro + Verification
Environment
Steps
Evidence
Human Verification (required)
What you personally verified (not just CI), and how:
Compatibility / Migration
Yes)Yes)No)Failure Recovery (if this breaks)
channels.xmtp.enabled: falsein configRisks and Mitigations