@unicitylabs/openclaw-unicity 0.5.4 → 0.5.6-dev.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/openclaw-unicity",
3
- "version": "0.5.4",
3
+ "version": "0.5.6-dev.1",
4
4
  "description": "Unicity wallet identity and encrypted DMs for OpenClaw agents — powered by Sphere SDK",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@clack/prompts": "^0.10.0",
46
46
  "@sinclair/typebox": "^0.34.48",
47
- "@unicitylabs/sphere-sdk": "^0.5.4"
47
+ "@unicitylabs/sphere-sdk": "0.6.8-dev.2"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "openclaw": "*"
package/src/channel.ts CHANGED
@@ -244,16 +244,27 @@ export const unicityChannelPlugin = {
244
244
 
245
245
  // Track DM start time and seen IDs to skip historical replays from the relay.
246
246
  // The SDK fires onDirectMessage for every cached/replayed DM on connect.
247
- const dmStartTime = Math.floor(Date.now() / 1000);
247
+ // msg.timestamp is milliseconds, so dmStartTime must be ms too.
248
+ const dmStartTime = Date.now();
248
249
  const seenDmIds = new Set<string>();
249
250
  const DM_SEEN_MAX = 1000;
250
251
 
251
252
  // Collect all known representations of "self" so we can detect echoed-back
252
- // messages. The SDK's built-in self-check compares transport pubkey against
253
- // chainPubkey, but those may differ in format (33-byte compressed vs 32-byte
254
- // x-only Nostr key), letting self-messages slip through.
253
+ // messages. We must track BOTH the 33-byte compressed chainPubkey (02/03…)
254
+ // and the 32-byte x-only Nostr transport key (chainPubkey without prefix).
255
+ // The SDK's own self-check in CommunicationsModule has a bug where it
256
+ // compares transport pubkey (32-byte) against chainPubkey (33-byte), which
257
+ // never matches — so self-messages can leak through to dmHandlers.
255
258
  const selfPubkeys = new Set<string>();
256
- if (sphere.identity?.chainPubkey) selfPubkeys.add(sphere.identity.chainPubkey);
259
+ if (sphere.identity?.chainPubkey) {
260
+ const cpk = sphere.identity.chainPubkey;
261
+ selfPubkeys.add(cpk);
262
+ // x-only transport key = compressed pubkey without the 02/03 prefix
263
+ if (cpk.length === 66 && (cpk.startsWith("02") || cpk.startsWith("03"))) {
264
+ selfPubkeys.add(cpk.slice(2));
265
+ }
266
+ }
267
+ // Also add the groupChat key (same value but grabbed independently for safety)
257
268
  const myNostrPubkey = sphere.groupChat?.getMyPublicKey?.() ?? null;
258
269
  if (myNostrPubkey) selfPubkeys.add(myNostrPubkey);
259
270
 
package/src/sphere.ts CHANGED
@@ -116,6 +116,7 @@ async function doInitSphere(
116
116
  ...(existingMnemonic ? { mnemonic: existingMnemonic } : { autoGenerate: true }),
117
117
  ...(cfg.nametag ? { nametag: cfg.nametag } : {}),
118
118
  ...(groupChat ? { groupChat: groupChatRelays ? { relays: groupChatRelays } : true } : {}),
119
+ dmSince: Math.floor(Date.now() / 1000) - 86400,
119
120
  });
120
121
 
121
122
  sphereInstance = result.sphere;
@@ -133,8 +134,10 @@ async function doInitSphere(
133
134
  }
134
135
 
135
136
  // Register nametag if configured and wallet doesn't have one yet
136
- const walletNametag = result.sphere.identity?.nametag;
137
- if (cfg.nametag && !walletNametag) {
137
+ // Normalize: strip leading '@' for consistent comparison
138
+ const walletNametag = result.sphere.identity?.nametag?.replace(/^@/, "");
139
+ const cfgNametag = cfg.nametag?.replace(/^@/, "");
140
+ if (cfgNametag && !walletNametag) {
138
141
  try {
139
142
  await result.sphere.registerNametag(cfg.nametag);
140
143
  const log = logger ?? console;
@@ -148,11 +151,31 @@ async function doInitSphere(
148
151
  console.warn(msg);
149
152
  }
150
153
  }
151
- } else if (cfg.nametag && walletNametag && cfg.nametag !== walletNametag) {
154
+ } else if (cfgNametag && walletNametag && cfgNametag !== walletNametag) {
155
+ // Nametag changed — check if another address in this wallet already owns it,
156
+ // otherwise derive a new HD address and mint the nametag there.
152
157
  const log = logger ?? console;
153
- log.warn(
154
- `[unicity] Config nametag '${cfg.nametag}' differs from wallet nametag '${walletNametag}'. Wallet nametag is used. To change nametag, create a new wallet.`,
155
- );
158
+ try {
159
+ const activeAddresses = result.sphere.getActiveAddresses() as
160
+ { index: number; nametag?: string }[];
161
+ const existing = activeAddresses.find(
162
+ (a) => a.nametag?.replace(/^@/, "") === cfgNametag,
163
+ );
164
+ if (existing) {
165
+ log.info(`[unicity] Switching to existing address ${existing.index} for nametag '${cfg.nametag}'...`);
166
+ await result.sphere.switchToAddress(existing.index);
167
+ log.info(`[unicity] Switched to address ${existing.index} with nametag '${cfg.nametag}'.`);
168
+ } else {
169
+ const nextIndex = activeAddresses.length > 0
170
+ ? Math.max(...activeAddresses.map((a) => a.index)) + 1
171
+ : 1;
172
+ log.info(`[unicity] Minting nametag '${cfg.nametag}' on new address ${nextIndex}...`);
173
+ await result.sphere.switchToAddress(nextIndex, { nametag: cfg.nametag });
174
+ log.info(`[unicity] Switched to address ${nextIndex} with nametag '${cfg.nametag}'.`);
175
+ }
176
+ } catch (err) {
177
+ log.warn(`[unicity] Failed to switch address for nametag '${cfg.nametag}': ${err}`);
178
+ }
156
179
  }
157
180
 
158
181
  // Send greeting DM to owner on first wallet creation