@elizaos/interop 2.0.0-alpha.2 → 2.0.0-alpha.201

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 (30) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts.map +1 -0
  3. package/dist/{typescript/python-bridge.d.ts → python-bridge.d.ts} +16 -0
  4. package/dist/python-bridge.d.ts.map +1 -0
  5. package/dist/{typescript/python-bridge.js → python-bridge.js} +61 -4
  6. package/dist/types.d.ts.map +1 -0
  7. package/dist/{typescript/wasm-loader.d.ts → wasm-loader.d.ts} +4 -0
  8. package/dist/wasm-loader.d.ts.map +1 -0
  9. package/dist/{typescript/wasm-loader.js → wasm-loader.js} +53 -8
  10. package/package.json +6 -6
  11. package/python/tests/test_rust_ffi_loader.py +2 -2
  12. package/dist/packages/interop/tsconfig.tsbuildinfo +0 -1
  13. package/dist/tsconfig.tsbuildinfo +0 -1
  14. package/dist/typescript/index.d.ts.map +0 -1
  15. package/dist/typescript/python-bridge.d.ts.map +0 -1
  16. package/dist/typescript/types.d.ts.map +0 -1
  17. package/dist/typescript/wasm-loader.d.ts.map +0 -1
  18. package/python/__pycache__/__init__.cpython-313.pyc +0 -0
  19. package/python/__pycache__/rust_ffi.cpython-313.pyc +0 -0
  20. package/python/__pycache__/ts_bridge.cpython-313.pyc +0 -0
  21. package/python/__pycache__/wasm_loader.cpython-313.pyc +0 -0
  22. package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  23. package/python/tests/__pycache__/test_bridge_server.cpython-313-pytest-9.0.2.pyc +0 -0
  24. package/python/tests/__pycache__/test_interop.cpython-313-pytest-9.0.2.pyc +0 -0
  25. package/python/tests/__pycache__/test_rust_ffi.cpython-313-pytest-9.0.2.pyc +0 -0
  26. package/python/tests/__pycache__/test_rust_ffi_loader.cpython-313-pytest-9.0.2.pyc +0 -0
  27. /package/dist/{typescript/index.d.ts → index.d.ts} +0 -0
  28. /package/dist/{typescript/index.js → index.js} +0 -0
  29. /package/dist/{typescript/types.d.ts → types.d.ts} +0 -0
  30. /package/dist/{typescript/types.js → types.js} +0 -0
package/README.md CHANGED
@@ -116,7 +116,7 @@ await runtime.register_plugin(plugin)
116
116
 
117
117
  ## Plugin Manifest
118
118
 
119
- Every cross-language plugin must include a `plugin.json` manifest:
119
+ Cross-language plugins are described by a manifest-like metadata object (format may vary by transport and host).
120
120
 
121
121
  ```json
122
122
  {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../typescript/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,iBAAiB,CAAC;AAEzB,YAAY,EACV,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,EAEf,UAAU,EACV,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,aAAa,EACb,eAAe,EAEf,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AACjB,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnE;;GAEG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAE5C,OAAO,KAAK,EAAuB,cAAc,EAAE,MAAM,SAAS,CAAC;AAGnE,MAAM,WAAW,sBAAsB;IACrC,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,MAAM,CAAC,CA4CjB"}
@@ -19,10 +19,25 @@ export interface PythonBridgeOptions {
19
19
  cwd?: string;
20
20
  /** Additional environment variables */
21
21
  env?: Record<string, string>;
22
+ /**
23
+ * Whether to inherit the parent process environment variables.
24
+ *
25
+ * Defaults to true for compatibility. For tighter isolation, set to false and pass only
26
+ * explicit `env` entries.
27
+ */
28
+ inheritEnv?: boolean;
29
+ /** Environment variable names to remove when inheriting. */
30
+ envDenylist?: string[];
22
31
  /** Path to the bridge script */
23
32
  bridgeScriptPath?: string;
24
33
  /** Connection timeout in milliseconds */
25
34
  timeout?: number;
35
+ /** Maximum number of in-flight IPC requests (prevents unbounded memory growth). */
36
+ maxPendingRequests?: number;
37
+ /** Maximum size (bytes) of a single newline-delimited IPC message. */
38
+ maxMessageBytes?: number;
39
+ /** Maximum size (bytes) of the internal stdout buffer. */
40
+ maxBufferBytes?: number;
26
41
  }
27
42
  /**
28
43
  * Python plugin bridge that communicates via subprocess
@@ -36,6 +51,7 @@ export declare class PythonPluginBridge extends EventEmitter {
36
51
  private initialized;
37
52
  private requestCounter;
38
53
  constructor(options: PythonBridgeOptions);
54
+ private buildChildEnv;
39
55
  /**
40
56
  * Start the Python subprocess
41
57
  */
@@ -0,0 +1 @@
1
+ {"version":3,"file":"python-bridge.d.ts","sourceRoot":"","sources":["../typescript/python-bridge.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EASV,MAAM,EAKP,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAGV,UAAU,EACV,WAAW,EACX,cAAc,EAGf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sEAAsE;IACtE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,0DAA0D;IAC1D,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAWD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,YAAY;IAStC,OAAO,CAAC,OAAO;IAR3B,OAAO,CAAC,OAAO,CACR;IACP,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAa;gBAEf,OAAO,EAAE,mBAAmB;IAIhD,OAAO,CAAC,aAAa;IA0BrB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuD5B;;OAEG;YACW,YAAY;IAsB1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAuDlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkBrB;;OAEG;IACG,WAAW,CAAC,CAAC,SAAS,WAAW,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAkCzE;;OAEG;IACH,WAAW,IAAI,cAAc,GAAG,IAAI;IAIpC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B3B;;OAEG;IACH,OAAO,CAAC,OAAO;CAWhB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAUjB;AA8KD;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpE"}
@@ -5,6 +5,7 @@
5
5
  * to the TypeScript Plugin interface.
6
6
  */
7
7
  import { EventEmitter } from "node:events";
8
+ import { logger } from "@elizaos/core";
8
9
  /**
9
10
  * Python plugin bridge that communicates via subprocess
10
11
  */
@@ -20,6 +21,27 @@ export class PythonPluginBridge extends EventEmitter {
20
21
  super();
21
22
  this.options = options;
22
23
  }
24
+ buildChildEnv() {
25
+ const inherit = this.options.inheritEnv !== false;
26
+ const base = {};
27
+ if (inherit) {
28
+ for (const [k, v] of Object.entries(process.env)) {
29
+ if (typeof v === "string") {
30
+ base[k] = v;
31
+ }
32
+ }
33
+ }
34
+ const deny = new Set(this.options.envDenylist ?? []);
35
+ for (const key of deny) {
36
+ delete base[key];
37
+ }
38
+ if (this.options.env) {
39
+ for (const [k, v] of Object.entries(this.options.env)) {
40
+ base[k] = v;
41
+ }
42
+ }
43
+ return base;
44
+ }
23
45
  /**
24
46
  * Start the Python subprocess
25
47
  */
@@ -30,7 +52,7 @@ export class PythonPluginBridge extends EventEmitter {
30
52
  new URL("../python/bridge_server.py", import.meta.url).pathname;
31
53
  this.process = spawn(pythonPath, ["-u", bridgeScript, "--module", this.options.moduleName], {
32
54
  cwd: this.options.cwd,
33
- env: { ...process.env, ...this.options.env },
55
+ env: this.buildChildEnv(),
34
56
  stdio: ["pipe", "pipe", "pipe"],
35
57
  });
36
58
  // Handle stdout (JSON-RPC messages)
@@ -42,7 +64,12 @@ export class PythonPluginBridge extends EventEmitter {
42
64
  // Handle stderr (logging)
43
65
  if (this.process.stderr) {
44
66
  this.process.stderr.on("data", (data) => {
45
- console.error(`[Python Plugin ${this.options.moduleName}]`, data.toString());
67
+ logger.error({
68
+ src: "interop:python-bridge",
69
+ event: "interop.ipc.stderr",
70
+ moduleName: this.options.moduleName,
71
+ stream: "stderr",
72
+ }, data.toString());
46
73
  });
47
74
  }
48
75
  // Handle process exit
@@ -80,18 +107,44 @@ export class PythonPluginBridge extends EventEmitter {
80
107
  * Handle incoming data from subprocess
81
108
  */
82
109
  handleData(data) {
110
+ const maxBufferBytes = this.options.maxBufferBytes ?? 2_000_000;
111
+ const maxMessageBytes = this.options.maxMessageBytes ?? 1_000_000;
83
112
  this.messageBuffer += data;
113
+ if (Buffer.byteLength(this.messageBuffer, "utf8") > maxBufferBytes) {
114
+ logger.error({
115
+ src: "interop:python-bridge",
116
+ event: "interop.ipc.stdout_buffer_exceeded",
117
+ moduleName: this.options.moduleName,
118
+ }, `IPC stdout buffer exceeded limit (${maxBufferBytes} bytes); terminating bridge`);
119
+ this.process?.kill("SIGKILL");
120
+ this.cleanup();
121
+ return;
122
+ }
84
123
  // Process complete JSON messages (newline-delimited)
85
124
  const lines = this.messageBuffer.split("\n");
86
125
  this.messageBuffer = lines.pop() ?? "";
87
126
  for (const line of lines) {
88
127
  if (line.trim()) {
128
+ if (Buffer.byteLength(line, "utf8") > maxMessageBytes) {
129
+ logger.error({
130
+ src: "interop:python-bridge",
131
+ event: "interop.ipc.message_exceeded",
132
+ moduleName: this.options.moduleName,
133
+ }, `IPC message exceeded limit (${maxMessageBytes} bytes); terminating bridge`);
134
+ this.process?.kill("SIGKILL");
135
+ this.cleanup();
136
+ return;
137
+ }
89
138
  try {
90
139
  const message = JSON.parse(line);
91
140
  this.handleMessage(message);
92
141
  }
93
- catch (error) {
94
- console.error("Failed to parse IPC message:", line, error);
142
+ catch (_error) {
143
+ logger.error({
144
+ src: "interop:python-bridge",
145
+ event: "interop.ipc.parse_failed",
146
+ moduleName: this.options.moduleName,
147
+ }, `Failed to parse IPC message: ${line}`);
95
148
  }
96
149
  }
97
150
  }
@@ -123,6 +176,10 @@ export class PythonPluginBridge extends EventEmitter {
123
176
  if (!this.process || !this.initialized) {
124
177
  throw new Error("Python bridge not started");
125
178
  }
179
+ const maxPendingRequests = this.options.maxPendingRequests ?? 1000;
180
+ if (this.pendingRequests.size >= maxPendingRequests) {
181
+ throw new Error(`Too many pending IPC requests (max=${maxPendingRequests})`);
182
+ }
126
183
  const id = `req_${++this.requestCounter}`;
127
184
  const requestWithId = { ...request, id };
128
185
  return new Promise((resolve, reject) => {
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../typescript/types.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,10 @@ export interface WasmLoaderOptions {
16
16
  manifestPath?: string;
17
17
  /** Import object for WASM instantiation */
18
18
  imports?: WebAssembly.Imports;
19
+ /** Maximum allowed WASM binary size in bytes. */
20
+ maxWasmBytes?: number;
21
+ /** Maximum allowed initial memory size in bytes (post-instantiation). */
22
+ maxMemoryBytes?: number;
19
23
  }
20
24
  /**
21
25
  * Load a WASM plugin and return an elizaOS Plugin interface
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wasm-loader.d.ts","sourceRoot":"","sources":["../typescript/wasm-loader.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EASV,MAAM,EAKP,MAAM,eAAe,CAAC;AAGvB,OAAO,KAAK,EAEV,cAAc,EAIf,MAAM,SAAS,CAAC;AAEjB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,WAAW,CAAC,OAAO,CAAC;IAC9B,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAQD;;GAEG;AACH,wBAAsB,cAAc,CAClC,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,MAAM,CAAC,CAkBjB;AAmZD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;IAClE,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC,CAkBD"}
@@ -4,6 +4,7 @@
4
4
  * Loads Rust (or other) plugins compiled to WebAssembly and adapts them
5
5
  * to the TypeScript Plugin interface.
6
6
  */
7
+ import { logger } from "@elizaos/core";
7
8
  /**
8
9
  * Text encoder/decoder for string passing
9
10
  */
@@ -15,7 +16,7 @@ const decoder = new TextDecoder();
15
16
  export async function loadWasmPlugin(options) {
16
17
  const { wasmPath, manifestPath } = options;
17
18
  // Load the WASM module
18
- const wasmInstance = await loadWasmModule(wasmPath, options.imports);
19
+ const wasmInstance = await loadWasmModule(wasmPath, options);
19
20
  // Get the manifest from the WASM module or external file
20
21
  let manifest;
21
22
  if (manifestPath) {
@@ -32,7 +33,7 @@ export async function loadWasmPlugin(options) {
32
33
  /**
33
34
  * Load a WASM module and instantiate it
34
35
  */
35
- async function loadWasmModule(wasmPath, customImports) {
36
+ async function loadWasmModule(wasmPath, options) {
36
37
  // Default imports for WASM
37
38
  const defaultImports = {
38
39
  env: {
@@ -66,7 +67,7 @@ async function loadWasmModule(wasmPath, customImports) {
66
67
  },
67
68
  },
68
69
  };
69
- const imports = { ...defaultImports, ...customImports };
70
+ const imports = { ...defaultImports, ...options.imports };
70
71
  // Determine if we're in Node.js or browser
71
72
  const isNode = typeof globalThis.process !== "undefined" &&
72
73
  globalThis.process.versions &&
@@ -75,32 +76,76 @@ async function loadWasmModule(wasmPath, customImports) {
75
76
  if (isNode) {
76
77
  // Node.js: read file
77
78
  const fs = await import("node:fs/promises");
79
+ if (typeof options.maxWasmBytes === "number") {
80
+ const st = await fs.stat(wasmPath);
81
+ if (st.size > options.maxWasmBytes) {
82
+ throw new Error(`WASM binary too large (${st.size} bytes > ${options.maxWasmBytes} bytes)`);
83
+ }
84
+ }
78
85
  const wasmBuffer = await fs.readFile(wasmPath);
86
+ if (typeof options.maxWasmBytes === "number" &&
87
+ wasmBuffer.byteLength > options.maxWasmBytes) {
88
+ throw new Error(`WASM binary too large (${wasmBuffer.byteLength} bytes > ${options.maxWasmBytes} bytes)`);
89
+ }
79
90
  wasmModule = await WebAssembly.compile(wasmBuffer);
80
91
  }
81
92
  else {
82
93
  // Browser: fetch
83
94
  const response = await fetch(wasmPath);
84
95
  const wasmBuffer = await response.arrayBuffer();
96
+ if (typeof options.maxWasmBytes === "number" &&
97
+ wasmBuffer.byteLength > options.maxWasmBytes) {
98
+ throw new Error(`WASM binary too large (${wasmBuffer.byteLength} bytes > ${options.maxWasmBytes} bytes)`);
99
+ }
85
100
  wasmModule = await WebAssembly.compile(wasmBuffer);
86
101
  }
87
- let instance = await WebAssembly.instantiate(wasmModule, defaultImports);
88
- instance = await WebAssembly.instantiate(wasmModule, imports);
102
+ const instance = await WebAssembly.instantiate(wasmModule, imports);
89
103
  // Set up console logging now that we have memory
90
104
  const memory = instance.exports.memory;
105
+ if (typeof options.maxMemoryBytes === "number" &&
106
+ memory.buffer.byteLength > options.maxMemoryBytes) {
107
+ throw new Error(`WASM memory too large (${memory.buffer.byteLength} bytes > ${options.maxMemoryBytes} bytes)`);
108
+ }
91
109
  if (imports.env) {
92
110
  const env = imports.env;
93
111
  env.console_log = (ptr, len) => {
94
112
  const bytes = new Uint8Array(memory.buffer, ptr, len);
95
- console.log(decoder.decode(bytes));
113
+ logger.info({ src: "interop:wasm", event: "interop.wasm.stdout", stream: "stdout" }, decoder.decode(bytes));
96
114
  };
97
115
  env.console_error = (ptr, len) => {
98
116
  const bytes = new Uint8Array(memory.buffer, ptr, len);
99
- console.error(decoder.decode(bytes));
117
+ logger.error({ src: "interop:wasm", event: "interop.wasm.stderr", stream: "stderr" }, decoder.decode(bytes));
100
118
  };
101
119
  }
120
+ // Provide secure randomness for WASI random_get if present
121
+ if (imports.wasi_snapshot_preview1) {
122
+ const wasi = imports.wasi_snapshot_preview1;
123
+ wasi.random_get = (buf, len) => {
124
+ const view = new Uint8Array(memory.buffer, buf, len);
125
+ const cryptoObj = globalThis.crypto;
126
+ if (!cryptoObj || typeof cryptoObj.getRandomValues !== "function") {
127
+ throw new Error("No secure random source available for WASI random_get");
128
+ }
129
+ cryptoObj.getRandomValues(view);
130
+ return 0;
131
+ };
132
+ }
133
+ // Type guard to validate exports match WasmPluginExports interface
134
+ const exports = instance.exports;
135
+ const typedExports = {
136
+ get_manifest: exports.get_manifest,
137
+ init: exports.init,
138
+ validate_action: exports.validate_action,
139
+ invoke_action: exports.invoke_action,
140
+ get_provider: exports.get_provider,
141
+ validate_evaluator: exports.validate_evaluator,
142
+ invoke_evaluator: exports.invoke_evaluator,
143
+ handle_route: exports.handle_route,
144
+ alloc: exports.alloc,
145
+ dealloc: exports.dealloc,
146
+ };
102
147
  return {
103
- exports: instance.exports,
148
+ exports: typedExports,
104
149
  memory: { buffer: memory.buffer },
105
150
  };
106
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elizaos/interop",
3
- "version": "2.0.0-alpha.2",
3
+ "version": "2.0.0-alpha.201",
4
4
  "description": "Cross-language plugin interoperability for elizaOS",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -22,22 +22,22 @@
22
22
  "build": "tsc",
23
23
  "test": "vitest run --pool=threads",
24
24
  "test:watch": "vitest --pool=threads",
25
- "test:python": "cd python && pytest tests/ -v -p no:anchorpy --asyncio-mode=auto || echo 'Python tests skipped'",
26
- "lint": "bunx @biomejs/biome check --write --unsafe .",
25
+ "test:python": "cd python && tmpdir=$(mktemp -d) && python3 -m venv \"$tmpdir\" && \"$tmpdir/bin/python\" -m pip install -e ../python -e '.[dev]' -q && \"$tmpdir/bin/python\" -m pytest tests/ -v -p no:anchorpy --asyncio-mode=auto; status=$?; rm -rf \"$tmpdir\"; exit $status",
26
+ "lint": "bunx @biomejs/biome check --write .",
27
27
  "lint:check": "bunx @biomejs/biome check .",
28
28
  "typecheck": "tsc --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@elizaos/core": "2.0.0-alpha.2"
31
+ "@elizaos/core": "2.0.0-alpha.201"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^25.0.3",
35
- "typescript": "^5.9.3"
35
+ "typescript": "^6.0.0"
36
36
  },
37
37
  "author": "elizaOS",
38
38
  "license": "MIT",
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "bc6cac8d36845d7cbde51a64307c6a57c16378ad"
42
+ "gitHead": "8be84790e151372fc31f4f2ed5640613511bbe6b"
43
43
  }
@@ -51,7 +51,7 @@ async def test_load_rust_plugin_produces_awaitable_handlers(monkeypatch: pytest.
51
51
  provider = plugin.providers[0]
52
52
  evaluator = plugin.evaluators[0]
53
53
 
54
- valid = await action.validate_fn(None, {"content": {}}, None) # type: ignore[arg-type]
54
+ valid = await action.validate(None, {"content": {}}, None) # type: ignore[arg-type]
55
55
  assert valid is True
56
56
 
57
57
  result = await action.handler(None, {"content": {}}, None, None, None, None) # type: ignore[arg-type]
@@ -62,7 +62,7 @@ async def test_load_rust_plugin_produces_awaitable_handlers(monkeypatch: pytest.
62
62
  prov_result = await provider.get(None, {"content": {}}, {"values": {}, "data": {}, "text": ""}) # type: ignore[arg-type]
63
63
  assert prov_result.text == "prov:PROV"
64
64
 
65
- eval_valid = await evaluator.validate_fn(None, {"content": {}}, None) # type: ignore[arg-type]
65
+ eval_valid = await evaluator.validate(None, {"content": {}}, None) # type: ignore[arg-type]
66
66
  assert eval_valid is True
67
67
 
68
68
  eval_result = await evaluator.handler(None, {"content": {}}, None, None, None, None) # type: ignore[arg-type]