@nekzus/liop 1.2.0-alpha.9 → 1.2.0

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.
Files changed (50) hide show
  1. package/README.md +12 -3
  2. package/dist/bin/agent.js +222 -51
  3. package/dist/bridge/index.js +7 -6
  4. package/dist/bridge/stream.js +11 -11
  5. package/dist/client/index.js +46 -35
  6. package/dist/crypto/logic-image-id.d.ts +3 -0
  7. package/dist/crypto/logic-image-id.js +27 -0
  8. package/dist/crypto/verifier.js +7 -19
  9. package/dist/economy/estimator.d.ts +53 -0
  10. package/dist/economy/estimator.js +69 -0
  11. package/dist/economy/index.d.ts +5 -0
  12. package/dist/economy/index.js +3 -0
  13. package/dist/economy/otel.d.ts +38 -0
  14. package/dist/economy/otel.js +100 -0
  15. package/dist/economy/telemetry.d.ts +77 -0
  16. package/dist/economy/telemetry.js +224 -0
  17. package/dist/errors.d.ts +14 -0
  18. package/dist/errors.js +19 -0
  19. package/dist/gateway/hybrid.d.ts +3 -1
  20. package/dist/gateway/hybrid.js +38 -13
  21. package/dist/gateway/router.d.ts +25 -9
  22. package/dist/gateway/router.js +484 -133
  23. package/dist/index.d.ts +3 -0
  24. package/dist/index.js +3 -0
  25. package/dist/mesh/node.d.ts +16 -0
  26. package/dist/mesh/node.js +394 -113
  27. package/dist/prompts/adapters.d.ts +16 -0
  28. package/dist/prompts/adapters.js +55 -0
  29. package/dist/rpc/proto.js +2 -1
  30. package/dist/rpc/server.d.ts +1 -1
  31. package/dist/rpc/server.js +4 -3
  32. package/dist/rpc/tls.js +3 -2
  33. package/dist/sandbox/wasi.d.ts +1 -1
  34. package/dist/sandbox/wasi.js +43 -3
  35. package/dist/security/guardian.js +3 -2
  36. package/dist/security/zk.d.ts +2 -3
  37. package/dist/security/zk.js +22 -9
  38. package/dist/server/index.d.ts +53 -4
  39. package/dist/server/index.js +362 -49
  40. package/dist/server/pii.d.ts +12 -0
  41. package/dist/server/pii.js +90 -0
  42. package/dist/types.d.ts +16 -0
  43. package/dist/utils/logger.d.ts +21 -0
  44. package/dist/utils/logger.js +70 -0
  45. package/dist/utils/mcpCompact.d.ts +11 -0
  46. package/dist/utils/mcpCompact.js +29 -0
  47. package/dist/workers/logic-execution.d.ts +1 -1
  48. package/dist/workers/logic-execution.js +38 -20
  49. package/dist/workers/zk-verifier.js +37 -33
  50. package/package.json +14 -2
@@ -26,6 +26,35 @@ function isLuhnValid(cardNumber) {
26
26
  }
27
27
  return sum % 10 === 0;
28
28
  }
29
+ /**
30
+ * Validates an International Bank Account Number (IBAN) using ISO 7064 Modulo 97.
31
+ * Uses BigInt algebra to avoid JS floating point truncation with 30-digit numbers.
32
+ */
33
+ function isIbanValid(iban) {
34
+ const sanitized = iban.replace(/\s+/g, "").toUpperCase();
35
+ if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/.test(sanitized))
36
+ return false;
37
+ const rearranged = sanitized.substring(4) + sanitized.substring(0, 4);
38
+ let numericString = "";
39
+ for (let i = 0; i < rearranged.length; i++) {
40
+ const charCode = rearranged.charCodeAt(i);
41
+ if (charCode >= 65 && charCode <= 90) {
42
+ numericString += (charCode - 55).toString();
43
+ }
44
+ else if (charCode >= 48 && charCode <= 57) {
45
+ numericString += rearranged.charAt(i);
46
+ }
47
+ else {
48
+ return false;
49
+ }
50
+ }
51
+ try {
52
+ return BigInt(numericString) % 97n === 1n;
53
+ }
54
+ catch (_e) {
55
+ return false;
56
+ }
57
+ }
29
58
  export const PII_PATTERNS = {
30
59
  EMAIL: {
31
60
  name: "EMAIL",
@@ -65,6 +94,67 @@ export const PII_PATTERNS = {
65
94
  return true;
66
95
  },
67
96
  },
97
+ SSN: {
98
+ name: "SSN",
99
+ pattern: /\b\d{3}[- ]?\d{2}[- ]?\d{4}\b/g,
100
+ validator: (match) => {
101
+ const digits = match.replace(/\D/g, "");
102
+ if (digits.length !== 9)
103
+ return false;
104
+ const area = parseInt(digits.substring(0, 3), 10);
105
+ if (area === 0 || area === 666 || area >= 900)
106
+ return false;
107
+ const group = parseInt(digits.substring(3, 5), 10);
108
+ if (group === 0)
109
+ return false;
110
+ const serial = parseInt(digits.substring(5, 9), 10);
111
+ if (serial === 0)
112
+ return false;
113
+ if (/^(\d)\1+$/.test(digits) || digits === "123456789")
114
+ return false;
115
+ return true;
116
+ },
117
+ },
118
+ IBAN: {
119
+ name: "IBAN",
120
+ pattern: /\b[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}\b/gi,
121
+ validator: isIbanValid,
122
+ },
123
+ PASSPORT_MRZ: {
124
+ name: "PASSPORT_MRZ",
125
+ // Machina Readable Zone line match for standard international passports
126
+ pattern: /\bP[A-Z<][A-Z<]{3}[A-Z0-9<]{39}(?:\b|\s|$)/g,
127
+ },
128
+ };
129
+ /**
130
+ * Regional and Cultural Security Presets for Out-Of-The-Box compliance.
131
+ * Developers can override, merge, or omit these based on local laws.
132
+ */
133
+ export const PII_PRESETS = {
134
+ GLOBAL_STRICT: [
135
+ PII_PATTERNS.EMAIL,
136
+ PII_PATTERNS.CREDIT_CARD,
137
+ PII_PATTERNS.IP_ADDRESS,
138
+ PII_PATTERNS.PHONE,
139
+ PII_PATTERNS.PASSPORT_MRZ,
140
+ PII_PATTERNS.IBAN,
141
+ ],
142
+ US_COMPLIANT: [
143
+ PII_PATTERNS.EMAIL,
144
+ PII_PATTERNS.CREDIT_CARD,
145
+ PII_PATTERNS.IP_ADDRESS,
146
+ PII_PATTERNS.PHONE,
147
+ PII_PATTERNS.SSN,
148
+ PII_PATTERNS.PASSPORT_MRZ,
149
+ ],
150
+ EU_GDPR: [
151
+ PII_PATTERNS.EMAIL,
152
+ PII_PATTERNS.CREDIT_CARD,
153
+ PII_PATTERNS.IP_ADDRESS,
154
+ PII_PATTERNS.PHONE,
155
+ PII_PATTERNS.IBAN,
156
+ PII_PATTERNS.PASSPORT_MRZ,
157
+ ],
68
158
  };
69
159
  export class PiiScanner {
70
160
  patterns;
package/dist/types.d.ts CHANGED
@@ -127,3 +127,19 @@ export interface ServerInfo {
127
127
  logging?: Record<string, unknown>;
128
128
  };
129
129
  }
130
+ export interface McpRequest {
131
+ method: string;
132
+ params?: unknown;
133
+ id?: string | number | null;
134
+ jsonrpc?: "2.0";
135
+ }
136
+ export interface McpResponse {
137
+ jsonrpc: "2.0";
138
+ id?: string | number | null;
139
+ result?: unknown;
140
+ error?: {
141
+ code: number;
142
+ message: string;
143
+ data?: unknown;
144
+ };
145
+ }
@@ -0,0 +1,21 @@
1
+ export type LogLevel = "silent" | "error" | "warn" | "info" | "debug";
2
+ /**
3
+ * LiopLogger - Structured Logging Abstraction
4
+ * Configurable via `process.env.LIOP_LOG_LEVEL`.
5
+ * Emits strictly to stderr to comply with MCP stdio protocols.
6
+ */
7
+ export declare class LiopLogger {
8
+ private static instance;
9
+ private level;
10
+ private constructor();
11
+ static getInstance(): LiopLogger;
12
+ private setLevelFromEnv;
13
+ setLevel(level: LogLevel): void;
14
+ private shouldLog;
15
+ private formatMessage;
16
+ error(message: string, ...args: unknown[]): void;
17
+ warn(message: string, ...args: unknown[]): void;
18
+ info(message: string, ...args: unknown[]): void;
19
+ debug(message: string, ...args: unknown[]): void;
20
+ }
21
+ export declare const log: LiopLogger;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * LiopLogger - Structured Logging Abstraction
3
+ * Configurable via `process.env.LIOP_LOG_LEVEL`.
4
+ * Emits strictly to stderr to comply with MCP stdio protocols.
5
+ */
6
+ export class LiopLogger {
7
+ static instance;
8
+ level = "info";
9
+ constructor() {
10
+ this.setLevelFromEnv();
11
+ }
12
+ static getInstance() {
13
+ if (!LiopLogger.instance) {
14
+ LiopLogger.instance = new LiopLogger();
15
+ }
16
+ return LiopLogger.instance;
17
+ }
18
+ setLevelFromEnv() {
19
+ const envLevel = process.env.LIOP_LOG_LEVEL?.toLowerCase();
20
+ if (envLevel === "silent" ||
21
+ envLevel === "error" ||
22
+ envLevel === "warn" ||
23
+ envLevel === "info" ||
24
+ envLevel === "debug") {
25
+ this.level = envLevel;
26
+ }
27
+ else {
28
+ // Default level: info
29
+ this.level = "info";
30
+ }
31
+ }
32
+ setLevel(level) {
33
+ this.level = level;
34
+ }
35
+ shouldLog(targetLevel) {
36
+ const levels = {
37
+ silent: 0,
38
+ error: 1,
39
+ warn: 2,
40
+ info: 3,
41
+ debug: 4,
42
+ };
43
+ return levels[this.level] >= levels[targetLevel];
44
+ }
45
+ formatMessage(level, message) {
46
+ const ts = new Date().toISOString();
47
+ return `[${ts}] [${level}] ${message}`;
48
+ }
49
+ error(message, ...args) {
50
+ if (this.shouldLog("error")) {
51
+ console.error(this.formatMessage("ERROR", message), ...args);
52
+ }
53
+ }
54
+ warn(message, ...args) {
55
+ if (this.shouldLog("warn")) {
56
+ console.error(this.formatMessage("WARN", message), ...args);
57
+ }
58
+ }
59
+ info(message, ...args) {
60
+ if (this.shouldLog("info")) {
61
+ console.error(this.formatMessage("INFO", message), ...args);
62
+ }
63
+ }
64
+ debug(message, ...args) {
65
+ if (this.shouldLog("debug")) {
66
+ console.error(this.formatMessage("DEBUG", message), ...args);
67
+ }
68
+ }
69
+ }
70
+ export const log = LiopLogger.getInstance();
@@ -0,0 +1,11 @@
1
+ /**
2
+ * MCP UX: optional compact tool descriptions for clients (e.g. cloud models)
3
+ * that over-trigger on long "envelope / injection" wording in tools/list.
4
+ *
5
+ * Full LIOP payload format remains in prompts/get → liop_blind_analyst.
6
+ */
7
+ export declare function mcpCompactToolDescriptions(): boolean;
8
+ /**
9
+ * Removes SDK-appended LIOP specification blocks from a registered tool description.
10
+ */
11
+ export declare function stripVerboseLiopToolDescription(description: string): string;
@@ -0,0 +1,29 @@
1
+ /**
2
+ * MCP UX: optional compact tool descriptions for clients (e.g. cloud models)
3
+ * that over-trigger on long "envelope / injection" wording in tools/list.
4
+ *
5
+ * Full LIOP payload format remains in prompts/get → liop_blind_analyst.
6
+ */
7
+ export function mcpCompactToolDescriptions() {
8
+ const v = process.env.LIOP_MCP_COMPACT_TOOL_DESCRIPTIONS?.toLowerCase().trim();
9
+ return v === "1" || v === "true" || v === "yes";
10
+ }
11
+ /**
12
+ * Removes SDK-appended LIOP specification blocks from a registered tool description.
13
+ */
14
+ export function stripVerboseLiopToolDescription(description) {
15
+ let d = description;
16
+ const markers = [
17
+ "\n\n[LIOP-PROTO-V1:",
18
+ "\r\n\r\n[LIOP-PROTO-V1:",
19
+ "\n[LIOP-PROTO-V1:", // rare
20
+ ];
21
+ for (const m of markers) {
22
+ const i = d.indexOf(m);
23
+ if (i !== -1) {
24
+ d = d.slice(0, i);
25
+ break;
26
+ }
27
+ }
28
+ return d.trimEnd();
29
+ }
@@ -11,7 +11,7 @@ export interface WorkerData {
11
11
  }
12
12
  export default function processLogicExecution(data: WorkerData): Promise<{
13
13
  image_id: string;
14
- output: string;
14
+ output: unknown;
15
15
  fuel_consumed: number;
16
16
  zk_receipt?: string;
17
17
  }>;
@@ -1,12 +1,14 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  import crypto from "node:crypto";
3
3
  import { createMlKem768 } from "mlkem";
4
+ import { deriveLogicImageDigest, normalizeLogicSource, } from "../crypto/logic-image-id.js";
4
5
  import { ASTGuardian } from "../sandbox/guardian.js";
5
6
  import { WasiSandbox } from "../sandbox/wasi.js";
6
7
  export default async function processLogicExecution(data) {
7
8
  const { ciphertext, secretKeyObj, wasmBinary, inputs, aesNonce, records, isEncrypted = true, } = data;
8
9
  let decryptedPayload;
9
10
  const decryptedInputs = {};
11
+ let sessionSecret = Buffer.alloc(32); // Fallback if plain text (no PQC)
10
12
  if (isEncrypted) {
11
13
  // 1. Decapsulate Kyber secret
12
14
  const sk = new Uint8Array(secretKeyObj);
@@ -14,6 +16,7 @@ export default async function processLogicExecution(data) {
14
16
  const kem = await createMlKem768();
15
17
  const sharedSecret = kem.decap(ct, sk);
16
18
  const aesKey = Buffer.from(sharedSecret);
19
+ sessionSecret = aesKey;
17
20
  // 2. Decrypt Main Payload (WASM/JS Code)
18
21
  // LIOP Serialization: Ciphertext = EncryptedData + 16-byte AuthTag
19
22
  const wasmBuffer = Buffer.from(wasmBinary);
@@ -63,33 +66,48 @@ export default async function processLogicExecution(data) {
63
66
  else if (decryptedPayload instanceof Buffer && !isWasm) {
64
67
  decryptedPayload = decryptedPayload.toString("utf-8");
65
68
  }
66
- // Sanitization: Remove LIOP Metadata, Manifests and Logic Block markers
69
+ // Strip only a whole-document LIOP envelope (see logic-image-id.ts).
67
70
  if (typeof decryptedPayload === "string") {
68
- decryptedPayload = decryptedPayload
69
- .replace(/^\s*LIOP_MAGIC:.*?\n/g, "")
70
- .replace(/^\s*MANIFEST:.*?\n/g, "")
71
- .replace(/\s*---BEGIN_LOGIC---\n?/g, "")
72
- .replace(/\n?---END_LOGIC---\s*$/g, "")
73
- .trim();
71
+ decryptedPayload = normalizeLogicSource(decryptedPayload);
74
72
  }
75
73
  // 4. Instantiate and Execute WASI Sandbox (or V8 Fallback)
76
74
  const sandbox = new WasiSandbox();
77
75
  await sandbox.init();
78
76
  try {
79
77
  const result = await sandbox.execute(decryptedPayload, records, decryptedInputs);
80
- // 5. Generate ZK Receipt Mock / Cryptographic Proof of Execution
81
- // Simulate the computational overhead of running the logic inside a zkVM Prover like RISC Zero (~50ms test)
82
- await new Promise((resolve) => setTimeout(resolve, 50));
83
- const logicBuffer = decryptedPayload instanceof Buffer
84
- ? decryptedPayload
85
- : Buffer.from(decryptedPayload);
86
- const hasher = crypto.createHash("sha256");
87
- hasher.update(logicBuffer);
88
- const imageId = hasher.digest("hex");
89
- // Phase 5: Structured ZK-Receipt (Journal + Seal)
90
- // Alpha payload with minimum entropy representing the cryptographic Seal
91
- const dummySeal = crypto.randomBytes(64).toString("hex");
92
- const zkReceipt = Buffer.from(`JOURNAL:${imageId}|SEAL:${dummySeal}`).toString("base64");
78
+ // 5. Generate Cryptographic Proof of Execution (HMAC-SHA256 Commitment)
79
+ let logicBytes;
80
+ if (typeof decryptedPayload === "string") {
81
+ logicBytes = Buffer.from(decryptedPayload, "utf-8");
82
+ }
83
+ else {
84
+ logicBytes = new Uint8Array(decryptedPayload);
85
+ }
86
+ const imageId = deriveLogicImageDigest(logicBytes).toString("hex");
87
+ const journal = Buffer.from(JSON.stringify({
88
+ image_id: imageId,
89
+ output_hash: crypto
90
+ .createHash("sha256")
91
+ .update(typeof result.output === "string"
92
+ ? result.output
93
+ : JSON.stringify(result.output))
94
+ .digest("hex"),
95
+ fuel: result.fuelConsumed,
96
+ ts: Date.now(),
97
+ }));
98
+ const seal = crypto
99
+ .createHmac("sha256", sessionSecret)
100
+ .update(journal)
101
+ .digest();
102
+ const journalLen = Buffer.alloc(2);
103
+ journalLen.writeUInt16BE(journal.length);
104
+ const receiptBuf = Buffer.concat([
105
+ Buffer.from([0x01]), // Receipt format v1
106
+ journalLen,
107
+ journal,
108
+ seal, // 32 bytes HMAC
109
+ ]);
110
+ const zkReceipt = receiptBuf.toString("base64");
93
111
  return {
94
112
  image_id: imageId,
95
113
  zk_receipt: zkReceipt,
@@ -1,35 +1,11 @@
1
- import * as crypto from "node:crypto";
2
1
  import { parentPort } from "node:worker_threads";
2
+ import { deriveLogicImageDigest } from "../crypto/logic-image-id.js";
3
3
  // Ensure this worker is used via Piscina pool
4
4
  if (!parentPort) {
5
5
  // Not fatal in Piscina, but handled appropriately
6
6
  }
7
- /**
8
- * Derives the ImageID of a logic payload following the LIOP v1 Standard.
9
- */
10
7
  function deriveImageId(logicPayload) {
11
- // Sanitization logic for JS payloads (Magic headers, etc.)
12
- let processed = Buffer.from(logicPayload);
13
- const isWasm = logicPayload[0] === 0x00 && logicPayload[1] === 0x61; // \0asm
14
- if (!isWasm) {
15
- const text = Buffer.from(logicPayload).toString("utf-8");
16
- const regex = /\s*LIOP_MAGIC:0x00FF\s*\n?\s*MANIFEST:(?<manifest>\{[\s\S]*?\})\s*\n?\s*---BEGIN_LOGIC---\n?(?<logic>[\s\S]*?)\n?---END_LOGIC---/m;
17
- const match = text.match(regex);
18
- if (match?.groups?.logic) {
19
- processed = Buffer.from(match.groups.logic.trim());
20
- }
21
- else {
22
- // Fallback string manipulation if no explicit full envelope
23
- const clean = text
24
- .replace(/^LIOP_MAGIC:.*?\n/g, "")
25
- .replace(/^MANIFEST:.*?\n/g, "")
26
- .replace(/---BEGIN_LOGIC---\n?/g, "")
27
- .replace(/\n?---END_LOGIC---/g, "")
28
- .trim();
29
- processed = Buffer.from(clean);
30
- }
31
- }
32
- return crypto.createHash("sha256").update(processed).digest();
8
+ return deriveLogicImageDigest(logicPayload);
33
9
  }
34
10
  /**
35
11
  * Simulates heavy ZK-Proof cryptographic verification.
@@ -46,19 +22,47 @@ async function verifyZkReceipt(payload) {
46
22
  message: `Integrity Violation: Local (${localImageIdHex.slice(0, 8)}) != Remote (${remoteImageIdHex.slice(0, 8)})`,
47
23
  };
48
24
  }
49
- // 2. Structural/Mathematical Seal Verification
50
- // Simulated cost of ZK Proof Polynomial Verification (~150-200ms depending on SNARK vs STARK)
51
- // In Production: await verifier.verify(zkReceipt, localImageIdHex);
52
- await new Promise((resolve) => setTimeout(resolve, 150));
53
- if (zkReceipt.length < 32) {
25
+ // 2. Structural Verification: Deserialize Binary Receipt
26
+ const receiptBuf = Buffer.from(zkReceipt);
27
+ if (receiptBuf.length < 35) {
28
+ // 1 version + 2 len + 32 seal minimum
29
+ return {
30
+ verified: false,
31
+ message: "Receipt too short for binary format.",
32
+ };
33
+ }
34
+ const version = receiptBuf[0];
35
+ if (version !== 0x01) {
54
36
  return {
55
37
  verified: false,
56
- message: "Invalid Receipt: Proof payload lacks minimum entropy.",
38
+ message: `Unknown receipt version: ${version}`,
57
39
  };
58
40
  }
41
+ const journalLen = receiptBuf.readUInt16BE(1);
42
+ const journal = receiptBuf.subarray(3, 3 + journalLen);
43
+ const seal = receiptBuf.subarray(3 + journalLen);
44
+ if (seal.length !== 32) {
45
+ return {
46
+ verified: false,
47
+ message: "Invalid seal length (expected 32 bytes HMAC-SHA256).",
48
+ };
49
+ }
50
+ // 3. Parse journal and verify imageId
51
+ try {
52
+ const journalData = JSON.parse(journal.toString());
53
+ if (journalData.image_id !== localImageIdHex) {
54
+ return {
55
+ verified: false,
56
+ message: `Journal ImageID mismatch: ${journalData.image_id.slice(0, 8)} != ${localImageIdHex.slice(0, 8)}`,
57
+ };
58
+ }
59
+ }
60
+ catch (_e) {
61
+ return { verified: false, message: "Failed to parse journal data." };
62
+ }
59
63
  return {
60
64
  verified: true,
61
- message: "ZK-SNARK Mathematical Audit: SUCCESS",
65
+ message: "HMAC Commitment Verified: Integrity intact.",
62
66
  };
63
67
  }
64
68
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nekzus/liop",
3
- "version": "1.2.0-alpha.9",
3
+ "version": "1.2.0",
4
4
  "description": "Official SDK for Logic-Injection-on-Origin Protocol (LIOP). Deploy Logic-on-Origin with WebAssembly at gRPC speed and bidirectional MCP compatibility.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -56,7 +56,16 @@
56
56
  "demo:client-quickstart": "pnpm --filter @liop/example-client-quickstart start",
57
57
  "demo:server-quickstart": "pnpm --filter @liop/example-server-quickstart start",
58
58
  "demo:server": "pnpm --filter @liop/example-server start",
59
- "demo:client": "pnpm --filter @liop/example-client start"
59
+ "demo:client": "pnpm --filter @liop/example-client start",
60
+ "test:crossnet": "tsx tests/infra/cli/crossnet.ts",
61
+ "test:crossnet:burn": "tsx tests/infra/cli/crossnet-burn.ts",
62
+ "demo:build": "tsx tests/infra/cli/demo-build.ts",
63
+ "demo:start": "tsx tests/infra/cli/demo-start.ts",
64
+ "demo:start:rebuild": "tsx tests/infra/cli/demo-start-rebuild.ts",
65
+ "demo:stop": "tsx tests/infra/cli/demo-stop.ts",
66
+ "demo:clean": "tsx tests/infra/cli/demo-clean.ts",
67
+ "demo:claude": "tsx tests/infra/cli/demo-claude.ts",
68
+ "demo:inspector": "tsx tests/infra/cli/demo-inspector.ts"
60
69
  },
61
70
  "keywords": [
62
71
  "liop",
@@ -95,6 +104,7 @@
95
104
  },
96
105
  "devDependencies": {
97
106
  "@biomejs/biome": "^2.4.4",
107
+ "@opentelemetry/sdk-metrics": "^2.7.0",
98
108
  "@types/node": "^25.3.1",
99
109
  "@vitest/coverage-v8": "^4.0.18",
100
110
  "tsx": "^4.21.0",
@@ -120,6 +130,8 @@
120
130
  "@libp2p/websockets": "^10.1.7",
121
131
  "@modelcontextprotocol/sdk": "^1.28.0",
122
132
  "@multiformats/multiaddr": "^13.0.1",
133
+ "@opentelemetry/api": "^1.9.1",
134
+ "gpt-tokenizer": "^3.4.0",
123
135
  "hono": "^4.12.5",
124
136
  "it-pipe": "^3.0.1",
125
137
  "libp2p": "^3.1.3",