@anuma/sdk 6.27.0 → 6.27.2

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.
@@ -273,11 +273,32 @@ function clearAllEncryptionState() {
273
273
  function clearAllEncryptionKeys() {
274
274
  clearAllEncryptionState();
275
275
  }
276
+ function hexNibble(code) {
277
+ if (code >= 48 && code <= 57) return code - 48;
278
+ if (code >= 65 && code <= 70) return code - 55;
279
+ if (code >= 97 && code <= 102) return code - 87;
280
+ return -1;
281
+ }
276
282
  function hexToBytes(hex) {
283
+ if (typeof hex !== "string") {
284
+ throw new TypeError(
285
+ "hexToBytes: expected a hex string. Pass raw signature bytes to deriveKeyFromSignatureBytes instead of stringifying them."
286
+ );
287
+ }
277
288
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
289
+ if (cleanHex.length === 0 || cleanHex.length % 2 !== 0) {
290
+ throw new Error(
291
+ `hexToBytes: invalid hex string (expected a non-empty even length, got ${cleanHex.length})`
292
+ );
293
+ }
278
294
  const bytes = new Uint8Array(cleanHex.length / 2);
279
295
  for (let i = 0; i < cleanHex.length; i += 2) {
280
- bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
296
+ const hi = hexNibble(cleanHex.charCodeAt(i));
297
+ const lo = hexNibble(cleanHex.charCodeAt(i + 1));
298
+ if (hi < 0 || lo < 0) {
299
+ throw new Error(`hexToBytes: invalid hex string (non-hex character at index ${i})`);
300
+ }
301
+ bytes[i / 2] = hi << 4 | lo;
281
302
  }
282
303
  return bytes;
283
304
  }
@@ -289,15 +310,12 @@ var SHARED_TEXT_DECODER = new TextDecoder();
289
310
  function isValidWalletAddress(address) {
290
311
  return /^0x[a-fA-F0-9]{40}$/.test(address);
291
312
  }
292
- async function deriveKeyFromSignature(signature) {
293
- const sigBytes = hexToBytes(signature);
294
- const hashBuffer = await crypto.subtle.digest("SHA-256", sigBytes.buffer);
295
- const hashBytes = new Uint8Array(hashBuffer);
296
- return bytesToHex(hashBytes);
297
- }
298
- async function deriveKeyFromSignatureV3(signature) {
299
- const sigBytes = hexToBytes(signature);
300
- const ikm = await crypto.subtle.digest("SHA-256", sigBytes.buffer);
313
+ var AES_GCM_V3_INFO = "anuma-sdk-aes-gcm-v3";
314
+ var AES_GCM_V4_INFO = "anuma-sdk-aes-gcm-v4";
315
+ var MIN_SIGNATURE_BYTES = 64;
316
+ async function deriveHkdfAesKeyHex(signatureBytes, info) {
317
+ const sigBytes = new Uint8Array(signatureBytes);
318
+ const ikm = await crypto.subtle.digest("SHA-256", sigBytes);
301
319
  const hkdfKey = await crypto.subtle.importKey("raw", ikm, { name: "HKDF" }, false, [
302
320
  "deriveBits"
303
321
  ]);
@@ -307,13 +325,32 @@ async function deriveKeyFromSignatureV3(signature) {
307
325
  hash: "SHA-256",
308
326
  salt: new Uint8Array(32),
309
327
  // Zero salt (HKDF spec: uses hash-length zero buffer)
310
- info: SHARED_TEXT_ENCODER.encode("anuma-sdk-aes-gcm-v3")
328
+ info: SHARED_TEXT_ENCODER.encode(info)
311
329
  },
312
330
  hkdfKey,
313
331
  256
314
332
  );
315
333
  return bytesToHex(new Uint8Array(derivedBits));
316
334
  }
335
+ async function deriveKeyFromSignature(signature) {
336
+ const sigBytes = hexToBytes(signature);
337
+ const hashBuffer = await crypto.subtle.digest("SHA-256", sigBytes.buffer);
338
+ return bytesToHex(new Uint8Array(hashBuffer));
339
+ }
340
+ async function deriveKeyFromSignatureV3(signature) {
341
+ return deriveHkdfAesKeyHex(hexToBytes(signature), AES_GCM_V3_INFO);
342
+ }
343
+ async function deriveKeyFromSignatureBytes(signature) {
344
+ if (!(signature instanceof Uint8Array)) {
345
+ throw new TypeError("deriveKeyFromSignatureBytes: expected a Uint8Array signature");
346
+ }
347
+ if (signature.byteLength < MIN_SIGNATURE_BYTES) {
348
+ throw new Error(
349
+ `deriveKeyFromSignatureBytes: signature must be at least ${MIN_SIGNATURE_BYTES} bytes, got ${signature.byteLength}`
350
+ );
351
+ }
352
+ return deriveHkdfAesKeyHex(signature, AES_GCM_V4_INFO);
353
+ }
317
354
  function getStoredKeyPair(address) {
318
355
  return keyPairStore.get(address) ?? null;
319
356
  }
@@ -331,8 +368,9 @@ async function deriveKeyPairFromSignature(signature, address) {
331
368
  {
332
369
  name: "HKDF",
333
370
  hash: "SHA-256",
371
+ // EVM addresses are case-insensitive. Base58 is not — this salt stays
372
+ // behind the EVM address gate in requestKeyPair.
334
373
  salt: SHARED_TEXT_ENCODER.encode(address.toLowerCase()),
335
- // Wallet address as salt
336
374
  info: SHARED_TEXT_ENCODER.encode("ECDH-P256-KeyPair")
337
375
  // Context info
338
376
  },
@@ -3937,6 +3975,34 @@ function createWebSearchPreProcessor(options = {}) {
3937
3975
  };
3938
3976
  }
3939
3977
 
3978
+ // src/lib/storage/mcpImages.ts
3979
+ var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
3980
+ "AnumaMediaMCP-anuma_create_image",
3981
+ "anuma_create_image",
3982
+ // Deprecated anuma-image-mcp tools, retained so images in already-stored
3983
+ // conversations still re-materialize on replay.
3984
+ "AnumaImageMCP-generate_cloud_image",
3985
+ "AnumaImageMCP-edit_cloud_image",
3986
+ "generate_cloud_image",
3987
+ "edit_cloud_image"
3988
+ ]);
3989
+ function toolOutputForModel(name, output) {
3990
+ if (!name || !IMAGE_TOOL_NAMES.has(name)) return output;
3991
+ try {
3992
+ const {
3993
+ imageUrl: _imageUrl,
3994
+ url: _url,
3995
+ output_images: _outputImages,
3996
+ ...rest
3997
+ } = JSON.parse(output);
3998
+ return JSON.stringify(rest);
3999
+ } catch {
4000
+ return output;
4001
+ }
4002
+ }
4003
+ var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
4004
+ var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
4005
+
3940
4006
  // src/lib/chat/runHooks.ts
3941
4007
  function composeHooks(hooks) {
3942
4008
  const handlers = hooks.filter((h) => h !== void 0 && h !== null);
@@ -6067,6 +6133,40 @@ function extractLastUserText(messages) {
6067
6133
  function isToolErrorResult(result) {
6068
6134
  return result !== null && typeof result === "object" && "error" in result && typeof result.error === "string";
6069
6135
  }
6136
+ function serverToolCallMessages(events, messages, skipIds) {
6137
+ const seen = new Set(skipIds);
6138
+ for (const m of messages) {
6139
+ if (m.tool_call_id) seen.add(m.tool_call_id);
6140
+ for (const tc of m.tool_calls ?? []) if (tc.id) seen.add(tc.id);
6141
+ }
6142
+ const executed = [];
6143
+ for (const event of events ?? []) {
6144
+ if (!event.id || event.output === void 0 || event.output === null || seen.has(event.id)) {
6145
+ continue;
6146
+ }
6147
+ seen.add(event.id);
6148
+ executed.push(event);
6149
+ }
6150
+ if (executed.length === 0) return [];
6151
+ return [
6152
+ {
6153
+ role: "assistant",
6154
+ content: void 0,
6155
+ tool_calls: executed.map((event) => ({
6156
+ id: event.id,
6157
+ type: "function",
6158
+ function: { name: event.name, arguments: event.arguments }
6159
+ }))
6160
+ },
6161
+ ...executed.map(
6162
+ (event) => ({
6163
+ role: "tool",
6164
+ tool_call_id: event.id,
6165
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output ?? "") }]
6166
+ })
6167
+ )
6168
+ ];
6169
+ }
6070
6170
  function getToolName(tool) {
6071
6171
  const func = tool.function;
6072
6172
  const nestedName = func?.name;
@@ -6836,7 +6936,14 @@ Executing tool: ${toolInfo}
6836
6936
  }
6837
6937
  }))
6838
6938
  };
6839
- const toolResultMessages = [assistantMessage];
6939
+ const toolResultMessages = [
6940
+ ...serverToolCallMessages(
6941
+ currentAccumulator.toolCallEvents,
6942
+ currentMessages,
6943
+ new Set(toolCallsToExecute.map((tc) => tc.id))
6944
+ ),
6945
+ assistantMessage
6946
+ ];
6840
6947
  for (const execResult of continueResults) {
6841
6948
  const resultContent = execResult.error ? `Error: ${execResult.error}` : safeJsonStringify(execResult.result);
6842
6949
  toolResultMessages.push({
@@ -9019,20 +9126,6 @@ function generateMediaId() {
9019
9126
  // src/lib/db/media/operations.ts
9020
9127
  import { Q as Q3 } from "@nozbe/watermelondb";
9021
9128
 
9022
- // src/lib/storage/mcpImages.ts
9023
- var IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
9024
- "AnumaMediaMCP-anuma_create_image",
9025
- "anuma_create_image",
9026
- // Deprecated anuma-image-mcp tools, retained so images in already-stored
9027
- // conversations still re-materialize on replay.
9028
- "AnumaImageMCP-generate_cloud_image",
9029
- "AnumaImageMCP-edit_cloud_image",
9030
- "generate_cloud_image",
9031
- "edit_cloud_image"
9032
- ]);
9033
- var VIDEO_EXTENSIONS = ["mp4", "webm", "mov"];
9034
- var VIDEO_EXTENSION_RE = new RegExp(`\\.(${VIDEO_EXTENSIONS.join("|")})(?:[?#]|$)`, "i");
9035
-
9036
9129
  // src/lib/storage/opfs.ts
9037
9130
  function isOPFSSupported() {
9038
9131
  return typeof navigator !== "undefined" && "storage" in navigator && "getDirectory" in navigator.storage;
@@ -19522,24 +19615,10 @@ function storedToLlmapiMessage(stored) {
19522
19615
  });
19523
19616
  for (const event of stored.toolCallEvents) {
19524
19617
  if (event.id && event.output !== void 0 && event.output !== null) {
19525
- let toolOutput = event.output;
19526
- if (event.name && IMAGE_TOOL_NAMES.has(event.name)) {
19527
- try {
19528
- const parsed = JSON.parse(toolOutput);
19529
- const {
19530
- imageUrl: _imageUrl,
19531
- url: _url,
19532
- output_images: _outputImages,
19533
- ...rest
19534
- } = parsed;
19535
- toolOutput = JSON.stringify(rest);
19536
- } catch {
19537
- }
19538
- }
19539
19618
  messages.push({
19540
19619
  role: "tool",
19541
19620
  tool_call_id: event.id,
19542
- content: [{ type: "text", text: toolOutput }]
19621
+ content: [{ type: "text", text: toolOutputForModel(event.name, event.output) }]
19543
19622
  });
19544
19623
  }
19545
19624
  }
@@ -23978,6 +24057,7 @@ export {
23978
24057
  deleteVaultFolderOp,
23979
24058
  deleteVaultMemoryOp,
23980
24059
  dequantizeEmbedding,
24060
+ deriveKeyFromSignatureBytes,
23981
24061
  discoverNotionOAuthEndpoints,
23982
24062
  eagerEmbedContent,
23983
24063
  embedAllMessages,
package/dist/index.cjs CHANGED
@@ -2916,11 +2916,32 @@ function getStoredKeyByVersion(address, version) {
2916
2916
  const value = version === "v2" ? keys.legacy : keys.current;
2917
2917
  return value ?? null;
2918
2918
  }
2919
+ function hexNibble(code) {
2920
+ if (code >= 48 && code <= 57) return code - 48;
2921
+ if (code >= 65 && code <= 70) return code - 55;
2922
+ if (code >= 97 && code <= 102) return code - 87;
2923
+ return -1;
2924
+ }
2919
2925
  function hexToBytes(hex) {
2926
+ if (typeof hex !== "string") {
2927
+ throw new TypeError(
2928
+ "hexToBytes: expected a hex string. Pass raw signature bytes to deriveKeyFromSignatureBytes instead of stringifying them."
2929
+ );
2930
+ }
2920
2931
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
2932
+ if (cleanHex.length === 0 || cleanHex.length % 2 !== 0) {
2933
+ throw new Error(
2934
+ `hexToBytes: invalid hex string (expected a non-empty even length, got ${cleanHex.length})`
2935
+ );
2936
+ }
2921
2937
  const bytes = new Uint8Array(cleanHex.length / 2);
2922
2938
  for (let i = 0; i < cleanHex.length; i += 2) {
2923
- bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
2939
+ const hi = hexNibble(cleanHex.charCodeAt(i));
2940
+ const lo = hexNibble(cleanHex.charCodeAt(i + 1));
2941
+ if (hi < 0 || lo < 0) {
2942
+ throw new Error(`hexToBytes: invalid hex string (non-hex character at index ${i})`);
2943
+ }
2944
+ bytes[i / 2] = hi << 4 | lo;
2924
2945
  }
2925
2946
  return bytes;
2926
2947
  }
package/dist/index.mjs CHANGED
@@ -2710,11 +2710,32 @@ function getStoredKeyByVersion(address, version) {
2710
2710
  const value = version === "v2" ? keys.legacy : keys.current;
2711
2711
  return value ?? null;
2712
2712
  }
2713
+ function hexNibble(code) {
2714
+ if (code >= 48 && code <= 57) return code - 48;
2715
+ if (code >= 65 && code <= 70) return code - 55;
2716
+ if (code >= 97 && code <= 102) return code - 87;
2717
+ return -1;
2718
+ }
2713
2719
  function hexToBytes(hex) {
2720
+ if (typeof hex !== "string") {
2721
+ throw new TypeError(
2722
+ "hexToBytes: expected a hex string. Pass raw signature bytes to deriveKeyFromSignatureBytes instead of stringifying them."
2723
+ );
2724
+ }
2714
2725
  const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
2726
+ if (cleanHex.length === 0 || cleanHex.length % 2 !== 0) {
2727
+ throw new Error(
2728
+ `hexToBytes: invalid hex string (expected a non-empty even length, got ${cleanHex.length})`
2729
+ );
2730
+ }
2715
2731
  const bytes = new Uint8Array(cleanHex.length / 2);
2716
2732
  for (let i = 0; i < cleanHex.length; i += 2) {
2717
- bytes[i / 2] = parseInt(cleanHex.slice(i, i + 2), 16);
2733
+ const hi = hexNibble(cleanHex.charCodeAt(i));
2734
+ const lo = hexNibble(cleanHex.charCodeAt(i + 1));
2735
+ if (hi < 0 || lo < 0) {
2736
+ throw new Error(`hexToBytes: invalid hex string (non-hex character at index ${i})`);
2737
+ }
2738
+ bytes[i / 2] = hi << 4 | lo;
2718
2739
  }
2719
2740
  return bytes;
2720
2741
  }
@@ -10,6 +10,11 @@ interface SignMessageOptions {
10
10
  /**
11
11
  * Type for the signMessage function that client must provide.
12
12
  * This is typically from Privy's useSignMessage hook.
13
+ *
14
+ * The resolved value must be a hex signature (`0x`-prefixed or bare). A
15
+ * non-hex string — including `String(uint8Array)` or a base58 signature —
16
+ * throws from key derivation instead of becoming a zero-filled AES key.
17
+ * Raw signature bytes go to {@link deriveKeyFromSignatureBytes}.
13
18
  */
14
19
  type SignMessageFn = (message: string, options?: SignMessageOptions) => Promise<string>;
15
20
  /**
@@ -10,6 +10,11 @@ interface SignMessageOptions {
10
10
  /**
11
11
  * Type for the signMessage function that client must provide.
12
12
  * This is typically from Privy's useSignMessage hook.
13
+ *
14
+ * The resolved value must be a hex signature (`0x`-prefixed or bare). A
15
+ * non-hex string — including `String(uint8Array)` or a base58 signature —
16
+ * throws from key derivation instead of becoming a zero-filled AES key.
17
+ * Raw signature bytes go to {@link deriveKeyFromSignatureBytes}.
13
18
  */
14
19
  type SignMessageFn = (message: string, options?: SignMessageOptions) => Promise<string>;
15
20
  /**