@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
@@ -1,3 +1,4 @@
1
+ import { Buffer } from "node:buffer";
1
2
  import crypto from "node:crypto";
2
3
  import { createRequire } from "node:module";
3
4
  import path from "node:path";
@@ -8,8 +9,17 @@ import { z } from "zod";
8
9
  import { zodToJsonSchema } from "zod-to-json-schema";
9
10
  import { MeshNode } from "../mesh/node.js";
10
11
  import { LiopRpcServer } from "../rpc/server.js";
11
- import { PII_PATTERNS, PiiScanner } from "./pii.js";
12
- export { PII_PATTERNS, PiiScanner };
12
+ import { log } from "../utils/logger.js";
13
+ import { PII_PATTERNS, PII_PRESETS, PiiScanner } from "./pii.js";
14
+ export { PII_PATTERNS, PII_PRESETS, PiiScanner };
15
+ /**
16
+ * When enabled, `payload` tools that are not LIOP v1 envelopes are passed through to the
17
+ * registered handler unchanged (no worker extraction). Default off for strict protocol tests.
18
+ */
19
+ function respectPlainToolPayload() {
20
+ const v = process.env.LIOP_RESPECT_PLAIN_TOOL_PAYLOAD?.toLowerCase().trim();
21
+ return v === "1" || v === "true" || v === "yes";
22
+ }
13
23
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
24
  export class LiopServer {
15
25
  serverInfo;
@@ -28,16 +38,179 @@ export class LiopServer {
28
38
  workerPool;
29
39
  meshNode = null;
30
40
  rpcServer = null;
41
+ boundPort = null;
31
42
  sessions = new Map();
32
- static LIOP_LOGIC_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;
43
+ // Compact envelope: @LIOP{target,name}\n<code>\n@END
44
+ static LIOP_COMPACT_REGEX = /@LIOP\{(?<target>[^,}]+)(?:,(?<name>[^}]*))?\}\n(?<logic>[\s\S]*?)\n@END/m;
33
45
  extractLogic(payload) {
34
- const match = payload.match(LiopServer.LIOP_LOGIC_REGEX);
35
- return match?.groups?.logic ? match.groups.logic.trim() : null;
46
+ const compact = payload.match(LiopServer.LIOP_COMPACT_REGEX);
47
+ return compact?.groups?.logic ? compact.groups.logic.trim() : null;
48
+ }
49
+ parseUnknownJson(input) {
50
+ if (typeof input !== "string")
51
+ return input;
52
+ const trimmed = input.trim();
53
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
54
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
55
+ try {
56
+ return JSON.parse(trimmed);
57
+ }
58
+ catch {
59
+ return input;
60
+ }
61
+ }
62
+ return input;
63
+ }
64
+ runPreflightPolicy(_toolName, logic, policy) {
65
+ if (!policy)
66
+ return null;
67
+ const compact = logic.replace(/\s+/g, " ");
68
+ if (policy.enforceAggregationFirst) {
69
+ const rowExtractionPatterns = [
70
+ /return\s+env\.records\b/i,
71
+ /return\s*\{[\s\S]*\b(accounts|patients|rows|records)\s*:\s*env\.records/i,
72
+ ];
73
+ if (rowExtractionPatterns.some((p) => p.test(compact))) {
74
+ return "Preflight policy rejected: potential row-level export pattern detected.";
75
+ }
76
+ }
77
+ if (policy.preflightDenyPatterns?.some((p) => p.test(compact))) {
78
+ return "Preflight policy rejected: custom deny pattern matched.";
79
+ }
80
+ return null;
81
+ }
82
+ validateOutputPolicy(toolName, output, policy) {
83
+ if (!policy)
84
+ return null;
85
+ const parsed = this.parseUnknownJson(output);
86
+ if (policy.outputSchema) {
87
+ const schemaResult = policy.outputSchema.safeParse(parsed);
88
+ if (!schemaResult.success) {
89
+ // Include a truncated preview of the rejected value so the LLM can self-correct
90
+ const preview = typeof parsed === "string"
91
+ ? parsed.slice(0, 200)
92
+ : JSON.stringify(parsed).slice(0, 200);
93
+ return `[LIOP] Output schema violation for ${toolName}: ${schemaResult.error.issues
94
+ .map((i) => `${i.path.join(".") || "<root>"} ${i.message}`)
95
+ .join("; ")}. Rejected value: ${preview}. HINT: Use 'env.records' to access the dataset inside your logic.`;
96
+ }
97
+ }
98
+ if (policy.enforceAggregationFirst &&
99
+ this.violatesAggregationFirstPolicy(this.unwrapForAggregationPolicyScan(parsed), policy.enforceAggregationFirst)) {
100
+ return "Aggregation-First Policy Violation: row-level export blocked. HINT: Use .reduce() to produce a flat {key:value} object. Do NOT use .map() to create arrays of objects.";
101
+ }
102
+ return null;
103
+ }
104
+ /**
105
+ * Proxied tools stringify a full MCP CallToolResult (`{ content: [...] }`).
106
+ * Aggregation-first heuristics must scan the inner business JSON, not the MCP envelope
107
+ * (otherwise `content` looks like a tabular array of objects and everything blocks).
108
+ */
109
+ unwrapForAggregationPolicyScan(input) {
110
+ if (typeof input === "string") {
111
+ const trimmed = input.trim();
112
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
113
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
114
+ try {
115
+ return this.unwrapForAggregationPolicyScan(JSON.parse(trimmed));
116
+ }
117
+ catch {
118
+ return input;
119
+ }
120
+ }
121
+ return input;
122
+ }
123
+ if (!input || typeof input !== "object") {
124
+ return input;
125
+ }
126
+ const rec = input;
127
+ if (!Array.isArray(rec.content) || rec.content.length === 0) {
128
+ return input;
129
+ }
130
+ const texts = [];
131
+ for (const part of rec.content) {
132
+ if (part && typeof part === "object" && "text" in part) {
133
+ const t = part.text;
134
+ if (typeof t === "string") {
135
+ texts.push(t);
136
+ }
137
+ }
138
+ }
139
+ if (texts.length === 0) {
140
+ return input;
141
+ }
142
+ const joined = texts.length === 1 ? texts[0] : texts.join("\n");
143
+ return this.unwrapForAggregationPolicyScan(joined);
144
+ }
145
+ violatesAggregationFirstPolicy(input, policyObj) {
146
+ if (typeof input === "string") {
147
+ const trimmed = input.trim();
148
+ if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
149
+ (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
150
+ try {
151
+ return this.violatesAggregationFirstPolicy(JSON.parse(trimmed), policyObj);
152
+ }
153
+ catch {
154
+ return false;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+ if (Array.isArray(input)) {
160
+ const maxRows = typeof policyObj === "object" &&
161
+ typeof policyObj.maxOutputRows === "number"
162
+ ? policyObj.maxOutputRows
163
+ : 10;
164
+ const allowPrimitives = typeof policyObj === "object" &&
165
+ typeof policyObj.allowPrimitiveArrays === "boolean"
166
+ ? policyObj.allowPrimitiveArrays
167
+ : true;
168
+ if (input.length > 0 &&
169
+ input.every((item) => typeof item === "object" && item !== null)) {
170
+ // Treat tabular row export as non-aggregated leakage risk if above threshold.
171
+ if (input.length > maxRows) {
172
+ return true;
173
+ }
174
+ return input.some((item) => this.violatesAggregationFirstPolicy(item, policyObj));
175
+ }
176
+ if (input.length > 0 &&
177
+ input.every((item) => typeof item !== "object" || item === null)) {
178
+ if (!allowPrimitives)
179
+ return true;
180
+ return false;
181
+ }
182
+ return input.some((item) => this.violatesAggregationFirstPolicy(item, policyObj));
183
+ }
184
+ if (input && typeof input === "object") {
185
+ return Object.values(input).some((value) => this.violatesAggregationFirstPolicy(value, policyObj));
186
+ }
187
+ return false;
36
188
  }
37
189
  constructor(serverInfo, config) {
38
190
  this.serverInfo = serverInfo;
39
191
  this.config = config;
40
- this.piiScanner = new PiiScanner(this.config?.security?.piiPatterns || [], this.config?.security?.forbiddenKeys || []);
192
+ this.piiScanner = new PiiScanner(this.config?.security?.piiPatterns ?? PII_PRESETS.GLOBAL_STRICT, this.config?.security?.forbiddenKeys ?? [
193
+ "id",
194
+ "name",
195
+ "fullName",
196
+ "firstName",
197
+ "lastName",
198
+ "address",
199
+ "street",
200
+ "city",
201
+ "postalCode",
202
+ "zipCode",
203
+ "phone",
204
+ "email",
205
+ "ssn",
206
+ "accountHolder",
207
+ "accountNumber",
208
+ "account_number",
209
+ "password",
210
+ "token",
211
+ "secret",
212
+ "privateKey",
213
+ ]);
41
214
  // Initialize Zero-Blocking Worker Pool for Heavy Cryptography & Sandboxing
42
215
  const isTS = import.meta.url.endsWith(".ts");
43
216
  const workerExt = isTS ? ".ts" : ".js";
@@ -67,6 +240,86 @@ export class LiopServer {
67
240
  taskQueue: new FixedQueue(),
68
241
  execArgv,
69
242
  });
243
+ // [Token Economy] Auto-register LIOP protocol spec as a single Resource.
244
+ // This centralizes the envelope documentation that was previously
245
+ // duplicated in every tool description, reducing token overhead.
246
+ this.resource("LIOP Envelope Specification", "liop://protocol/envelope-spec", "Complete Logic-on-Origin envelope format, execution rules, and security constraints", "text/plain", () => Promise.resolve(this.buildEnvelopeSpec()));
247
+ }
248
+ /**
249
+ * Builds the centralized LIOP envelope specification document.
250
+ * Served as a single Resource (liop://protocol/envelope-spec) instead
251
+ * of being duplicated across every tool description.
252
+ */
253
+ buildEnvelopeSpec() {
254
+ const lines = [
255
+ "LIOP v1 Envelope Specification",
256
+ "================================",
257
+ "",
258
+ "FORMAT:",
259
+ "",
260
+ "Compact Envelope:",
261
+ " @LIOP{wasi_v1,TaskName}",
262
+ " <JavaScript code>",
263
+ " @END",
264
+ "",
265
+ "RUNTIME ENVIRONMENT:",
266
+ "- env.records: Array of data objects from the origin",
267
+ "- Must use 'return' to output results",
268
+ "- Zero-Trust WASI Sandbox (Node.js Worker Pool)",
269
+ "- Return aggregated objects, NOT raw row-level arrays",
270
+ "",
271
+ "SECURITY CONSTRAINTS:",
272
+ "- PII Egress Shield blocks raw identifiers in output",
273
+ "- Aggregation-First policy: prefer counts, averages, summaries",
274
+ "- AST Guardian: static analysis before execution",
275
+ ];
276
+ if (this.config?.security?.forbiddenKeys?.length) {
277
+ lines.push(`- Restricted fields: ${this.config.security.forbiddenKeys.join(", ")}`);
278
+ }
279
+ lines.push("", "OPTIONAL PARAMETERS:", "- __liop_bypass_ast_cache: boolean (force AST re-evaluation)");
280
+ return lines.join("\n");
281
+ }
282
+ /**
283
+ * Extracts a compact, human-readable field summary from a JSON Schema.
284
+ *
285
+ * Walks the schema structure to find actual data property names and types,
286
+ * rather than returning top-level schema metadata keys (type, items, etc.).
287
+ *
288
+ * Example output for a banking schema:
289
+ * "Array of {id(string), accountHolder(string), balance(number), transactions(array of {date(string), amount(number)})}"
290
+ */
291
+ extractSchemaFieldSummary(schema, depth = 0) {
292
+ // Prevent excessive recursion in deeply nested schemas
293
+ if (depth > 3)
294
+ return "{...}";
295
+ const schemaType = schema.type;
296
+ const properties = schema.properties;
297
+ const items = schema.items;
298
+ // Object with properties → list field names with their types
299
+ if (properties) {
300
+ const fields = Object.entries(properties).map(([key, prop]) => {
301
+ const propType = prop.type;
302
+ if (propType === "array" && prop.items) {
303
+ const nested = this.extractSchemaFieldSummary(prop.items, depth + 1);
304
+ return `${key}(array of ${nested})`;
305
+ }
306
+ if (propType === "object" && prop.properties) {
307
+ const nested = this.extractSchemaFieldSummary(prop, depth + 1);
308
+ return `${key}(${nested})`;
309
+ }
310
+ return `${key}(${propType || "unknown"})`;
311
+ });
312
+ return `{${fields.join(", ")}}`;
313
+ }
314
+ // Array type → describe the items structure
315
+ if (schemaType === "array" && items) {
316
+ const itemsSummary = this.extractSchemaFieldSummary(items, depth + 1);
317
+ return `Array of ${itemsSummary}`;
318
+ }
319
+ // Simple type or unknown structure → fallback to key listing
320
+ if (schemaType)
321
+ return schemaType;
322
+ return Object.keys(schema).join(", ");
70
323
  }
71
324
  /**
72
325
  * Convenience alias for connectToMesh(), matching official documentation.
@@ -77,7 +330,7 @@ export class LiopServer {
77
330
  /**
78
331
  * Register a new Tool
79
332
  */
80
- tool(name, description, shape, handler) {
333
+ tool(name, description, shape, handler, policy) {
81
334
  if (this.tools.has(name)) {
82
335
  throw new Error(`Tool already registered: ${name}`);
83
336
  }
@@ -88,14 +341,21 @@ export class LiopServer {
88
341
  // LIOP Zero-Shot Autonomy Middleware: Detect Logic-on-Origin tools
89
342
  if (shape.payload && shape.payload instanceof z.ZodString) {
90
343
  const blockedKeys = this.config?.security?.forbiddenKeys || [];
91
- finalDescription += `\n\n[LIOP-PROTO-V1: LOGIC-ON-ORIGIN SPECIFICATION]\nCRITICAL: This tool requires a strictly formatted Logic-on-Origin payload. Failure to wrap JavaScript code within the LIOP envelope will result in a MalformedPayloadError.\n\nREQUIRED FORMAT:\nLIOP_MAGIC:0x00FF\nMANIFEST:{"target":"wasi_v1","name":"[ModuleName]","integrity_checks":true}\n---BEGIN_LOGIC---\n// Pure JavaScript logic. Access data via 'env.records'.\n// You MUST use 'return' to output results.\n---END_LOGIC---\n\nExecution Environment: Zero-Trust WASI Sandbox (Node.js Worker Pool).`;
344
+ // [Token Economy] Centralized description: reference the protocol spec
345
+ // Resource instead of duplicating the full envelope format per tool.
346
+ // Same information, delivered once via liop://protocol/envelope-spec.
347
+ finalDescription +=
348
+ "\n\nPayload: LIOP v1 envelope (WASI sandbox)." +
349
+ " Format: @LIOP{wasi_v1,TaskName}\\n<JS code>\\n@END" +
350
+ " | Access data: env.records. Return aggregated object." +
351
+ " | Full spec: resource liop://protocol/envelope-spec";
92
352
  if (blockedKeys.length > 0) {
93
- finalDescription += `\n// SECURITY RESTRICTION: Do NOT include any of the following fields: ${blockedKeys.join(", ")}`;
353
+ finalDescription += `\nRestricted fields: ${blockedKeys.join(", ")}.`;
94
354
  }
95
355
  if (this.activeSchema) {
96
- finalDescription += `\n\nSTRICT SCHEMA ADHERENCE:\nThe 'env.records' array contains objects with: ${JSON.stringify(this.activeSchema)}`;
356
+ const schemaDigest = this.extractSchemaFieldSummary(this.activeSchema);
357
+ finalDescription += `\nData structure: ${schemaDigest}. Full schema: resource liop://schema/global`;
97
358
  }
98
- finalDescription += `\n\nOptional: You can include an "__liop_bypass_ast_cache" boolean parameter set to true to force AST re-evaluation.`;
99
359
  finalHandler = async (args, _extra) => {
100
360
  const clientId = "global_connection"; // Simplify for now, treating the instance as one connection
101
361
  const now = Date.now();
@@ -131,10 +391,20 @@ export class LiopServer {
131
391
  if (logic) {
132
392
  args.payload = logic;
133
393
  // DELEGATE TO WORKER POOL: Parallel PQC & Sandboxing
134
- return await this.executeInWorkerPool(args, logic);
394
+ const preflightReason = this.runPreflightPolicy(name, logic, policy);
395
+ if (preflightReason) {
396
+ return {
397
+ content: [{ type: "text", text: preflightReason }],
398
+ isError: true,
399
+ };
400
+ }
401
+ return await this.executeInWorkerPool(args, logic, name);
135
402
  }
136
403
  }
137
404
  if (!logic) {
405
+ if (respectPlainToolPayload()) {
406
+ return await handler(args, _extra);
407
+ }
138
408
  stats.failures++;
139
409
  stats.lastAttempt = now;
140
410
  this.connectionStats.set(clientId, stats);
@@ -142,7 +412,7 @@ export class LiopServer {
142
412
  content: [
143
413
  {
144
414
  type: "text",
145
- text: 'Error: Malformed payload. Missing LIOP_MAGIC, MANIFEST, or boundaries.\nYou MUST wrap your logic exactly like this:\n\nLIOP_MAGIC:0x00FF\nMANIFEST:{"target":"wasi_v1","name":"DynamicAudit","integrity_checks":true}\n---BEGIN_LOGIC---\n// Your JS code here\n---END_LOGIC---',
415
+ text: "Error: Malformed payload. Missing @LIOP boundary.\\nYou MUST wrap your logic exactly like this:\\n\\n@LIOP{wasi_v1,DynamicAudit}\\n// Your JS code here\\n@END",
146
416
  },
147
417
  ],
148
418
  isError: true,
@@ -155,7 +425,17 @@ export class LiopServer {
155
425
  // Extract pure logic and deliver it to the developer's function
156
426
  args.payload = logic;
157
427
  // DELEGATE TO WORKER POOL: Parallel PQC & Sandboxing (Includes PII Shield)
158
- const result = await this.executeInWorkerPool(args, logic);
428
+ const preflightReason = this.runPreflightPolicy(name, logic, policy);
429
+ if (preflightReason) {
430
+ stats.failures++;
431
+ stats.lastAttempt = now;
432
+ this.connectionStats.set(clientId, stats);
433
+ return {
434
+ content: [{ type: "text", text: preflightReason }],
435
+ isError: true,
436
+ };
437
+ }
438
+ const result = await this.executeInWorkerPool(args, logic, name);
159
439
  if (!result.isError) {
160
440
  this.connectionStats.set(clientId, {
161
441
  failures: 0,
@@ -196,11 +476,12 @@ export class LiopServer {
196
476
  tool: { name, description: finalDescription, inputSchema },
197
477
  handler: finalHandler,
198
478
  schema,
479
+ policy,
199
480
  });
200
481
  // [LIOP-ALPHA] Auto-announce capability to the Mesh P2P DHT if node is active
201
482
  if (this.meshNode) {
202
483
  this.meshNode.announceCapability(name).catch((err) => {
203
- console.error(`[LIOP-Mesh] Failed to auto-announce tool ${name}: ${err.message}`);
484
+ log.info(`[LIOP-Mesh] Failed to auto-announce tool ${name}: ${err.message}`);
204
485
  });
205
486
  }
206
487
  }
@@ -234,13 +515,11 @@ Your objective is to perform secure Logic-on-Origin injections. You must process
234
515
  INDUSTRIAL CONSTRAINTS & PROTOCOL RULES:
235
516
  1. DATA PRIVACY: NEVER attempt to export Personally Identifiable Information (PII). The LIOP Egress Shield will block any response containing raw IDs, names, or addresses.
236
517
  2. AGGREGATION FIRST: Always prefer returning counts, averages, or anonymized summaries.
237
- 3. PAYLOAD ENCAPSULATION: Your JavaScript payloads MUST strictly adhere to the LIOPv1 Envelope. DO NOT include markdown backticks or leading text inside the 'payload' argument.
518
+ 3. PAYLOAD ENCAPSULATION: Your JavaScript payloads MUST strictly adhere to the Compact Envelope. DO NOT include markdown backticks or leading text inside the 'payload' argument.
238
519
  Structure:
239
- LIOP_MAGIC:0x00FF
240
- MANIFEST:{"target":"wasi_v1","name":"AnalysisTask","integrity_checks":true}
241
- ---BEGIN_LOGIC---
520
+ @LIOP{wasi_v1,AnalysisTask}
242
521
  // Your JS Code Here
243
- ---END_LOGIC---
522
+ @END
244
523
  4. RUNTIME SCOPE: The execution environment provides a global 'env' object. Use 'env.records' to access the target dataset.
245
524
  5. LOCALIZATION: Format all JSON response keys in the language used by the user in their query (e.g., use Spanish keys if the query is in Spanish).
246
525
  6. SCHEMA RIGIDITY: Only use fields defined in the 'Data Dictionary'. Usage of non-existent fields will trigger a sandbox runtime exception.${this.activeSchema
@@ -268,13 +547,15 @@ Protocol Adherence is mandatory for successful execution.`,
268
547
  */
269
548
  dataDictionary(schema, name = "Global Medical Data Dictionary", uri = "liop://schema/global", description = "Exposes the internal database schema for Zero-Shot Autonomy planning") {
270
549
  this.activeSchema = schema;
271
- // Retroactively update tool descriptions for already registered tools
550
+ // [Token Economy] Retroactively update tool descriptions with schema field references.
551
+ // Extracts actual data property names from the JSON Schema structure.
552
+ const schemaDigest = this.extractSchemaFieldSummary(schema);
272
553
  for (const [toolName, entry] of this.tools.entries()) {
273
554
  if (entry.schema.shape.payload &&
274
555
  entry.schema.shape.payload instanceof z.ZodString &&
275
556
  entry.tool.description &&
276
- !entry.tool.description.includes("STRICT SCHEMA ADHERENCE")) {
277
- entry.tool.description += `\n\nSTRICT SCHEMA ADHERENCE:\nThe 'env.records' array contains objects with the EXACT following structure. ONLY use these fields. Do NOT guess or use fallbacks (e.g. do not use 'gender' if not listed below):\n${JSON.stringify(schema, null, 2)}`;
557
+ !entry.tool.description.includes("Data structure:")) {
558
+ entry.tool.description += `\nData structure: ${schemaDigest}. Full schema: resource ${uri}`;
278
559
  this.tools.set(toolName, entry);
279
560
  }
280
561
  }
@@ -285,7 +566,7 @@ Protocol Adherence is mandatory for successful execution.`,
285
566
  */
286
567
  clearAstCache() {
287
568
  this.logicCache.clear();
288
- console.error("[LIOP-SDK] AST Security Cache cleared by Admin.");
569
+ log.info("[LIOP-SDK] AST Security Cache cleared by Admin.");
289
570
  }
290
571
  /**
291
572
  * Emulates calling a tool (used locally or via LIOPMcpBridge)
@@ -310,8 +591,15 @@ Protocol Adherence is mandatory for successful execution.`,
310
591
  .payload;
311
592
  const logic = this.extractLogic(payload);
312
593
  if (logic) {
594
+ const preflightReason = this.runPreflightPolicy(request.name, logic, entry.policy);
595
+ if (preflightReason) {
596
+ return {
597
+ content: [{ type: "text", text: preflightReason }],
598
+ isError: true,
599
+ };
600
+ }
313
601
  parsedArgs.payload = logic;
314
- return await this.executeInWorkerPool(parsedArgs, logic);
602
+ return await this.executeInWorkerPool(parsedArgs, logic, request.name);
315
603
  }
316
604
  }
317
605
  const result = await entry.handler(parsedArgs, {});
@@ -401,12 +689,18 @@ Protocol Adherence is mandatory for successful execution.`,
401
689
  setSandboxData(records) {
402
690
  this.sandboxRecords = records;
403
691
  }
692
+ getBoundPort() {
693
+ return this.boundPort;
694
+ }
404
695
  /**
405
696
  * Connects to the libp2p Kademlia DHT and announces capabilities.
406
697
  * Boots the gRPC server for secure Logic-on-Origin.
407
698
  */
408
699
  async connectToMesh(options = {}) {
409
- const port = options.port || 50051;
700
+ const envPort = process.env.LIOP_GRPC_PORT
701
+ ? Number.parseInt(process.env.LIOP_GRPC_PORT, 10)
702
+ : undefined;
703
+ const port = options.port ?? envPort ?? 50051;
410
704
  // 1. Initialize Mesh Node (Discovery)
411
705
  this.meshNode = new MeshNode(options.meshConfig);
412
706
  await this.meshNode.start();
@@ -436,16 +730,16 @@ Protocol Adherence is mandatory for successful execution.`,
436
730
  });
437
731
  // 3. Announce local tools to the DHT
438
732
  for (const tool of this.listTools()) {
439
- await this.meshNode.announceCapability(tool.name).catch(console.error);
733
+ await this.meshNode.announceCapability(tool.name).catch(log.info);
440
734
  }
441
735
  // 4. Announce manifest availability
442
- await this.meshNode.announceManifest().catch(console.error);
736
+ await this.meshNode.announceManifest().catch(log.info);
443
737
  // 5. Initialize gRPC Server (Execution)
444
738
  this.rpcServer = new LiopRpcServer();
445
739
  this.rpcServer.addService({
446
740
  negotiateIntent: (call, callback) => {
447
741
  const request = call.request;
448
- console.error(`[LIOP-RPC] Negotiating intent for capability: ${request.capability_hash}`);
742
+ log.info(`[LIOP-RPC] Negotiating intent for capability: ${request.capability_hash}`);
449
743
  // Standard dynamic import to avoid potential circularity
450
744
  import("../rpc/crypto/kyber.js").then(async ({ Kyber768Wrapper }) => {
451
745
  const { publicKey, secretKey } = await Kyber768Wrapper.generateKeyPair();
@@ -464,7 +758,7 @@ Protocol Adherence is mandatory for successful execution.`,
464
758
  },
465
759
  executeLogic: async (call) => {
466
760
  const request = call.request;
467
- console.error(`[LIOP-RPC] Executing Logic-on-Origin for session: ${request.session_token}`);
761
+ log.info(`[LIOP-RPC] Executing Logic-on-Origin for session: ${request.session_token}`);
468
762
  const session = this.sessions.get(request.session_token);
469
763
  if (!session) {
470
764
  call.emit("error", {
@@ -485,13 +779,16 @@ Protocol Adherence is mandatory for successful execution.`,
485
779
  sessionToken: request.session_token,
486
780
  isEncrypted: true,
487
781
  });
488
- let finalOutput = workerResponse.output;
489
- // [PROTOCOL TRANSFORMER] Support for Proxied Tool Calls
490
- // If the execution resulted in a special proxy command, handle it
782
+ let finalOutput;
491
783
  try {
784
+ finalOutput =
785
+ typeof workerResponse.output === "string"
786
+ ? workerResponse.output
787
+ : JSON.stringify(workerResponse.output);
788
+ // [PROTOCOL TRANSFORMER] Support for Proxied Tool Calls
492
789
  const decoded = JSON.parse(finalOutput);
493
790
  if (decoded.__liop_proxy_tool) {
494
- console.error(`[LIOP-RPC] Executing Proxied Tool: ${decoded.__liop_proxy_tool}`);
791
+ log.info(`[LIOP-RPC] Executing Proxied Tool: ${decoded.__liop_proxy_tool}`);
495
792
  const toolResult = await this.callTool({
496
793
  name: decoded.__liop_proxy_tool,
497
794
  arguments: decoded.__liop_proxy_args || {},
@@ -500,24 +797,26 @@ Protocol Adherence is mandatory for successful execution.`,
500
797
  }
501
798
  }
502
799
  catch {
503
- // Not a proxy command, continue with raw output
800
+ finalOutput = String(workerResponse.output);
504
801
  }
505
802
  const response = {
506
803
  semantic_evidence: finalOutput,
507
804
  cryptographic_proof: Buffer.from(workerResponse.image_id || "", "hex"),
508
- zk_receipt: crypto
509
- .createHash("sha256")
510
- .update(finalOutput)
511
- .digest(), // Deterministic Beta Seal
805
+ zk_receipt: workerResponse.zk_receipt
806
+ ? Buffer.from(workerResponse.zk_receipt, "base64")
807
+ : Buffer.from(""),
512
808
  is_error: false,
513
809
  };
514
810
  // Final PII check for gRPC egress
515
811
  const violation = this.piiScanner.scan([
516
812
  { type: "text", text: finalOutput },
517
813
  ]);
518
- if (violation) {
519
- console.error(`[LIOP-RPC] PII Leak blocked in gRPC stream: ${violation}`);
520
- response.semantic_evidence = `[LIOP] Egress Security Violation. Output blocked due to PII leakage (${violation}).`;
814
+ const aggregationViolation = this.violatesAggregationFirstPolicy(this.unwrapForAggregationPolicyScan(finalOutput));
815
+ if (violation || aggregationViolation) {
816
+ const reason = violation ||
817
+ "Aggregation-First Policy Violation: row-level export blocked. HINT: Use .reduce() to produce a flat {key:value} object. Do NOT use .map() to create arrays of objects.";
818
+ log.info(`[LIOP-RPC] Secure egress blocked in gRPC stream: ${reason}`);
819
+ response.semantic_evidence = `[LIOP] Egress Security Violation. Output blocked due to policy enforcement (${reason}).`;
521
820
  response.is_error = true;
522
821
  }
523
822
  call.write(response, () => {
@@ -526,7 +825,7 @@ Protocol Adherence is mandatory for successful execution.`,
526
825
  }
527
826
  catch (error) {
528
827
  const e = error;
529
- console.error(`[LIOP-RPC] Execution Error: ${e.message}`);
828
+ log.error(`[LIOP-RPC] Execution Error: ${e.message}`);
530
829
  // Send error response before closing, avoiding "stream closed without results"
531
830
  const errorResponse = {
532
831
  semantic_evidence: `Execution Error: ${e.message}`,
@@ -545,13 +844,13 @@ Protocol Adherence is mandatory for successful execution.`,
545
844
  }
546
845
  },
547
846
  });
548
- await this.rpcServer.listen(port);
549
- console.error(`[LIOP-SDK] Node successfully announced to Mesh. PeerID: ${this.meshNode.getPeerId()}`);
847
+ this.boundPort = await this.rpcServer.listen(port);
848
+ log.info(`[LIOP-SDK] Node successfully announced to Mesh. PeerID: ${this.meshNode.getPeerId()}`);
550
849
  }
551
850
  /**
552
851
  * Internal worker execution with Egress Filtering logic.
553
852
  */
554
- async executeInWorkerPool(_args, rawPayload) {
853
+ async executeInWorkerPool(_args, rawPayload, toolName) {
555
854
  try {
556
855
  // Transparent local execution without dynamic PQC
557
856
  const workerResponse = await this.workerPool.run({
@@ -577,15 +876,29 @@ Protocol Adherence is mandatory for successful execution.`,
577
876
  text: textOutput,
578
877
  },
579
878
  ];
879
+ const toolPolicy = toolName
880
+ ? this.tools.get(toolName)?.policy
881
+ : undefined;
882
+ const policyViolation = this.validateOutputPolicy(toolName || "unknown_tool", workerResponse.output, toolPolicy);
883
+ if (policyViolation) {
884
+ log.info(`[LIOP-SDK] Output policy blocked for ${toolName || "unknown_tool"}: ${policyViolation}`);
885
+ return {
886
+ content: [{ type: "text", text: `[LIOP] ${policyViolation}` }],
887
+ isError: true,
888
+ };
889
+ }
580
890
  // Professional PII Protection Guard
581
891
  const violation = this.piiScanner.scan(content);
582
- if (violation) {
583
- console.error(`[LIOP-SDK] PII Leak blocked in local execution: ${violation}`);
892
+ const aggregationViolation = this.violatesAggregationFirstPolicy(workerResponse.output);
893
+ if (violation || aggregationViolation) {
894
+ const reason = violation ||
895
+ "Aggregation-First Policy Violation: row-level export blocked. HINT: Use .reduce() to produce a flat {key:value} object. Do NOT use .map() to create arrays of objects.";
896
+ log.info(`[LIOP-SDK] Secure egress blocked in local execution: ${reason}`);
584
897
  return {
585
898
  content: [
586
899
  {
587
900
  type: "text",
588
- text: `[LIOP] Egress Security Violation. Output blocked due to PII leakage (${violation}).`,
901
+ text: `[LIOP] Egress Security Violation. Output blocked due to policy enforcement (${reason}).`,
589
902
  },
590
903
  ],
591
904
  isError: true,
@@ -14,6 +14,18 @@ export declare const PII_PATTERNS: {
14
14
  CREDIT_CARD: PiiRuleDefinition;
15
15
  IP_ADDRESS: PiiRuleDefinition;
16
16
  PHONE: PiiRuleDefinition;
17
+ SSN: PiiRuleDefinition;
18
+ IBAN: PiiRuleDefinition;
19
+ PASSPORT_MRZ: PiiRuleDefinition;
20
+ };
21
+ /**
22
+ * Regional and Cultural Security Presets for Out-Of-The-Box compliance.
23
+ * Developers can override, merge, or omit these based on local laws.
24
+ */
25
+ export declare const PII_PRESETS: {
26
+ GLOBAL_STRICT: PiiRuleDefinition[];
27
+ US_COMPLIANT: PiiRuleDefinition[];
28
+ EU_GDPR: PiiRuleDefinition[];
17
29
  };
18
30
  export declare class PiiScanner {
19
31
  private patterns;