@elizaos/interop 2.0.0-alpha

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +436 -0
  3. package/dist/packages/interop/tsconfig.tsbuildinfo +1 -0
  4. package/dist/tsconfig.tsbuildinfo +1 -0
  5. package/dist/typescript/index.d.ts +33 -0
  6. package/dist/typescript/index.d.ts.map +1 -0
  7. package/dist/typescript/index.js +121 -0
  8. package/dist/typescript/python-bridge.d.ts +80 -0
  9. package/dist/typescript/python-bridge.d.ts.map +1 -0
  10. package/dist/typescript/python-bridge.js +334 -0
  11. package/dist/typescript/types.d.ts +301 -0
  12. package/dist/typescript/types.d.ts.map +1 -0
  13. package/dist/typescript/types.js +10 -0
  14. package/dist/typescript/wasm-loader.d.ts +32 -0
  15. package/dist/typescript/wasm-loader.d.ts.map +1 -0
  16. package/dist/typescript/wasm-loader.js +269 -0
  17. package/package.json +43 -0
  18. package/python/__init__.py +50 -0
  19. package/python/__pycache__/__init__.cpython-313.pyc +0 -0
  20. package/python/__pycache__/rust_ffi.cpython-313.pyc +0 -0
  21. package/python/__pycache__/ts_bridge.cpython-313.pyc +0 -0
  22. package/python/__pycache__/wasm_loader.cpython-313.pyc +0 -0
  23. package/python/bridge_server.py +505 -0
  24. package/python/rust_ffi.py +418 -0
  25. package/python/tests/__init__.py +1 -0
  26. package/python/tests/__pycache__/__init__.cpython-313.pyc +0 -0
  27. package/python/tests/__pycache__/test_bridge_server.cpython-313-pytest-9.0.2.pyc +0 -0
  28. package/python/tests/__pycache__/test_interop.cpython-313-pytest-9.0.2.pyc +0 -0
  29. package/python/tests/__pycache__/test_rust_ffi.cpython-313-pytest-9.0.2.pyc +0 -0
  30. package/python/tests/__pycache__/test_rust_ffi_loader.cpython-313-pytest-9.0.2.pyc +0 -0
  31. package/python/tests/test_bridge_server.py +525 -0
  32. package/python/tests/test_interop.py +319 -0
  33. package/python/tests/test_rust_ffi.py +352 -0
  34. package/python/tests/test_rust_ffi_loader.py +71 -0
  35. package/python/ts_bridge.py +452 -0
  36. package/python/ts_bridge_runner.mjs +284 -0
  37. package/python/wasm_loader.py +517 -0
  38. package/rust/ffi_exports.rs +362 -0
  39. package/rust/mod.rs +21 -0
  40. package/rust/py_loader.rs +412 -0
  41. package/rust/ts_loader.rs +467 -0
  42. package/rust/wasm_plugin.rs +320 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,436 @@
1
+ # elizaOS Cross-Language Plugin Interoperability
2
+
3
+ This module provides seamless interoperability between elizaOS runtimes written in different languages (Rust, TypeScript, Python). **Any runtime can load any plugin**, regardless of the language it was written in.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```
8
+ ┌─────────────────────────────────────┐
9
+ │ Protocol Buffer Schemas │
10
+ │ (Single source of truth for types) │
11
+ └──────────────────┬──────────────────┘
12
+
13
+ ┌──────────────────────────────┼──────────────────────────────┐
14
+ ▼ ▼ ▼
15
+ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
16
+ │ RUST RUNTIME │ │ TS RUNTIME │ │ PYTHON RUNTIME │
17
+ │ │ │ │ │ │
18
+ │ • Native Rust │ │ • Native TS │ │ • Native Python │
19
+ │ • TS via IPC │◄──────────►│ • Rust via WASM │◄──────────►│ • Rust via FFI │
20
+ │ • Python via IPC│ │ • Python via IPC│ │ • TS via IPC │
21
+ └─────────────────┘ └─────────────────┘ └─────────────────┘
22
+ │ │ │
23
+ └──────────────────────────────┴──────────────────────────────┘
24
+
25
+ ┌────────┴────────┐
26
+ ▼ ▼
27
+ ┌─────────────┐ ┌─────────────────┐
28
+ │ PLUGINS │ │ TEST PLUGINS │
29
+ │ │ │ │
30
+ │ • Rust │ │ • eliza-classic │
31
+ │ • TypeScript│ │ • inmemorydb │
32
+ │ • Python │ │ │
33
+ └─────────────┘ └─────────────────┘
34
+ ```
35
+
36
+ ## Complete Interop Matrix
37
+
38
+ Every combination is supported:
39
+
40
+ | Host Runtime | Plugin Language | Method | Performance | Sandboxed |
41
+ | -------------- | --------------- | ------ | ----------- | --------- |
42
+ | **TypeScript** | Rust | WASM | High | ✅ Yes |
43
+ | **TypeScript** | Python | IPC | Medium | ✅ Yes |
44
+ | **TypeScript** | TypeScript | Direct | Native | ❌ No |
45
+ | **Python** | Rust | FFI | Native | ❌ No |
46
+ | **Python** | Rust | WASM | High | ✅ Yes |
47
+ | **Python** | TypeScript | IPC | Medium | ✅ Yes |
48
+ | **Python** | Python | Direct | Native | ❌ No |
49
+ | **Rust** | TypeScript | IPC | Medium | ✅ Yes |
50
+ | **Rust** | Python | IPC | Medium | ✅ Yes |
51
+ | **Rust** | Rust | Direct | Native | ❌ No |
52
+
53
+ ## Interop Methods
54
+
55
+ ### 1. **WASM (WebAssembly)** - Rust ↔ TypeScript
56
+
57
+ - Rust plugins compile to WASM via `wasm-bindgen`
58
+ - TypeScript runtime loads WASM modules dynamically
59
+ - High performance, sandboxed execution
60
+
61
+ ### 2. **PyO3/FFI** - Rust ↔ Python
62
+
63
+ - Rust plugins expose Python bindings via PyO3
64
+ - Python can call Rust code directly via FFI
65
+ - Native performance, type-safe
66
+
67
+ ### 3. **IPC (Inter-Process Communication)** - Any ↔ Any
68
+
69
+ - JSON-RPC over Unix sockets or TCP
70
+ - Works for all language combinations
71
+ - Flexible but has serialization overhead
72
+
73
+ ### 4. **subprocess** - TypeScript/Python host ↔ Rust/Python plugin
74
+
75
+ - Spawn plugin as subprocess
76
+ - Communicate via stdin/stdout JSON
77
+ - Simplest to implement, good isolation
78
+
79
+ ## Usage
80
+
81
+ ### Loading a Rust Plugin in TypeScript
82
+
83
+ ```typescript
84
+ import { loadWasmPlugin } from "@elizaos/interop";
85
+
86
+ const plugin = await loadWasmPlugin("./my-rust-plugin.wasm");
87
+ runtime.registerPlugin(plugin);
88
+ ```
89
+
90
+ ### Loading a TypeScript Plugin in Rust
91
+
92
+ ```rust
93
+ use elizaos::interop::WasmPluginLoader;
94
+
95
+ let plugin = WasmPluginLoader::load("./my-ts-plugin.wasm").await?;
96
+ runtime.register_plugin(plugin);
97
+ ```
98
+
99
+ ### Loading a Python Plugin in TypeScript
100
+
101
+ ```typescript
102
+ import { loadPythonPlugin } from "@elizaos/interop";
103
+
104
+ const plugin = await loadPythonPlugin("my_python_plugin");
105
+ runtime.registerPlugin(plugin);
106
+ ```
107
+
108
+ ### Loading a Rust Plugin in Python
109
+
110
+ ```python
111
+ from elizaos.interop import load_rust_plugin
112
+
113
+ plugin = load_rust_plugin("./my_rust_plugin.so")
114
+ await runtime.register_plugin(plugin)
115
+ ```
116
+
117
+ ## Plugin Manifest
118
+
119
+ Every cross-language plugin must include a `plugin.json` manifest:
120
+
121
+ ```json
122
+ {
123
+ "name": "my-plugin",
124
+ "description": "A cross-language plugin",
125
+ "version": "1.0.0",
126
+ "language": "rust",
127
+ "interop": {
128
+ "protocol": "wasm",
129
+ "wasmPath": "./dist/my_plugin.wasm"
130
+ },
131
+ "actions": [
132
+ {
133
+ "name": "MY_ACTION",
134
+ "description": "Does something cool"
135
+ }
136
+ ]
137
+ }
138
+ ```
139
+
140
+ ## Building Cross-Language Plugins
141
+
142
+ ### Rust → WASM (for TypeScript)
143
+
144
+ ```bash
145
+ cd packages/my-rust-plugin
146
+ cargo build --target wasm32-unknown-unknown --release
147
+ wasm-bindgen target/wasm32-unknown-unknown/release/my_plugin.wasm --out-dir dist
148
+ ```
149
+
150
+ ### Rust → Python Extension
151
+
152
+ ```bash
153
+ cd packages/my-rust-plugin
154
+ maturin build --release
155
+ pip install target/wheels/my_plugin-*.whl
156
+ ```
157
+
158
+ ### TypeScript → WASM (experimental)
159
+
160
+ ```bash
161
+ # Using AssemblyScript or similar
162
+ cd packages/my-ts-plugin
163
+ asc src/index.ts -o dist/plugin.wasm
164
+ ```
165
+
166
+ ## Protocol Messages
167
+
168
+ All interop communication uses JSON-serialized messages:
169
+
170
+ ### Action Invocation
171
+
172
+ ```json
173
+ {
174
+ "type": "action.invoke",
175
+ "id": "uuid",
176
+ "action": "MY_ACTION",
177
+ "memory": { ... },
178
+ "state": { ... },
179
+ "options": { ... }
180
+ }
181
+ ```
182
+
183
+ ### Action Response
184
+
185
+ ```json
186
+ {
187
+ "type": "action.result",
188
+ "id": "uuid",
189
+ "result": {
190
+ "success": true,
191
+ "text": "Action completed",
192
+ "data": { ... }
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### Provider Request
198
+
199
+ ```json
200
+ {
201
+ "type": "provider.get",
202
+ "id": "uuid",
203
+ "provider": "MY_PROVIDER",
204
+ "memory": { ... },
205
+ "state": { ... }
206
+ }
207
+ ```
208
+
209
+ ### Provider Response
210
+
211
+ ```json
212
+ {
213
+ "type": "provider.result",
214
+ "id": "uuid",
215
+ "result": {
216
+ "text": "Provider data",
217
+ "values": { ... },
218
+ "data": { ... }
219
+ }
220
+ }
221
+ ```
222
+
223
+ ## Quick Start Examples
224
+
225
+ ### TypeScript Loading Any Plugin
226
+
227
+ ```typescript
228
+ import { loadPlugin, loadWasmPlugin, loadPythonPlugin } from "@elizaos/interop";
229
+
230
+ // Universal loader (auto-detects from manifest)
231
+ const plugin = await loadPlugin("./path/to/plugin");
232
+
233
+ // Or be explicit:
234
+ const rustPlugin = await loadWasmPlugin("./rust-plugin.wasm");
235
+ const pythonPlugin = await loadPythonPlugin("./python-plugin");
236
+
237
+ // Use like any native plugin
238
+ runtime.registerPlugin(plugin);
239
+ ```
240
+
241
+ ### Python Loading Any Plugin
242
+
243
+ ```python
244
+ from elizaos.interop import load_plugin, load_rust_plugin, load_ts_plugin, load_wasm_plugin
245
+
246
+ # Universal loader
247
+ plugin = load_plugin('./path/to/plugin')
248
+
249
+ # Or be explicit:
250
+ rust_ffi_plugin = load_rust_plugin('./libplugin.so') # FFI
251
+ rust_wasm_plugin = load_wasm_plugin('./plugin.wasm') # WASM
252
+ ts_plugin = load_ts_plugin('./typescript-plugin') # IPC
253
+
254
+ # Use like any native plugin
255
+ await runtime.register_plugin(plugin)
256
+ ```
257
+
258
+ ### Rust Loading Any Plugin
259
+
260
+ ```rust
261
+ use elizaos::interop::{load_plugin, TypeScriptPluginLoader, PythonPluginLoader};
262
+
263
+ // Via IPC subprocess
264
+ let ts_plugin = TypeScriptPluginLoader::new().load("./ts-plugin")?;
265
+ let py_plugin = PythonPluginLoader::new().load("./python-plugin")?;
266
+
267
+ // Native Rust - just use directly!
268
+ use my_rust_plugin::plugin;
269
+ runtime.register_plugin(plugin);
270
+ ```
271
+
272
+ ## Test Plugins
273
+
274
+ Two reference plugins demonstrate all interop paths:
275
+
276
+ ### plugin-eliza-classic
277
+
278
+ Classic ELIZA pattern matching chatbot, implemented in all three languages:
279
+
280
+ ```bash
281
+ # Rust (with WASM + FFI + IPC support)
282
+ cd plugins/plugin-eliza-classic/rust
283
+ cargo build --features wasm,ffi,ipc
284
+
285
+ # Python
286
+ cd plugins/plugin-eliza-classic/python
287
+ pip install -e .
288
+
289
+ # TypeScript
290
+ cd plugins/plugin-eliza-classic/typescript
291
+ pnpm build
292
+ ```
293
+
294
+ ### plugin-inmemorydb
295
+
296
+ Ephemeral in-memory database adapter with vector search:
297
+
298
+ ```bash
299
+ # Available in all three languages with identical API
300
+ plugins/plugin-inmemorydb/
301
+ ├── rust/ # Native Rust implementation
302
+ ├── python/ # Native Python implementation
303
+ └── typescript/ # Native TypeScript implementation
304
+ ```
305
+
306
+ ## Building Plugins for Interop
307
+
308
+ ### Rust Plugin (WASM + FFI + IPC)
309
+
310
+ ```toml
311
+ # Cargo.toml
312
+ [features]
313
+ wasm = ["wasm-bindgen"]
314
+ ffi = []
315
+ ipc = ["tokio"]
316
+
317
+ [lib]
318
+ crate-type = ["cdylib", "rlib"]
319
+ ```
320
+
321
+ ```bash
322
+ # WASM for TypeScript
323
+ cargo build --target wasm32-unknown-unknown --features wasm
324
+
325
+ # Shared lib for Python FFI
326
+ cargo build --release --features ffi
327
+
328
+ # IPC server binary
329
+ cargo build --features ipc --bin my-plugin-ipc
330
+ ```
331
+
332
+ ### Python Plugin (IPC)
333
+
334
+ ```python
335
+ # my_plugin/__init__.py
336
+ from elizaos import Plugin
337
+
338
+ plugin = Plugin(
339
+ name="my-plugin",
340
+ actions=[...],
341
+ providers=[...],
342
+ )
343
+
344
+ # Can be loaded via:
345
+ # python -m elizaos.interop.bridge_server my_plugin
346
+ ```
347
+
348
+ ### TypeScript Plugin (IPC)
349
+
350
+ ```typescript
351
+ // index.ts
352
+ export const plugin: Plugin = {
353
+ name: 'my-plugin',
354
+ actions: [...],
355
+ providers: [...],
356
+ };
357
+
358
+ // Can be loaded via ts-bridge-server
359
+ ```
360
+
361
+ ## File Structure
362
+
363
+ ```
364
+ packages/interop/
365
+ ├── README.md # This file
366
+ ├── examples/ # Complete examples for all interop paths
367
+ │ ├── README.md # Example documentation
368
+ │ ├── ts-loads-all.ts # TypeScript loading all languages
369
+ │ ├── py_loads_all.py # Python loading all languages
370
+ │ └── rust_loads_all.rs # Rust loading all languages
371
+ ├── typescript/ # TypeScript interop implementations
372
+ │ ├── index.ts # Main exports
373
+ │ ├── wasm-loader.ts # Load Rust WASM plugins
374
+ │ ├── python-bridge.ts # IPC bridge to Python plugins
375
+ │ └── types.ts # Interop types
376
+ ├── rust/ # Rust interop implementations
377
+ │ ├── mod.rs # Module exports
378
+ │ ├── wasm_plugin.rs # WASM export traits/macros
379
+ │ ├── ffi_exports.rs # FFI export for Python
380
+ │ ├── ts_loader.rs # Load TypeScript via IPC
381
+ │ └── py_loader.rs # Load Python via IPC
382
+ └── python/ # Python interop implementations
383
+ ├── __init__.py # Package exports
384
+ ├── rust_ffi.py # Load Rust via ctypes FFI
385
+ ├── wasm_loader.py # Load Rust via wasmtime
386
+ ├── ts_bridge.py # Load TypeScript via IPC
387
+ └── bridge_server.py # IPC server for Python plugins
388
+ ```
389
+
390
+ ## Performance Comparison
391
+
392
+ | Method | Latency | Throughput | Memory | Use Case |
393
+ | ---------- | ------- | ---------- | -------- | -------------------------- |
394
+ | **Direct** | ~1ns | Highest | Shared | Same language |
395
+ | **WASM** | ~1μs | High | Isolated | TS↔Rust, sandboxed |
396
+ | **FFI** | ~10ns | Very High | Shared | Python↔Rust, perf-critical |
397
+ | **IPC** | ~1ms | Medium | Isolated | Any↔Any, maximum isolation |
398
+
399
+ **Recommendations:**
400
+
401
+ - Use **Direct** when host and plugin are same language
402
+ - Use **WASM** for TypeScript↔Rust when sandboxing is important
403
+ - Use **FFI** for Python↔Rust when performance is critical
404
+ - Use **IPC** for maximum flexibility and isolation
405
+
406
+ ## Security & SOC2 Readiness Notes
407
+
408
+ This package is often used at a trust boundary (loading plugins). For SOC 2–aligned deployments:
409
+
410
+ ### Sandbox boundaries (be explicit)
411
+
412
+ - **Direct (same-language)**: no sandbox. Plugin code runs with full process privileges.
413
+ - **FFI (Python↔Rust shared library)**: **no sandbox**. This is native code execution in the host process.
414
+ - **IPC (subprocess stdin/stdout)**: isolates memory space, but **does not prevent exfiltration** (plugins can still perform network/file I/O unless the operator constrains the process).
415
+ - **WASM**: provides isolation from host memory, but security depends on the host runtime imports and resource limits. It is not an automatic “secure enclave.”
416
+
417
+ ### Resource limits (recommended defaults)
418
+
419
+ - **TypeScript→Python IPC** (`packages/interop/typescript/python-bridge.ts`)
420
+ - Supports `maxPendingRequests`, `maxMessageBytes`, and `maxBufferBytes` to prevent unbounded memory growth from malformed or hostile plugin output.
421
+ - Supports `inheritEnv` and `envDenylist` to reduce accidental secret exposure to subprocesses.
422
+
423
+ - **WASM loading**
424
+ - TypeScript loader supports `maxWasmBytes` and `maxMemoryBytes` to limit module and initial memory footprint, and provides a secure `random_get` implementation for WASI.
425
+ - Python loader supports `max_module_bytes`, `max_memory_bytes`, and `fuel` (wasmtime fuel) for coarse CPU budgeting.
426
+
427
+ ### Logging
428
+
429
+ Interop subprocess/WASM output is routed through the core logger (when used from the TypeScript runtime) so existing redaction rules apply. Operators should still treat plugin logs as potentially sensitive and configure log retention accordingly.
430
+
431
+ ## See Also
432
+
433
+ - [Examples README](./examples/README.md) - Complete working examples
434
+ - [Protocol Buffers Schemas](../@schemas/README.md) - Type definitions
435
+ - [plugin-eliza-classic](../../plugins/plugin-eliza-classic/) - Reference implementation
436
+ - [plugin-inmemorydb](../../plugins/plugin-inmemorydb/) - Database adapter example