@nekzus/liop 2.0.0-alpha.1 → 2.0.0-alpha.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.
Files changed (123) hide show
  1. package/dist/bin/agent.d.ts +0 -1
  2. package/dist/bin/agent.js +5 -306
  3. package/dist/bin/agent.js.map +1 -0
  4. package/dist/{bridge/stream.d.ts → bridge.d.ts} +44 -3
  5. package/dist/bridge.js +2 -0
  6. package/dist/bridge.js.map +1 -0
  7. package/dist/chunk-4ABAFG44.js +33 -0
  8. package/dist/chunk-4ABAFG44.js.map +1 -0
  9. package/dist/chunk-ANFXJGMP.js +2 -0
  10. package/dist/chunk-ANFXJGMP.js.map +1 -0
  11. package/dist/chunk-DBXGYHKY.js +2 -0
  12. package/dist/chunk-DBXGYHKY.js.map +1 -0
  13. package/dist/chunk-HM77MWB6.js +2 -0
  14. package/dist/chunk-HM77MWB6.js.map +1 -0
  15. package/dist/chunk-HNDVAKEK.js +24 -0
  16. package/dist/chunk-HNDVAKEK.js.map +1 -0
  17. package/dist/chunk-HQZHZM6U.js +2 -0
  18. package/dist/chunk-HQZHZM6U.js.map +1 -0
  19. package/dist/chunk-P52IE4L6.js +2 -0
  20. package/dist/chunk-P52IE4L6.js.map +1 -0
  21. package/dist/chunk-PIBCW4BD.js +13 -0
  22. package/dist/chunk-PIBCW4BD.js.map +1 -0
  23. package/dist/chunk-PPCOS2NU.js +2 -0
  24. package/dist/chunk-PPCOS2NU.js.map +1 -0
  25. package/dist/chunk-RWRRBYG4.js +2 -0
  26. package/dist/chunk-RWRRBYG4.js.map +1 -0
  27. package/dist/chunk-S6RJHZV2.js +2 -0
  28. package/dist/chunk-S6RJHZV2.js.map +1 -0
  29. package/dist/chunk-UVTEJYHN.js +2 -0
  30. package/dist/chunk-UVTEJYHN.js.map +1 -0
  31. package/dist/chunk-X6FJATUE.js +29 -0
  32. package/dist/chunk-X6FJATUE.js.map +1 -0
  33. package/dist/chunk-XLVRRGOX.js +3 -0
  34. package/dist/chunk-XLVRRGOX.js.map +1 -0
  35. package/dist/client.d.ts +5 -0
  36. package/dist/client.js +2 -0
  37. package/dist/client.js.map +1 -0
  38. package/dist/{gateway/router.d.ts → gateway.d.ts} +30 -5
  39. package/dist/gateway.js +2 -0
  40. package/dist/gateway.js.map +1 -0
  41. package/dist/{client/index.d.ts → index-CyxNLlz7.d.ts} +24 -5
  42. package/dist/index.d.ts +313 -12
  43. package/dist/index.js +31 -12
  44. package/dist/index.js.map +1 -0
  45. package/dist/kyber-2WDOTUQX.js +2 -0
  46. package/dist/kyber-2WDOTUQX.js.map +1 -0
  47. package/dist/{mesh/node.d.ts → mesh.d.ts} +5 -3
  48. package/dist/mesh.js +2 -0
  49. package/dist/mesh.js.map +1 -0
  50. package/dist/{server/index.d.ts → server.d.ts} +125 -12
  51. package/dist/server.js +2 -0
  52. package/dist/server.js.map +1 -0
  53. package/dist/types.d.ts +17 -14
  54. package/dist/types.js +2 -26
  55. package/dist/types.js.map +1 -0
  56. package/dist/{crypto/verifier.d.ts → verifier-DTCD9imJ.d.ts} +3 -1
  57. package/dist/verifier-RQRYXA4C.js +2 -0
  58. package/dist/verifier-RQRYXA4C.js.map +1 -0
  59. package/dist/workers/logic-execution.d.ts +4 -2
  60. package/dist/workers/logic-execution.js +2 -123
  61. package/dist/workers/logic-execution.js.map +1 -0
  62. package/dist/workers/zk-verifier.d.ts +4 -2
  63. package/dist/workers/zk-verifier.js +2 -98
  64. package/dist/workers/zk-verifier.js.map +1 -0
  65. package/package.json +31 -18
  66. package/dist/bridge/index.d.ts +0 -37
  67. package/dist/bridge/index.js +0 -249
  68. package/dist/bridge/stream.js +0 -210
  69. package/dist/client/index.js +0 -275
  70. package/dist/crypto/logic-image-id.d.ts +0 -3
  71. package/dist/crypto/logic-image-id.js +0 -27
  72. package/dist/crypto/verifier.js +0 -97
  73. package/dist/economy/estimator.d.ts +0 -53
  74. package/dist/economy/estimator.js +0 -69
  75. package/dist/economy/index.d.ts +0 -5
  76. package/dist/economy/index.js +0 -3
  77. package/dist/economy/otel.d.ts +0 -38
  78. package/dist/economy/otel.js +0 -100
  79. package/dist/economy/telemetry.d.ts +0 -77
  80. package/dist/economy/telemetry.js +0 -224
  81. package/dist/errors.d.ts +0 -14
  82. package/dist/errors.js +0 -19
  83. package/dist/gateway/hybrid.d.ts +0 -23
  84. package/dist/gateway/hybrid.js +0 -199
  85. package/dist/gateway/router.js +0 -1054
  86. package/dist/mesh/index.d.ts +0 -1
  87. package/dist/mesh/index.js +0 -1
  88. package/dist/mesh/node.js +0 -853
  89. package/dist/prompts/adapters.d.ts +0 -16
  90. package/dist/prompts/adapters.js +0 -55
  91. package/dist/rpc/client.d.ts +0 -22
  92. package/dist/rpc/client.js +0 -40
  93. package/dist/rpc/codec/lpm.d.ts +0 -20
  94. package/dist/rpc/codec/lpm.js +0 -36
  95. package/dist/rpc/crypto/aes.d.ts +0 -22
  96. package/dist/rpc/crypto/aes.js +0 -47
  97. package/dist/rpc/crypto/kyber.d.ts +0 -27
  98. package/dist/rpc/crypto/kyber.js +0 -70
  99. package/dist/rpc/proto.d.ts +0 -2
  100. package/dist/rpc/proto.js +0 -33
  101. package/dist/rpc/server.d.ts +0 -13
  102. package/dist/rpc/server.js +0 -50
  103. package/dist/rpc/tls.d.ts +0 -26
  104. package/dist/rpc/tls.js +0 -54
  105. package/dist/rpc/types.d.ts +0 -28
  106. package/dist/rpc/types.js +0 -5
  107. package/dist/sandbox/guardian.d.ts +0 -18
  108. package/dist/sandbox/guardian.js +0 -58
  109. package/dist/sandbox/wasi.d.ts +0 -36
  110. package/dist/sandbox/wasi.js +0 -233
  111. package/dist/security/guardian.d.ts +0 -22
  112. package/dist/security/guardian.js +0 -52
  113. package/dist/security/zk.d.ts +0 -37
  114. package/dist/security/zk.js +0 -76
  115. package/dist/server/index.js +0 -1047
  116. package/dist/server/ner-scanner.d.ts +0 -29
  117. package/dist/server/ner-scanner.js +0 -141
  118. package/dist/server/pii.d.ts +0 -66
  119. package/dist/server/pii.js +0 -428
  120. package/dist/utils/logger.d.ts +0 -21
  121. package/dist/utils/logger.js +0 -70
  122. package/dist/utils/mcpCompact.d.ts +0 -11
  123. package/dist/utils/mcpCompact.js +0 -29
@@ -1,1047 +0,0 @@
1
- import { Buffer } from "node:buffer";
2
- import crypto from "node:crypto";
3
- import { createRequire } from "node:module";
4
- import path from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
6
- import * as grpc from "@grpc/grpc-js";
7
- import { FixedQueue, Piscina } from "piscina";
8
- import { z } from "zod";
9
- import { zodToJsonSchema } from "zod-to-json-schema";
10
- import { MeshNode } from "../mesh/node.js";
11
- import { LiopRpcServer } from "../rpc/server.js";
12
- import { log } from "../utils/logger.js";
13
- import { NerScanner } from "./ner-scanner.js";
14
- import { PII_PATTERNS, PII_PRESETS, PiiScanner } from "./pii.js";
15
- export { NerScanner, PII_PATTERNS, PII_PRESETS, PiiScanner };
16
- /**
17
- * When enabled, `payload` tools that are not LIOP v1 envelopes are passed through to the
18
- * registered handler unchanged (no worker extraction). Default off for strict protocol tests.
19
- */
20
- function respectPlainToolPayload() {
21
- const v = process.env.LIOP_RESPECT_PLAIN_TOOL_PAYLOAD?.toLowerCase().trim();
22
- return v === "1" || v === "true" || v === "yes";
23
- }
24
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
25
- export class LiopServer {
26
- serverInfo;
27
- config;
28
- logicCache = new Map();
29
- connectionStats = new Map();
30
- CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
31
- THROTTLE_THRESHOLD = 5;
32
- THROTTLE_COOLDOWN_MS = 60 * 1000; // 60 seconds
33
- // [OWASP-A01] Sliding window rate limiter — prevents micro-query exfiltration
34
- toolCallWindows = new Map();
35
- toolCallMaxPerWindow;
36
- toolCallWindowMs;
37
- tools = new Map();
38
- resources = new Map();
39
- prompts = new Map();
40
- activeSchema = null;
41
- sandboxRecords = [];
42
- piiScanner;
43
- workerPool;
44
- meshNode = null;
45
- rpcServer = null;
46
- boundPort = null;
47
- sessions = new Map();
48
- // Compact envelope: @LIOP{target,name}\n<code>\n@END
49
- static LIOP_COMPACT_REGEX = /@LIOP\{(?<target>[^,}]+)(?:,(?<name>[^}]*))?\}\n(?<logic>[\s\S]*?)\n@END/m;
50
- extractLogic(payload) {
51
- const compact = payload.match(LiopServer.LIOP_COMPACT_REGEX);
52
- return compact?.groups?.logic ? compact.groups.logic.trim() : null;
53
- }
54
- parseUnknownJson(input) {
55
- if (typeof input !== "string")
56
- return input;
57
- const trimmed = input.trim();
58
- if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
59
- (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
60
- try {
61
- return JSON.parse(trimmed);
62
- }
63
- catch {
64
- return input;
65
- }
66
- }
67
- return input;
68
- }
69
- runPreflightPolicy(_toolName, logic, policy) {
70
- if (!policy)
71
- return null;
72
- const compact = logic.replace(/\s+/g, " ");
73
- if (policy.enforceAggregationFirst) {
74
- const rowExtractionPatterns = [
75
- // Block raw record dumps but allow safe aggregation chains
76
- // (.reduce, .length, .filter().length, .every, .some)
77
- /return\s+env\.records(?!\s*\.\s*(?:reduce|length|filter|every|some|find)\b)/i,
78
- /return\s*\{[\s\S]*\b(accounts|patients|rows|records)\s*:\s*env\.records(?!\s*\.\s*(?:reduce|length|filter)\b)/i,
79
- ];
80
- if (rowExtractionPatterns.some((p) => p.test(compact))) {
81
- return "Preflight policy rejected: potential row-level export pattern detected.";
82
- }
83
- }
84
- if (policy.preflightDenyPatterns?.some((p) => p.test(compact))) {
85
- return "Preflight policy rejected: custom deny pattern matched.";
86
- }
87
- return null;
88
- }
89
- validateOutputPolicy(toolName, output, policy) {
90
- if (!policy)
91
- return null;
92
- const parsed = this.parseUnknownJson(output);
93
- if (policy.outputSchema) {
94
- // SEC-HARDENING: Force strict mode on ZodObject schemas to prevent
95
- // key aliasing bypasses via .passthrough(). However, respect schemas
96
- // that explicitly use .catchall() — calling .strict() would override
97
- // the catchall with ZodNever, destroying the developer's intent.
98
- const effectiveSchema = (() => {
99
- if (!(policy.outputSchema instanceof z.ZodObject)) {
100
- return policy.outputSchema;
101
- }
102
- const obj = policy.outputSchema;
103
- // If schema has an explicit catchall (not ZodNever), respect it
104
- if (!(obj._def.catchall instanceof z.ZodNever)) {
105
- return obj;
106
- }
107
- // Otherwise force strict to block unrecognized keys by default
108
- return obj.strict();
109
- })();
110
- const schemaResult = effectiveSchema.safeParse(parsed);
111
- if (!schemaResult.success) {
112
- // SEC-CRITICAL: Never expose rejected data in error messages.
113
- // Only report the structural violation (unrecognized keys, type mismatches).
114
- return `[LIOP] Output schema violation for ${toolName}: ${schemaResult.error.issues
115
- .map((i) => `${i.path.join(".") || "<root>"} ${i.message}`)
116
- .join("; ")}. HINT: Your output must conform to the declared schema. Use 'env.records' to access the dataset and return only allowed fields.`;
117
- }
118
- }
119
- if (policy.enforceAggregationFirst &&
120
- this.violatesAggregationFirstPolicy(this.unwrapForAggregationPolicyScan(parsed), policy.enforceAggregationFirst)) {
121
- 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.";
122
- }
123
- return null;
124
- }
125
- /**
126
- * Proxied tools stringify a full MCP CallToolResult (`{ content: [...] }`).
127
- * Aggregation-first heuristics must scan the inner business JSON, not the MCP envelope
128
- * (otherwise `content` looks like a tabular array of objects and everything blocks).
129
- */
130
- unwrapForAggregationPolicyScan(input) {
131
- if (typeof input === "string") {
132
- const trimmed = input.trim();
133
- if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
134
- (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
135
- try {
136
- return this.unwrapForAggregationPolicyScan(JSON.parse(trimmed));
137
- }
138
- catch {
139
- return input;
140
- }
141
- }
142
- return input;
143
- }
144
- if (!input || typeof input !== "object") {
145
- return input;
146
- }
147
- const rec = input;
148
- if (!Array.isArray(rec.content) || rec.content.length === 0) {
149
- return input;
150
- }
151
- const texts = [];
152
- for (const part of rec.content) {
153
- if (part && typeof part === "object" && "text" in part) {
154
- const t = part.text;
155
- if (typeof t === "string") {
156
- texts.push(t);
157
- }
158
- }
159
- }
160
- if (texts.length === 0) {
161
- return input;
162
- }
163
- const joined = texts.length === 1 ? texts[0] : texts.join("\n");
164
- return this.unwrapForAggregationPolicyScan(joined);
165
- }
166
- violatesAggregationFirstPolicy(input, policyObj) {
167
- const maxRows = typeof policyObj === "object" &&
168
- typeof policyObj.maxOutputRows === "number"
169
- ? policyObj.maxOutputRows
170
- : 10;
171
- const allowPrimitives = typeof policyObj === "object" &&
172
- typeof policyObj.allowPrimitiveArrays === "boolean"
173
- ? policyObj.allowPrimitiveArrays
174
- : true;
175
- if (typeof input === "string") {
176
- const trimmed = input.trim();
177
- if ((trimmed.startsWith("{") && trimmed.endsWith("}")) ||
178
- (trimmed.startsWith("[") && trimmed.endsWith("]"))) {
179
- try {
180
- return this.violatesAggregationFirstPolicy(JSON.parse(trimmed), policyObj);
181
- }
182
- catch {
183
- return false;
184
- }
185
- }
186
- return false;
187
- }
188
- if (Array.isArray(input)) {
189
- if (input.length > 0 &&
190
- input.every((item) => typeof item === "object" && item !== null)) {
191
- // Treat tabular row export as non-aggregated leakage risk if above threshold.
192
- if (input.length > maxRows) {
193
- return true;
194
- }
195
- return input.some((item) => this.violatesAggregationFirstPolicy(item, policyObj));
196
- }
197
- if (input.length > 0 &&
198
- input.every((item) => typeof item !== "object" || item === null)) {
199
- if (!allowPrimitives)
200
- return true;
201
- return false;
202
- }
203
- return input.some((item) => this.violatesAggregationFirstPolicy(item, policyObj));
204
- }
205
- if (input && typeof input === "object") {
206
- const keys = Object.keys(input);
207
- // Treat flat dictionary with too many keys as non-aggregated leakage risk (Dynamic Key Bypass).
208
- if (keys.length > maxRows) {
209
- return true;
210
- }
211
- return Object.values(input).some((value) => this.violatesAggregationFirstPolicy(value, policyObj));
212
- }
213
- return false;
214
- }
215
- constructor(serverInfo, config) {
216
- this.serverInfo = serverInfo;
217
- this.config = config;
218
- const nerScanner = this.config?.security?.enableNerScanning
219
- ? new NerScanner()
220
- : null;
221
- this.piiScanner = new PiiScanner(this.config?.security?.piiPatterns ?? PII_PRESETS.GLOBAL_STRICT, this.config?.security?.forbiddenKeys ?? [
222
- "id",
223
- "name",
224
- "fullName",
225
- "firstName",
226
- "lastName",
227
- "address",
228
- "street",
229
- "city",
230
- "postalCode",
231
- "zipCode",
232
- "phone",
233
- "email",
234
- "ssn",
235
- "accountHolder",
236
- "accountNumber",
237
- "account_number",
238
- "password",
239
- "token",
240
- "secret",
241
- "privateKey",
242
- ], nerScanner);
243
- // [OWASP-A01] Rate limit: config > env > default (30 calls/min)
244
- const rlConfig = this.config?.security?.rateLimit;
245
- this.toolCallWindowMs =
246
- rlConfig?.windowMs ??
247
- Number.parseInt(process.env.LIOP_RATE_LIMIT_WINDOW_MS ?? "60000", 10);
248
- this.toolCallMaxPerWindow =
249
- rlConfig?.maxPerWindow ??
250
- Number.parseInt(process.env.LIOP_RATE_LIMIT_MAX ?? "30", 10);
251
- // Initialize Zero-Blocking Worker Pool for Heavy Cryptography & Sandboxing
252
- const isTS = import.meta.url.endsWith(".ts");
253
- const workerExt = isTS ? ".ts" : ".js";
254
- let execArgv = [];
255
- if (isTS) {
256
- try {
257
- const req = createRequire(import.meta.url);
258
- const tsxPkg = req.resolve("tsx/package.json");
259
- const absoluteTsx = pathToFileURL(path.join(path.dirname(tsxPkg), "dist", "loader.mjs")).href;
260
- execArgv = ["--import", absoluteTsx];
261
- }
262
- catch (_e) {
263
- execArgv = ["--import", "tsx"];
264
- }
265
- }
266
- const isTest = process.env.NODE_ENV === "test" || process.env.VITEST;
267
- // Sync capabilities to serverInfo for MCP Handshakes
268
- if (this.config?.capabilities && !this.serverInfo.capabilities) {
269
- this.serverInfo.capabilities = this.config.capabilities;
270
- }
271
- this.workerPool = new Piscina({
272
- filename: path.resolve(__dirname, `../workers/logic-execution${workerExt}`),
273
- minThreads: this.config?.workerPool?.minThreads ?? (isTest ? 0 : 2),
274
- maxThreads: this.config?.workerPool?.maxThreads ?? (isTest ? 1 : 8),
275
- idleTimeout: this.config?.workerPool?.idleTimeout ?? (isTest ? 500 : 5000),
276
- maxQueue: "auto",
277
- taskQueue: new FixedQueue(),
278
- execArgv,
279
- // [DoS Defense] Enforce hard memory ceiling per worker thread.
280
- // Workers exceeding this limit are terminated by Node.js runtime.
281
- resourceLimits: {
282
- maxOldGenerationSizeMb: this.config?.workerPool?.maxHeapMb ??
283
- Number.parseInt(process.env.LIOP_WORKER_MAX_HEAP_MB ?? "64", 10),
284
- },
285
- });
286
- // [Token Economy] Auto-register LIOP protocol spec as a single Resource.
287
- // This centralizes the envelope documentation that was previously
288
- // duplicated in every tool description, reducing token overhead.
289
- 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()));
290
- }
291
- /**
292
- * Builds the centralized LIOP envelope specification document.
293
- * Served as a single Resource (liop://protocol/envelope-spec) instead
294
- * of being duplicated across every tool description.
295
- */
296
- buildEnvelopeSpec() {
297
- const lines = [
298
- "LIOP v1 Envelope Specification",
299
- "================================",
300
- "",
301
- "FORMAT:",
302
- "",
303
- "Compact Envelope:",
304
- " @LIOP{wasi_v1,TaskName}",
305
- " <JavaScript code>",
306
- " @END",
307
- "",
308
- "RUNTIME ENVIRONMENT:",
309
- "- env.records: Array of data objects from the origin",
310
- "- Must use 'return' to output results",
311
- "- Zero-Trust WASI Sandbox (Node.js Worker Pool)",
312
- "- Return aggregated objects, NOT raw row-level arrays",
313
- "",
314
- "SECURITY CONSTRAINTS:",
315
- "- PII Egress Shield blocks raw identifiers in output",
316
- "- Aggregation-First policy: prefer counts, averages, summaries",
317
- "- AST Guardian: static analysis before execution",
318
- ];
319
- if (this.config?.security?.forbiddenKeys?.length) {
320
- lines.push(`- Restricted fields: ${this.config.security.forbiddenKeys.join(", ")}`);
321
- }
322
- lines.push("", "OPTIONAL PARAMETERS:", "- __liop_bypass_ast_cache: boolean (force AST re-evaluation)");
323
- return lines.join("\n");
324
- }
325
- /**
326
- * Extracts a compact, human-readable field summary from a JSON Schema.
327
- *
328
- * Walks the schema structure to find actual data property names and types,
329
- * rather than returning top-level schema metadata keys (type, items, etc.).
330
- *
331
- * Example output for a banking schema:
332
- * "Array of {id(string), accountHolder(string), balance(number), transactions(array of {date(string), amount(number)})}"
333
- */
334
- extractSchemaFieldSummary(schema, depth = 0) {
335
- // Prevent excessive recursion in deeply nested schemas
336
- if (depth > 3)
337
- return "{...}";
338
- const schemaType = schema.type;
339
- const properties = schema.properties;
340
- const items = schema.items;
341
- // Object with properties → list field names with their types
342
- if (properties) {
343
- const fields = Object.entries(properties).map(([key, prop]) => {
344
- const propType = prop.type;
345
- if (propType === "array" && prop.items) {
346
- const nested = this.extractSchemaFieldSummary(prop.items, depth + 1);
347
- return `${key}(array of ${nested})`;
348
- }
349
- if (propType === "object" && prop.properties) {
350
- const nested = this.extractSchemaFieldSummary(prop, depth + 1);
351
- return `${key}(${nested})`;
352
- }
353
- return `${key}(${propType || "unknown"})`;
354
- });
355
- return `{${fields.join(", ")}}`;
356
- }
357
- // Array type → describe the items structure
358
- if (schemaType === "array" && items) {
359
- const itemsSummary = this.extractSchemaFieldSummary(items, depth + 1);
360
- return `Array of ${itemsSummary}`;
361
- }
362
- // Simple type or unknown structure → fallback to key listing
363
- if (schemaType)
364
- return schemaType;
365
- return Object.keys(schema).join(", ");
366
- }
367
- /**
368
- * Convenience alias for connectToMesh(), matching official documentation.
369
- */
370
- async connect(options = {}) {
371
- return this.connectToMesh(options);
372
- }
373
- /**
374
- * Register a new Tool
375
- */
376
- tool(name, description, shape, handler, policy) {
377
- if (this.tools.has(name)) {
378
- throw new Error(`Tool already registered: ${name}`);
379
- }
380
- const schema = z.object(shape);
381
- const generatedSchema = zodToJsonSchema(schema);
382
- let finalDescription = description;
383
- let finalHandler = handler;
384
- // LIOP Zero-Shot Autonomy Middleware: Detect Logic-on-Origin tools
385
- if (shape.payload && shape.payload instanceof z.ZodString) {
386
- const blockedKeys = this.config?.security?.forbiddenKeys || [];
387
- // [Token Economy] Centralized description: reference the protocol spec
388
- // Resource instead of duplicating the full envelope format per tool.
389
- // Same information, delivered once via liop://protocol/envelope-spec.
390
- finalDescription +=
391
- "\n\nPayload: LIOP v1 envelope (WASI sandbox)." +
392
- " Format: @LIOP{wasi_v1,TaskName}\\n<JS code>\\n@END" +
393
- " | Access data: env.records. Return aggregated object." +
394
- " | Full spec: resource liop://protocol/envelope-spec";
395
- if (blockedKeys.length > 0) {
396
- finalDescription += `\nRestricted fields: ${blockedKeys.join(", ")}.`;
397
- }
398
- if (this.activeSchema) {
399
- const schemaDigest = this.extractSchemaFieldSummary(this.activeSchema);
400
- finalDescription += `\nData structure: ${schemaDigest}. Full schema: resource liop://schema/global`;
401
- }
402
- finalHandler = async (args, _extra) => {
403
- const clientId = "global_connection"; // Simplify for now, treating the instance as one connection
404
- const now = Date.now();
405
- const stats = this.connectionStats.get(clientId) || {
406
- failures: 0,
407
- lastAttempt: 0,
408
- };
409
- if (stats.failures >= this.THROTTLE_THRESHOLD &&
410
- now - stats.lastAttempt < this.THROTTLE_COOLDOWN_MS) {
411
- return {
412
- content: [
413
- {
414
- type: "text",
415
- text: "LIOP_THROTTLED: Too many violations. Cooling down for 60 seconds.",
416
- },
417
- ],
418
- isError: true,
419
- };
420
- }
421
- const payloadValue = args
422
- .payload;
423
- const bypassCache = args.__liop_bypass_ast_cache === true;
424
- const payloadHash = crypto
425
- .createHash("sha256")
426
- .update(payloadValue)
427
- .digest("hex");
428
- const logic = this.extractLogic(payloadValue);
429
- const cached = this.logicCache.get(payloadHash);
430
- if (!bypassCache &&
431
- cached &&
432
- now - cached.timestamp < this.CACHE_TTL_MS) {
433
- // Hash verified. Skips boundaries check (already validated!). Extract logic directly.
434
- if (logic) {
435
- args.payload = logic;
436
- // DELEGATE TO WORKER POOL: Parallel PQC & Sandboxing
437
- const preflightReason = this.runPreflightPolicy(name, logic, policy);
438
- if (preflightReason) {
439
- return {
440
- content: [{ type: "text", text: preflightReason }],
441
- isError: true,
442
- };
443
- }
444
- return await this.executeInWorkerPool(args, logic, name);
445
- }
446
- }
447
- if (!logic) {
448
- if (respectPlainToolPayload()) {
449
- return await handler(args, _extra);
450
- }
451
- stats.failures++;
452
- stats.lastAttempt = now;
453
- this.connectionStats.set(clientId, stats);
454
- return {
455
- content: [
456
- {
457
- type: "text",
458
- 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",
459
- },
460
- ],
461
- isError: true,
462
- };
463
- }
464
- try {
465
- // Logic check already performed above, extraction is guaranteed at this point.
466
- // biome-ignore lint/style/noNonNullAssertion: safe extraction after check
467
- const logic = this.extractLogic(args.payload);
468
- // Extract pure logic and deliver it to the developer's function
469
- args.payload = logic;
470
- // DELEGATE TO WORKER POOL: Parallel PQC & Sandboxing (Includes PII Shield)
471
- const preflightReason = this.runPreflightPolicy(name, logic, policy);
472
- if (preflightReason) {
473
- stats.failures++;
474
- stats.lastAttempt = now;
475
- this.connectionStats.set(clientId, stats);
476
- return {
477
- content: [{ type: "text", text: preflightReason }],
478
- isError: true,
479
- };
480
- }
481
- const result = await this.executeInWorkerPool(args, logic, name);
482
- if (!result.isError) {
483
- this.connectionStats.set(clientId, {
484
- failures: 0,
485
- lastAttempt: now,
486
- });
487
- this.logicCache.set(payloadHash, {
488
- hash: payloadHash,
489
- timestamp: now,
490
- });
491
- }
492
- else {
493
- stats.failures++;
494
- stats.lastAttempt = now;
495
- this.connectionStats.set(clientId, stats);
496
- }
497
- return result;
498
- }
499
- catch (error) {
500
- const e = error;
501
- stats.failures++;
502
- stats.lastAttempt = now;
503
- this.connectionStats.set(clientId, stats);
504
- return {
505
- content: [
506
- { type: "text", text: `ExecutionRuntimeException: ${e.message}` },
507
- ],
508
- isError: true,
509
- };
510
- }
511
- };
512
- }
513
- const inputSchema = {
514
- type: "object",
515
- properties: generatedSchema.properties || {},
516
- required: generatedSchema.required,
517
- };
518
- this.tools.set(name, {
519
- tool: { name, description: finalDescription, inputSchema },
520
- handler: finalHandler,
521
- schema,
522
- policy,
523
- });
524
- // [LIOP-ALPHA] Auto-announce capability to the Mesh P2P DHT if node is active
525
- if (this.meshNode) {
526
- this.meshNode.announceCapability(name).catch((err) => {
527
- log.info(`[LIOP-Mesh] Failed to auto-announce tool ${name}: ${err.message}`);
528
- });
529
- }
530
- }
531
- /**
532
- * Register a dynamic prompt
533
- */
534
- prompt(name, description, args, handler) {
535
- if (this.prompts.has(name)) {
536
- throw new Error(`Prompt already registered: ${name}`);
537
- }
538
- this.prompts.set(name, {
539
- prompt: { name, description, arguments: args },
540
- handler,
541
- });
542
- }
543
- /**
544
- * Enables LIOP Zero-Shot Autonomy by registering the Blind Analyst standard prompt.
545
- */
546
- enableZeroShotAutonomy() {
547
- this.prompt("liop_blind_analyst", "The official Logic-Injection-on-Origin Protocol system prompt. Instructs the LLM on how to securely inject Logic-on-Origin without violating PII or safety constraints.", [], (_request) => {
548
- return {
549
- description: "LIOP Blind Analyst Instructions",
550
- messages: [
551
- {
552
- role: "user",
553
- content: {
554
- type: "text",
555
- text: `You are the "Blind Analyst" operating within the Logic-Injection-on-Origin Protocol (LIOP) ecosystem.
556
- Your objective is to perform secure Logic-on-Origin injections. You must process remote data without ever requesting its extraction.
557
-
558
- INDUSTRIAL CONSTRAINTS & PROTOCOL RULES:
559
- 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.
560
- 2. AGGREGATION FIRST: Always prefer returning counts, averages, or anonymized summaries.
561
- 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.
562
- Structure:
563
- @LIOP{wasi_v1,AnalysisTask}
564
- // Your JS Code Here
565
- @END
566
- 4. RUNTIME SCOPE: The execution environment provides a global 'env' object. Use 'env.records' to access the target dataset.
567
- 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).
568
- 6. SCHEMA RIGIDITY: Only use fields defined in the 'Data Dictionary'. Usage of non-existent fields will trigger a sandbox runtime exception.${this.activeSchema
569
- ? `\n\nCURRENT DATA DICTIONARY (STRICT):\n${JSON.stringify(this.activeSchema, null, 2)}`
570
- : ""}
571
-
572
- Protocol Adherence is mandatory for successful execution.`,
573
- },
574
- },
575
- ],
576
- };
577
- });
578
- }
579
- /**
580
- * Register a dynamic resource
581
- */
582
- resource(name, uri, description, mimeType, content) {
583
- if (this.resources.has(uri)) {
584
- throw new Error(`Resource URI already registered: ${uri}`);
585
- }
586
- this.resources.set(uri, { name, uri, description, mimeType, content });
587
- }
588
- /**
589
- * Broadcasts the Data Dictionary to the LLM prior to code injection.
590
- */
591
- dataDictionary(schema, name = "Global Medical Data Dictionary", uri = "liop://schema/global", description = "Exposes the internal database schema for Zero-Shot Autonomy planning") {
592
- this.activeSchema = schema;
593
- // [Token Economy] Retroactively update tool descriptions with schema field references.
594
- // Extracts actual data property names from the JSON Schema structure.
595
- const schemaDigest = this.extractSchemaFieldSummary(schema);
596
- for (const [toolName, entry] of this.tools.entries()) {
597
- if (entry.schema.shape.payload &&
598
- entry.schema.shape.payload instanceof z.ZodString &&
599
- entry.tool.description &&
600
- !entry.tool.description.includes("Data structure:")) {
601
- entry.tool.description += `\nData structure: ${schemaDigest}. Full schema: resource ${uri}`;
602
- this.tools.set(toolName, entry);
603
- }
604
- }
605
- this.resource(name, uri, description, "application/json", JSON.stringify(schema, null, 2));
606
- }
607
- /**
608
- * Manually invalidates the AST Logic Cache (e.g. for Zero-Day patches).
609
- */
610
- clearAstCache() {
611
- this.logicCache.clear();
612
- log.info("[LIOP-SDK] AST Security Cache cleared by Admin.");
613
- }
614
- /**
615
- * Sliding window rate limiter for tool call frequency.
616
- * Prevents micro-query exfiltration attacks where an attacker
617
- * makes hundreds of individually-legitimate calls to reconstruct
618
- * the full dataset field by field. (OWASP A01)
619
- */
620
- checkToolCallRateLimit(toolName) {
621
- const now = Date.now();
622
- const windowMs = this.toolCallWindowMs;
623
- const maxPerWindow = this.toolCallMaxPerWindow;
624
- const window = this.toolCallWindows.get(toolName) || [];
625
- // Evict expired timestamps outside the sliding window
626
- const active = window.filter((t) => now - t < windowMs);
627
- if (active.length >= maxPerWindow) {
628
- const retryAfterSec = Math.ceil((active[0] + windowMs - now) / 1000);
629
- return {
630
- content: [
631
- {
632
- type: "text",
633
- text: `LIOP_RATE_LIMITED: Too many calls to ${toolName}. ` +
634
- `Max ${maxPerWindow} per ${windowMs / 1000}s window. ` +
635
- `Retry after ${retryAfterSec}s.`,
636
- },
637
- ],
638
- isError: true,
639
- };
640
- }
641
- active.push(now);
642
- this.toolCallWindows.set(toolName, active);
643
- return null;
644
- }
645
- /**
646
- * Emulates calling a tool (used locally or via LIOPMcpBridge)
647
- */
648
- async callTool(request) {
649
- const entry = this.tools.get(request.name);
650
- if (!entry) {
651
- throw new Error(`Tool not found: ${request.name}`);
652
- }
653
- // [OWASP-A01] Rate limiting: prevent micro-query exfiltration
654
- const rateLimitResult = this.checkToolCallRateLimit(request.name);
655
- if (rateLimitResult)
656
- return rateLimitResult;
657
- try {
658
- // Validate inputs natively with Zod before execution
659
- const parsedArgs = entry.schema.parse(request.arguments || {});
660
- // Re-inject the bypass flag if present since Zod might strip unrecognized keys
661
- if (request.arguments
662
- ?.__liop_bypass_ast_cache === true) {
663
- parsedArgs.__liop_bypass_ast_cache = true;
664
- }
665
- // [LOGIC-ON-ORIGIN] Intercept code injection directly
666
- if (parsedArgs &&
667
- typeof parsedArgs.payload === "string") {
668
- const payload = parsedArgs
669
- .payload;
670
- const logic = this.extractLogic(payload);
671
- if (logic) {
672
- const preflightReason = this.runPreflightPolicy(request.name, logic, entry.policy);
673
- if (preflightReason) {
674
- return {
675
- content: [{ type: "text", text: preflightReason }],
676
- isError: true,
677
- };
678
- }
679
- parsedArgs.payload = logic;
680
- return await this.executeInWorkerPool(parsedArgs, logic, request.name);
681
- }
682
- }
683
- const result = await entry.handler(parsedArgs, {});
684
- return result;
685
- }
686
- catch (error) {
687
- const e = error;
688
- if (e instanceof z.ZodError) {
689
- return {
690
- content: [{ type: "text", text: `Validation Error: ${e.message}` }],
691
- isError: true,
692
- };
693
- }
694
- return {
695
- content: [
696
- { type: "text", text: `Internal Execution Error: ${e.message}` },
697
- ],
698
- isError: true,
699
- };
700
- }
701
- }
702
- /**
703
- * Retrieves registered tools
704
- */
705
- listTools() {
706
- return Array.from(this.tools.values()).map((t) => t.tool);
707
- }
708
- /**
709
- * Retrieves registered prompts
710
- */
711
- listPrompts() {
712
- return Array.from(this.prompts.values()).map((p) => p.prompt);
713
- }
714
- /**
715
- * Gets a specific prompt by name
716
- */
717
- async getPrompt(request) {
718
- const entry = this.prompts.get(request.name);
719
- if (!entry) {
720
- throw new Error(`Prompt not found: ${request.name}`);
721
- }
722
- return await entry.handler(request);
723
- }
724
- /**
725
- * Retrieves registered resources
726
- */
727
- listResources() {
728
- return Array.from(this.resources.values());
729
- }
730
- /**
731
- * Reads a specific resource by URI
732
- */
733
- async readResource(uri) {
734
- const resource = this.resources.get(uri);
735
- if (!resource) {
736
- throw new Error(`Resource not found: ${uri}`);
737
- }
738
- let text = "No description provided";
739
- if (typeof resource.content === "function") {
740
- text = await resource.content();
741
- }
742
- else if (typeof resource.content === "string") {
743
- text = resource.content;
744
- }
745
- else if (resource.description) {
746
- text = resource.description;
747
- }
748
- return {
749
- contents: [
750
- {
751
- uri: resource.uri,
752
- mimeType: resource.mimeType || "text/plain",
753
- text,
754
- },
755
- ],
756
- };
757
- }
758
- getServerInfo() {
759
- return this.serverInfo;
760
- }
761
- getMeshNode() {
762
- return this.meshNode;
763
- }
764
- /**
765
- * Injects data into the secure sandbox context for Logic-on-Origin tools.
766
- */
767
- setSandboxData(records) {
768
- this.sandboxRecords = records;
769
- }
770
- getBoundPort() {
771
- return this.boundPort;
772
- }
773
- /**
774
- * Connects to the libp2p Kademlia DHT and announces capabilities.
775
- * Boots the gRPC server for secure Logic-on-Origin.
776
- */
777
- async connectToMesh(options = {}) {
778
- const envPort = process.env.LIOP_GRPC_PORT
779
- ? Number.parseInt(process.env.LIOP_GRPC_PORT, 10)
780
- : undefined;
781
- const port = options.port ?? envPort ?? 50051;
782
- // 1. Initialize Mesh Node (Discovery)
783
- this.meshNode = new MeshNode(options.meshConfig);
784
- await this.meshNode.start();
785
- // 2. Register LIOP Manifest Protocol Handler
786
- // This allows remote peers to query our tool/resource metadata dynamically.
787
- const meshNodeRef = this.meshNode;
788
- this.meshNode.registerManifestHandler(() => {
789
- const tools = this.listTools().map((t) => ({
790
- name: t.name,
791
- description: t.description,
792
- inputSchema: t.inputSchema,
793
- }));
794
- const resources = Array.from(this.resources.values()).map((r) => ({
795
- name: r.name,
796
- uri: r.uri,
797
- description: r.description,
798
- mimeType: r.mimeType,
799
- text: typeof r.content === "string" ? r.content : r.description,
800
- }));
801
- return {
802
- peerId: meshNodeRef.getPeerId(),
803
- grpcPort: port,
804
- tools,
805
- resources,
806
- serverInfo: this.serverInfo,
807
- };
808
- });
809
- // 3. Announce local tools to the DHT
810
- for (const tool of this.listTools()) {
811
- await this.meshNode.announceCapability(tool.name).catch(log.info);
812
- }
813
- // 4. Announce manifest availability
814
- await this.meshNode.announceManifest().catch(log.info);
815
- // 5. Initialize gRPC Server (Execution)
816
- this.rpcServer = new LiopRpcServer();
817
- this.rpcServer.addService({
818
- negotiateIntent: (call, callback) => {
819
- const request = call.request;
820
- log.info(`[LIOP-RPC] Negotiating intent for capability: ${request.capability_hash}`);
821
- // Standard dynamic import to avoid potential circularity
822
- import("../rpc/crypto/kyber.js").then(async ({ Kyber768Wrapper }) => {
823
- const { publicKey, secretKey } = await Kyber768Wrapper.generateKeyPair();
824
- const sessionToken = crypto.randomUUID();
825
- this.sessions.set(sessionToken, {
826
- capability_hash: request.capability_hash,
827
- kyber_sk: secretKey,
828
- });
829
- callback(null, {
830
- accepted: true,
831
- session_token: sessionToken,
832
- error_message: "",
833
- kyber_public_key: publicKey,
834
- });
835
- });
836
- },
837
- executeLogic: async (call) => {
838
- const request = call.request;
839
- log.info(`[LIOP-RPC] Executing Logic-on-Origin for session: ${request.session_token}`);
840
- const session = this.sessions.get(request.session_token);
841
- if (!session) {
842
- call.emit("error", {
843
- code: grpc.status.UNAUTHENTICATED,
844
- details: "Invalid session token",
845
- });
846
- return;
847
- }
848
- try {
849
- // Pass to Worker Pool for PQC Decryption and WASI/V8 execution
850
- const workerResponse = await this.workerPool.run({
851
- ciphertext: request.pqc_ciphertext,
852
- secretKeyObj: Array.from(session.kyber_sk),
853
- wasmBinary: request.wasm_binary,
854
- inputs: request.inputs,
855
- aesNonce: request.aes_nonce,
856
- records: this.sandboxRecords,
857
- sessionToken: request.session_token,
858
- isEncrypted: true,
859
- });
860
- let finalOutput;
861
- try {
862
- finalOutput =
863
- typeof workerResponse.output === "string"
864
- ? workerResponse.output
865
- : JSON.stringify(workerResponse.output);
866
- // [PROTOCOL TRANSFORMER] Support for Proxied Tool Calls
867
- const decoded = JSON.parse(finalOutput);
868
- if (decoded.__liop_proxy_tool) {
869
- log.info(`[LIOP-RPC] Executing Proxied Tool: ${decoded.__liop_proxy_tool}`);
870
- const toolResult = await this.callTool({
871
- name: decoded.__liop_proxy_tool,
872
- arguments: decoded.__liop_proxy_args || {},
873
- });
874
- finalOutput = JSON.stringify(toolResult);
875
- }
876
- }
877
- catch {
878
- finalOutput = String(workerResponse.output);
879
- }
880
- const response = {
881
- semantic_evidence: finalOutput,
882
- cryptographic_proof: Buffer.from(workerResponse.image_id || "", "hex"),
883
- zk_receipt: workerResponse.zk_receipt
884
- ? Buffer.from(workerResponse.zk_receipt, "base64")
885
- : Buffer.from(""),
886
- is_error: false,
887
- };
888
- // Final PII check for gRPC egress
889
- const violation = this.piiScanner.scan([
890
- { type: "text", text: finalOutput },
891
- ]);
892
- const aggregationViolation = this.violatesAggregationFirstPolicy(this.unwrapForAggregationPolicyScan(finalOutput));
893
- if (violation || aggregationViolation) {
894
- // SEC-CRITICAL: Log details server-side, never expose to caller
895
- const internalReason = violation || "Aggregation-First Policy Violation";
896
- log.info(`[LIOP-RPC] Secure egress blocked in gRPC stream: ${internalReason}`);
897
- response.semantic_evidence =
898
- "[LIOP] Egress Security Violation. Output blocked due to policy enforcement.";
899
- response.is_error = true;
900
- }
901
- call.write(response, () => {
902
- call.end();
903
- });
904
- }
905
- catch (error) {
906
- const e = error;
907
- const isDev = process.env.NODE_ENV === "development" ||
908
- process.env.NODE_ENV === "test";
909
- const detail = e.message || String(error);
910
- log.error(`[LIOP-RPC] Execution Error: ${detail}`);
911
- const errorMessage = isDev
912
- ? `Execution Error: ${detail}`
913
- : "[LIOP] Execution Failed. The injected logic violated runtime constraints or encountered a fatal error.";
914
- // Send error response before closing, avoiding "stream closed without results"
915
- const errorResponse = {
916
- semantic_evidence: errorMessage,
917
- cryptographic_proof: Buffer.from(""),
918
- zk_receipt: Buffer.from(""),
919
- is_error: true,
920
- };
921
- try {
922
- call.write(errorResponse, () => {
923
- call.end();
924
- });
925
- }
926
- catch (_writeErr) {
927
- call.end();
928
- }
929
- }
930
- },
931
- });
932
- this.boundPort = await this.rpcServer.listen(port);
933
- log.info(`[LIOP-SDK] Node successfully announced to Mesh. PeerID: ${this.meshNode.getPeerId()}`);
934
- }
935
- /**
936
- * Internal worker execution with Egress Filtering logic.
937
- */
938
- async executeInWorkerPool(_args, rawPayload, toolName) {
939
- try {
940
- // Transparent local execution without dynamic PQC
941
- const workerResponse = await this.workerPool.run({
942
- ciphertext: new Uint8Array(0),
943
- secretKeyObj: Array.from(new Uint8Array(0)),
944
- kyberPublicKey: new Uint8Array(0),
945
- wasmBinary: Buffer.from(rawPayload),
946
- inputs: {},
947
- records: this.sandboxRecords,
948
- sessionToken: "local-dev-token",
949
- isEncrypted: false, // Use plaintext for local Logic-on-Origin injection
950
- });
951
- // Standard MCP Content Array
952
- const textOutput = JSON.stringify({
953
- computation_result: workerResponse.output,
954
- image_id: workerResponse.image_id,
955
- zk_receipt: workerResponse.zk_receipt,
956
- status: "Worker Pool Execution Success",
957
- });
958
- const content = [
959
- {
960
- type: "text",
961
- text: textOutput,
962
- },
963
- ];
964
- const toolPolicy = toolName
965
- ? this.tools.get(toolName)?.policy
966
- : undefined;
967
- const policyViolation = this.validateOutputPolicy(toolName || "unknown_tool", workerResponse.output, toolPolicy);
968
- if (policyViolation) {
969
- // SEC-CRITICAL: Log details server-side, never expose to caller in Production
970
- log.info(`[LIOP-SDK] Output policy blocked for ${toolName || "unknown_tool"}: ${policyViolation}`);
971
- const isDev = process.env.NODE_ENV === "development" ||
972
- process.env.NODE_ENV === "test";
973
- const errorMessage = isDev
974
- ? policyViolation
975
- : "[LIOP] Egress Security Violation. Output blocked due to policy enforcement. HINT: Return only aggregated, non-PII results using .reduce() to produce a flat {key:value} object with allowed schema fields.";
976
- return {
977
- content: [
978
- {
979
- type: "text",
980
- text: errorMessage,
981
- },
982
- ],
983
- isError: true,
984
- };
985
- }
986
- // Professional PII Protection Guard
987
- const violation = this.piiScanner.scan(content);
988
- const aggregationViolation = this.violatesAggregationFirstPolicy(workerResponse.output);
989
- if (violation || aggregationViolation) {
990
- // SEC-CRITICAL: Log the specific violation reason server-side only.
991
- // Never expose detection details (entity names, matched values) to the caller in Production.
992
- const internalReason = violation ||
993
- "Aggregation-First Policy Violation: Output blocked due to dynamic flat-key policy enforcement.";
994
- log.info(`[LIOP-SDK] Secure egress blocked in local execution: ${internalReason}`);
995
- const isDev = process.env.NODE_ENV === "development" ||
996
- process.env.NODE_ENV === "test";
997
- const errorMessage = isDev
998
- ? `[LIOP] Egress Security Violation: ${internalReason}`
999
- : "[LIOP] Egress Security Violation. Output blocked due to policy enforcement. HINT: Return only aggregated, non-PII results using .reduce() to produce a flat {key:value} object with allowed schema fields.";
1000
- return {
1001
- content: [
1002
- {
1003
- type: "text",
1004
- text: errorMessage,
1005
- },
1006
- ],
1007
- isError: true,
1008
- };
1009
- }
1010
- return { content };
1011
- }
1012
- catch (error) {
1013
- const e = error;
1014
- const isDev = process.env.NODE_ENV === "development" ||
1015
- process.env.NODE_ENV === "test";
1016
- const detail = e.message || String(error);
1017
- log.error(`[LIOP-SDK] WorkerPool Execution Fault: ${detail}`);
1018
- const errorMessage = isDev
1019
- ? `WorkerPoolError: ${detail}`
1020
- : "[LIOP] Execution Failed. The injected logic violated runtime constraints or encountered a fatal error.";
1021
- return {
1022
- content: [
1023
- {
1024
- type: "text",
1025
- text: errorMessage,
1026
- },
1027
- ],
1028
- isError: true,
1029
- };
1030
- }
1031
- }
1032
- /**
1033
- * Safely destroys the worker pool, gRPC server, and Mesh node.
1034
- * Recommended to be called during graceful shutdowns or test teardowns.
1035
- */
1036
- async close() {
1037
- if (this.workerPool) {
1038
- await this.workerPool.close({ force: true });
1039
- }
1040
- if (this.rpcServer) {
1041
- await this.rpcServer.stop();
1042
- }
1043
- if (this.meshNode) {
1044
- await this.meshNode.stop();
1045
- }
1046
- }
1047
- }