@celestia-island/plana-types 0.1.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 (96) hide show
  1. package/Cargo.toml +38 -0
  2. package/bindings/FileAnchor.ts +3 -0
  3. package/bindings/engine.ts +277 -0
  4. package/bindings/enums.ts +31 -0
  5. package/bindings/httpTypes.ts +197 -0
  6. package/bindings/index.ts +47 -0
  7. package/bindings/mcp/aporia.ts +61 -0
  8. package/bindings/mcp/eleos.ts +18 -0
  9. package/bindings/mcp/epieikeia.ts +48 -0
  10. package/bindings/mcp/haplotes.ts +47 -0
  11. package/bindings/mcp/hubris.ts +41 -0
  12. package/bindings/mcp/index.ts +13 -0
  13. package/bindings/mcp/kalos.ts +47 -0
  14. package/bindings/mcp/neikos.ts +76 -0
  15. package/bindings/mcp/orexis.ts +64 -0
  16. package/bindings/mcp/philia.ts +57 -0
  17. package/bindings/mcp/polemos.ts +55 -0
  18. package/bindings/mcp/skemma.ts +58 -0
  19. package/bindings/mcp/skopeo.ts +56 -0
  20. package/bindings/mcp/webAutomation.ts +31 -0
  21. package/bindings/model.ts +240 -0
  22. package/bindings/package.json +16 -0
  23. package/bindings/region.ts +3 -0
  24. package/bindings/serde_json/JsonValue.ts +3 -0
  25. package/bindings/ws/agentLifecycle.ts +41 -0
  26. package/bindings/ws/auth.ts +15 -0
  27. package/bindings/ws/baseMessages.ts +7 -0
  28. package/bindings/ws/bridgeNetwork.ts +71 -0
  29. package/bindings/ws/core.ts +51 -0
  30. package/bindings/ws/fileBrowsing.ts +57 -0
  31. package/bindings/ws/handshake.ts +23 -0
  32. package/bindings/ws/industrial.ts +72 -0
  33. package/bindings/ws/knowledgeBase.ts +10 -0
  34. package/bindings/ws/layer2.ts +25 -0
  35. package/bindings/ws/llmProvider.ts +74 -0
  36. package/bindings/ws/logs.ts +11 -0
  37. package/bindings/ws/malkuth.ts +62 -0
  38. package/bindings/ws/noa.ts +17 -0
  39. package/bindings/ws/stateSync.ts +19 -0
  40. package/bindings/ws/systemUi.ts +5 -0
  41. package/bindings/ws/tasks.ts +6 -0
  42. package/bindings/ws/views.ts +163 -0
  43. package/bindings/ws/workspace.ts +11 -0
  44. package/bindings/ws/yolo.ts +32 -0
  45. package/examples/schema_dump.rs +51 -0
  46. package/package.json +6 -0
  47. package/pnpm-workspace.yaml +2 -0
  48. package/src/engine.rs +602 -0
  49. package/src/enums.rs +334 -0
  50. package/src/external_mcp.rs +132 -0
  51. package/src/http.rs +1077 -0
  52. package/src/identity.rs +160 -0
  53. package/src/lib.rs +1215 -0
  54. package/src/malkuth.rs +145 -0
  55. package/src/mcp/aporia.rs +272 -0
  56. package/src/mcp/eleos.rs +210 -0
  57. package/src/mcp/epieikeia.rs +194 -0
  58. package/src/mcp/haplotes.rs +310 -0
  59. package/src/mcp/hubris.rs +377 -0
  60. package/src/mcp/kalos.rs +251 -0
  61. package/src/mcp/mod.rs +23 -0
  62. package/src/mcp/neikos.rs +533 -0
  63. package/src/mcp/orexis.rs +493 -0
  64. package/src/mcp/philia.rs +267 -0
  65. package/src/mcp/polemos.rs +241 -0
  66. package/src/mcp/skemma.rs +442 -0
  67. package/src/mcp/skopeo.rs +282 -0
  68. package/src/mcp/web_automation.rs +122 -0
  69. package/src/model.rs +421 -0
  70. package/src/protocol/base_messages.rs +125 -0
  71. package/src/protocol/handshake.rs +364 -0
  72. package/src/protocol/jsonrpc.rs +888 -0
  73. package/src/protocol/mod.rs +10 -0
  74. package/src/rbac.rs +786 -0
  75. package/src/region.rs +362 -0
  76. package/src/tracing_helpers.rs +9 -0
  77. package/src/ws/agent/agent_lifecycle.rs +221 -0
  78. package/src/ws/agent/layer2.rs +126 -0
  79. package/src/ws/agent/mod.rs +9 -0
  80. package/src/ws/agent/state_sync.rs +111 -0
  81. package/src/ws/agent/tasks.rs +43 -0
  82. package/src/ws/agent/yolo.rs +161 -0
  83. package/src/ws/mod.rs +10 -0
  84. package/src/ws/services/auth.rs +99 -0
  85. package/src/ws/services/industrial.rs +647 -0
  86. package/src/ws/services/knowledge_base.rs +59 -0
  87. package/src/ws/services/llm_provider.rs +371 -0
  88. package/src/ws/services/mod.rs +7 -0
  89. package/src/ws/ui/bridge_network.rs +96 -0
  90. package/src/ws/ui/file_browsing.rs +88 -0
  91. package/src/ws/ui/logs.rs +55 -0
  92. package/src/ws/ui/mod.rs +11 -0
  93. package/src/ws/ui/noa.rs +105 -0
  94. package/src/ws/ui/system_ui.rs +27 -0
  95. package/src/ws/ui/views.rs +159 -0
  96. package/src/ws/ui/workspace.rs +73 -0
package/src/engine.rs ADDED
@@ -0,0 +1,602 @@
1
+ //! Celestia Engine Protocol (CEP) — unified model-runtime access protocol.
2
+ //!
3
+ //! Any model-serving engine — llama.cpp, vLLM, a speech/sensor model, or a
4
+ //! custom engine written in any language — speaks this protocol over a
5
+ //! WebSocket transport using JSON-RPC 2.0 envelopes (see
6
+ //! [`crate::protocol::jsonrpc`]) to join the arona gateway's Router cluster.
7
+ //! The gateway acts as the intermediary: clients never talk to engines
8
+ //! directly, only to the gateway, and the gateway multiplexes requests
9
+ //! across engines via least-count routing, session affinity and
10
+ //! capacity-aware placement.
11
+ //!
12
+ //! ## The protocol is capability-driven, not input/output-locked
13
+ //!
14
+ //! CEP does NOT assume a text LLM. Engines declare what they actually
15
+ //! consume and produce at handshake time (`modalities`, `content_types`),
16
+ //! and request payloads are free-form JSON ([`EngineInvokeParams`]) or
17
+ //! content-part messages ([`EngineMessage`]) that can carry text, base64
18
+ //! binary (audio/image/video), structured sensor readings or arbitrary
19
+ //! data. The convenience methods `Engine.Chat` / `Engine.Embeddings` are
20
+ //! merely the two most common shapes; engines with specialised I/O
21
+ //! (audio generation, sensor-signal processing, tensor streaming…) use
22
+ //! `Engine.Invoke` with engine-defined method names and payloads. The
23
+ //! gateway routes by declared capability and passes unknown payloads
24
+ //! through untouched.
25
+ //!
26
+ //! ## Wire methods (JSON-RPC method names)
27
+ //!
28
+ //! | Method | Direction | Purpose |
29
+ //! |--------|-----------|---------|
30
+ //! | `Engine.Handshake` | engine → gateway (first) | Identity + capability declaration |
31
+ //! | `Engine.Chat` | gateway → engine | Convenience: non-streaming text chat |
32
+ //! | `Engine.ChatStart` | gateway → engine | Convenience: streaming text chat |
33
+ //! | `Engine.ChatChunk` | engine → gateway (notification) | Streamed text token delta |
34
+ //! | `Engine.Embeddings` | gateway → engine | Convenience: batch text embeddings |
35
+ //! | `Engine.Invoke` | gateway → engine | Generic method: any engine-defined operation |
36
+ //! | `Engine.InvokeStart` | gateway → engine | Generic streaming invocation |
37
+ //! | `Engine.StreamChunk` | engine → gateway (notification) | Generic streamed data block (any mime) |
38
+ //! | `Engine.Models` | gateway → engine | List serving models |
39
+ //! | `Engine.Stats` | gateway → engine | Telemetry (GPU utilisation etc.) |
40
+ //! | `Engine.Shutdown` | gateway → engine | Graceful stop (deploy stop path) |
41
+ //!
42
+ //! ## Streaming
43
+ //!
44
+ //! Text streams use the convenience `Engine.ChatStart` / `Engine.ChatChunk`
45
+ //! pair (gateway-generated `stream_id` correlation). Generic streams —
46
+ //! audio frames, sensor samples, tensors — use `Engine.InvokeStart`
47
+ //! (same `stream_id` accept/notify shape) with [`EngineStreamChunk`]
48
+ //! carrying a mime + encoding description so the consumer can decode
49
+ //! without prior agreement. Large binary blocks may additionally travel
50
+ //! as WebSocket binary frames: a JSON notification announces the chunk
51
+ //! (`encoding: "binary-frame"`), and the immediately following WS binary
52
+ //! frame carries the bytes.
53
+ //!
54
+ //! ## Handshake
55
+ //!
56
+ //! The engine MUST send `Engine.Handshake` as its first message. The
57
+ //! gateway replies with [`EngineHandshakeResult`]; a rejected handshake
58
+ //! closes the connection with the given error.
59
+ //!
60
+ //! ## Binary transfer (JSON-RPC cannot carry binary — this is the escape
61
+ //! hatch, and every byte is labelled with a MIME type)
62
+ //!
63
+ //! JSON-RPC 2.0 defines messages as JSON objects; there is no binary frame
64
+ //! in the spec. WebSocket, however, has first-class binary frames that may
65
+ //! interleave with text frames on one connection. CEP uses that:
66
+ //!
67
+ //! 1. **Announce** — the sender emits a JSON-RPC *notification*
68
+ //! `Engine.BinaryStart` declaring `transfer_id`, the MIME type of the
69
+ //! payload, `total_bytes`, the expected binary-frame count and an
70
+ //! optional SHA-256 hex checksum. The receiver switches to
71
+ //! "binary-receive" state for this transfer id.
72
+ //! 2. **Payload** — the bytes travel as a sequence of raw WebSocket
73
+ //! **binary frames** (no JSON envelope). Senders MUST split large
74
+ //! payloads into frames of at most [`ENGINE_BINARY_MAX_FRAME_BYTES`]
75
+ //! so interop holds across implementations (browsers, embedded
76
+ //! engines). The receiver concatenates frames in arrival order.
77
+ //! 3. **Finish** — after the last frame the sender emits the JSON-RPC
78
+ //! *notification* `Engine.BinaryEnd` with the received byte count and
79
+ //! checksum status. The receiver validates and returns to normal RPC
80
+ //! operation. A single connection carries at most one active binary
81
+ //! transfer at a time (frames are ordered per-connection; concurrent
82
+ //! transfers would be ambiguous — use separate connections or the
83
+ //! stream channel for concurrency).
84
+ //! 4. **Abort** — either side may emit `Engine.BinaryAbort` to cancel;
85
+ //! the receiver discards buffered bytes and returns to normal RPC.
86
+ //!
87
+ //! ### Resynchronisation & failure handling (how the receiver always finds
88
+ //! the next JSON message)
89
+ //!
90
+ //! WebSocket frames carry a type (Text vs Binary) and TCP underneath makes
91
+ //! frames ordered and lossless within a connection — the receiver's state
92
+ //! machine therefore never scans bytes for JSON boundaries:
93
+ //!
94
+ //! - **Normal state** accepts only Text frames; a Binary frame without an
95
+ //! active transfer is a protocol violation → the connection is closed.
96
+ //! - **Binary-receive state** accepts only Binary frames; a Text frame that
97
+ //! is not `Engine.BinaryEnd`/`Engine.BinaryAbort` means the sender
98
+ //! abandoned the transfer → buffered bytes are discarded and the Text
99
+ //! frame is processed normally (the state machine simply resets).
100
+ //! - **Timeouts** ([`ENGINE_BINARY_RECEIVE_TIMEOUT_SECS`]) guard against a
101
+ //! sender that dies mid-transfer: if the first frame after
102
+ //! `Engine.BinaryStart`, or any inter-frame gap, exceeds the timeout the
103
+ //! receiver aborts the transfer (locally and/or via `Engine.BinaryAbort`)
104
+ //! and returns to normal RPC.
105
+ //! - **Corruption** (byte-count or checksum mismatch at
106
+ //! `Engine.BinaryEnd`) invalidates the whole transfer — binary payloads
107
+ //! have no internal boundaries, so partial data can never be trusted.
108
+ //! - **Hard failure** (connection close mid-transfer) resets everything:
109
+ //! reconnect + re-handshake, then re-run the transfer.
110
+ //!
111
+ //! No sliding window is needed: frames are ordered and lossless, so there
112
+ //! is nothing to reorder or retransmit inside a connection.
113
+ //!
114
+ //! Every transfer is labelled with a MIME type ([`EngineBinaryStartParams`])
115
+ //! — the same MIME vocabulary used by content parts and stream chunks — so
116
+ //! consumers never guess what the bytes are.
117
+ //!
118
+ //! ### Why not base64-only?
119
+ //!
120
+ //! Base64 works everywhere but costs +33% bandwidth and CPU. The
121
+ //! announce/payload/finish triple keeps the JSON-RPC control plane intact
122
+ //! (handshake, correlation, errors all stay JSON) while the bulk bytes
123
+ //! avoid the encoding tax. Engines that prefer simplicity may still send
124
+ //! base64 content parts — both paths are first-class.
125
+
126
+ use serde::{Deserialize, Serialize};
127
+ use ts_rs::TS;
128
+
129
+ /// CEP wire-protocol version. Bumped on incompatible payload changes.
130
+ pub const ENGINE_PROTOCOL_VERSION: u32 = 3;
131
+
132
+ /// Upper bound for a single binary-transfer frame, in bytes. Senders MUST
133
+ /// split larger payloads; receivers SHOULD refuse oversized frames.
134
+ pub const ENGINE_BINARY_MAX_FRAME_BYTES: usize = 256 * 1024;
135
+
136
+ /// Timeout guard for an in-flight binary transfer, in seconds. The
137
+ /// receiver aborts the transfer if the first frame after `Engine.BinaryStart`
138
+ /// or any inter-frame gap exceeds this bound (sender died mid-transfer).
139
+ /// A transfer this size or smaller should normally complete in well under
140
+ /// this budget.
141
+ pub const ENGINE_BINARY_RECEIVE_TIMEOUT_SECS: u64 = 60;
142
+
143
+ // ═══════════════════════════════════════════════════════════
144
+ // Handshake / identity
145
+ // ═══════════════════════════════════════════════════════════
146
+
147
+ /// `Engine.Handshake` params — the engine's first message on connect.
148
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
149
+ #[ts(export, export_to = "engine.ts")]
150
+ pub struct EngineHandshakeParams {
151
+ /// Optional shared token; the gateway rejects mismatches.
152
+ #[serde(default, skip_serializing_if = "Option::is_none")]
153
+ #[ts(optional)]
154
+ pub token: Option<String>,
155
+ pub engine: EngineIdentity,
156
+ pub capabilities: EngineCapabilities,
157
+ }
158
+
159
+ /// Engine implementation identity (any language is fine — this is the
160
+ /// interchange contract).
161
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
162
+ #[ts(export, export_to = "engine.ts")]
163
+ pub struct EngineIdentity {
164
+ pub name: String,
165
+ pub version: String,
166
+ /// Implementation language, e.g. "rust", "cpp".
167
+ #[serde(default)]
168
+ #[ts(optional)]
169
+ pub language: Option<String>,
170
+ /// Optional vendor URL.
171
+ #[serde(default)]
172
+ #[ts(optional)]
173
+ pub vendor: Option<String>,
174
+ }
175
+
176
+ /// Input/output modalities an engine can handle. The gateway does NOT
177
+ /// assume text — it routes and passes payloads through based on this
178
+ /// declaration.
179
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
180
+ #[ts(export, export_to = "engine.ts")]
181
+ pub enum EngineModality {
182
+ Text,
183
+ Audio,
184
+ Image,
185
+ Video,
186
+ Sensor,
187
+ Tensor,
188
+ Generic,
189
+ }
190
+
191
+ /// Static capability declaration supplied at handshake time.
192
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
193
+ #[ts(export, export_to = "engine.ts")]
194
+ pub struct EngineCapabilities {
195
+ #[serde(default = "default_true")]
196
+ pub streaming: bool,
197
+ #[serde(default)]
198
+ pub embeddings: bool,
199
+ #[serde(default = "default_context")]
200
+ pub max_context_length: usize,
201
+ #[serde(default)]
202
+ pub hardware: Vec<EngineGpuInfo>,
203
+ /// Modalities the engine can consume as input (empty = text only).
204
+ #[serde(default)]
205
+ pub input_modalities: Vec<EngineModality>,
206
+ /// Modalities the engine can produce as output.
207
+ #[serde(default)]
208
+ pub output_modalities: Vec<EngineModality>,
209
+ /// MIME content types accepted as input (e.g. "audio/wav",
210
+ /// "application/octet-stream", "application/json").
211
+ #[serde(default)]
212
+ pub content_types: Vec<String>,
213
+ /// Engine-defined `Engine.Invoke` method names beyond the standard
214
+ /// convenience methods (e.g. "audio.generate", "signal.filter").
215
+ /// Any engine-specific operation is reachable via `Engine.Invoke`
216
+ /// even when absent from this list.
217
+ #[serde(default)]
218
+ pub methods: Vec<String>,
219
+ }
220
+
221
+ fn default_true() -> bool {
222
+ true
223
+ }
224
+
225
+ fn default_context() -> usize {
226
+ 128_000
227
+ }
228
+
229
+ /// One GPU the engine can drive — used by capacity-aware placement.
230
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
231
+ #[ts(export, export_to = "engine.ts")]
232
+ pub struct EngineGpuInfo {
233
+ pub name: String,
234
+ #[serde(default)]
235
+ pub vram_gb: u64,
236
+ }
237
+
238
+ /// `Engine.Handshake` result. `ok: false` closes the connection.
239
+ ///
240
+ /// Handshake direction: when the gateway connects to an engine (engine is
241
+ /// the server), the gateway sends `Engine.Handshake` and the engine answers
242
+ /// with this result carrying its **own** declared capabilities, so the
243
+ /// gateway learns modalities/content types before any request. When the
244
+ /// engine connects to the gateway (engine is the client), the engine sends
245
+ /// `Engine.Handshake` with its capabilities in the params and the gateway
246
+ /// answers with `ok` only — the `capabilities` field is then ignored.
247
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
248
+ #[ts(export, export_to = "engine.ts")]
249
+ pub struct EngineHandshakeResult {
250
+ pub ok: bool,
251
+ #[serde(default)]
252
+ #[ts(optional)]
253
+ pub error: Option<String>,
254
+ pub protocol_version: u32,
255
+ /// The engine's own capability declaration (server-mode handshake).
256
+ #[serde(default, skip_serializing_if = "Option::is_none")]
257
+ #[ts(optional)]
258
+ pub capabilities: Option<EngineCapabilities>,
259
+ }
260
+
261
+ // ═══════════════════════════════════════════════════════════
262
+ // Chat
263
+ // ═══════════════════════════════════════════════════════════
264
+
265
+ /// One content unit inside a message. Text is a plain string; everything
266
+ /// else is a data block described by mime + encoding so consumers can
267
+ /// decode without prior agreement:
268
+ /// - `data`: base64 bytes (standard `encoding: "base64"`)
269
+ /// - `encoding: "binary-frame"`: bytes arrive in the immediately following
270
+ /// WebSocket binary frame (JSON notification is the announcer/trailer)
271
+ /// - `encoding: "json"`: `data` is inline JSON (structured sensor readings,
272
+ /// tensors, feature vectors…)
273
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
274
+ #[ts(export, export_to = "engine.ts")]
275
+ pub struct EngineContentPart {
276
+ /// MIME type — "text/plain" for plain text parts.
277
+ pub mime: String,
278
+ /// Encoding of `data` ("base64" | "binary-frame" | "json" | "utf-8").
279
+ pub encoding: String,
280
+ /// Payload: base64 text, inline JSON, or raw text depending on
281
+ /// `encoding`. Empty for binary-frame parts (bytes follow as a WS
282
+ /// binary frame).
283
+ #[serde(default, skip_serializing_if = "Option::is_none")]
284
+ #[ts(optional)]
285
+ pub data: Option<serde_json::Value>,
286
+ /// Optional shape hint for tensor/sensor parts, e.g. [1, 16000]
287
+ /// (channels × samples) or the sensor schema id.
288
+ #[serde(default, skip_serializing_if = "Option::is_none")]
289
+ #[ts(optional)]
290
+ pub shape: Option<Vec<usize>>,
291
+ }
292
+
293
+ impl EngineContentPart {
294
+ pub fn text(content: &str) -> Self {
295
+ Self {
296
+ mime: "text/plain".into(),
297
+ encoding: "utf-8".into(),
298
+ data: Some(serde_json::Value::String(content.to_string())),
299
+ shape: None,
300
+ }
301
+ }
302
+
303
+ pub fn base64(mime: &str, bytes: &str) -> Self {
304
+ Self {
305
+ mime: mime.into(),
306
+ encoding: "base64".into(),
307
+ data: Some(serde_json::Value::String(bytes.to_string())),
308
+ shape: None,
309
+ }
310
+ }
311
+
312
+ pub fn json(mime: &str, value: serde_json::Value) -> Self {
313
+ Self {
314
+ mime: mime.into(),
315
+ encoding: "json".into(),
316
+ data: Some(value),
317
+ shape: None,
318
+ }
319
+ }
320
+
321
+ pub fn binary_frame(mime: &str) -> Self {
322
+ Self {
323
+ mime: mime.into(),
324
+ encoding: "binary-frame".into(),
325
+ data: None,
326
+ shape: None,
327
+ }
328
+ }
329
+ }
330
+
331
+ /// A message in an `Engine.Chat` / `Engine.ChatStart` / `Engine.Invoke`
332
+ /// payload. Content is a list of parts so mixed-modality inputs (text +
333
+ /// audio + sensor…) are representable. `role` is advisory; specialised
334
+ /// engines may ignore it.
335
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
336
+ #[ts(export, export_to = "engine.ts")]
337
+ pub struct EngineMessage {
338
+ pub role: String,
339
+ pub content: Vec<EngineContentPart>,
340
+ }
341
+
342
+ impl EngineMessage {
343
+ pub fn text(role: &str, content: &str) -> Self {
344
+ Self {
345
+ role: role.into(),
346
+ content: vec![EngineContentPart::text(content)],
347
+ }
348
+ }
349
+ }
350
+
351
+ /// `Engine.Chat` (non-streaming) / `Engine.ChatStart` (streaming) params.
352
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
353
+ #[ts(export, export_to = "engine.ts")]
354
+ pub struct EngineChatParams {
355
+ pub model: String,
356
+ pub messages: Vec<EngineMessage>,
357
+ #[serde(default)]
358
+ #[ts(optional)]
359
+ pub temperature: Option<f32>,
360
+ #[serde(default)]
361
+ #[ts(optional)]
362
+ pub max_tokens: Option<u32>,
363
+ /// Present for streaming requests — chunks are tagged with this id.
364
+ #[serde(default)]
365
+ #[ts(optional)]
366
+ pub stream_id: Option<String>,
367
+ /// Free-form passthrough merged into the upstream payload (same
368
+ /// semantics as the gateway's `extra` field).
369
+ #[serde(default)]
370
+ #[ts(optional)]
371
+ pub extra: Option<serde_json::Value>,
372
+ }
373
+
374
+ /// `Engine.Chat` result for a non-streaming completion.
375
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
376
+ #[ts(export, export_to = "engine.ts")]
377
+ pub struct EngineChatResult {
378
+ pub model: String,
379
+ pub content: String,
380
+ #[serde(default)]
381
+ #[ts(optional)]
382
+ pub usage: Option<EngineUsage>,
383
+ }
384
+
385
+ /// `Engine.ChatStart` acceptance result. `ok: false` rejects the stream
386
+ /// before any chunk is sent.
387
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
388
+ #[ts(export, export_to = "engine.ts")]
389
+ pub struct EngineChatStartResult {
390
+ pub ok: bool,
391
+ #[serde(default)]
392
+ #[ts(optional)]
393
+ pub error: Option<String>,
394
+ pub stream_id: String,
395
+ }
396
+
397
+ /// `Engine.ChatChunk` notification — streamed token delta.
398
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
399
+ #[ts(export, export_to = "engine.ts")]
400
+ pub struct EngineChatChunk {
401
+ pub stream_id: String,
402
+ #[serde(default)]
403
+ pub token: String,
404
+ /// Set on the final chunk.
405
+ #[serde(default)]
406
+ pub is_complete: bool,
407
+ #[serde(default)]
408
+ #[ts(optional)]
409
+ pub usage: Option<EngineUsage>,
410
+ }
411
+
412
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
413
+ #[ts(export, export_to = "engine.ts")]
414
+ pub struct EngineUsage {
415
+ #[serde(default)]
416
+ pub prompt_tokens: usize,
417
+ #[serde(default)]
418
+ pub completion_tokens: usize,
419
+ }
420
+
421
+ // ═══════════════════════════════════════════════════════════
422
+ // Generic invocation (capability-driven extension channel)
423
+ // ═══════════════════════════════════════════════════════════
424
+
425
+ /// `Engine.Invoke` / `Engine.InvokeStart` params — the generic extension
426
+ /// channel. `method` is engine-defined (e.g. "audio.generate",
427
+ /// "signal.filter", "train.step"); `params` is any JSON the engine
428
+ /// understands. `messages` is optional and reuses the multimodal content
429
+ /// model for engines that mix free-form payloads with content parts.
430
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
431
+ #[ts(export, export_to = "engine.ts")]
432
+ pub struct EngineInvokeParams {
433
+ pub method: String,
434
+ pub params: serde_json::Value,
435
+ #[serde(default)]
436
+ #[ts(optional)]
437
+ pub messages: Option<Vec<EngineMessage>>,
438
+ /// Present for streaming invocations — chunks are tagged with this id.
439
+ #[serde(default)]
440
+ #[ts(optional)]
441
+ pub stream_id: Option<String>,
442
+ }
443
+
444
+ /// `Engine.Invoke` result — any JSON the engine returns.
445
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
446
+ #[ts(export, export_to = "engine.ts")]
447
+ pub struct EngineInvokeResult {
448
+ pub method: String,
449
+ pub result: serde_json::Value,
450
+ }
451
+
452
+ /// `Engine.InvokeStart` acceptance result (same shape as ChatStart).
453
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
454
+ #[ts(export, export_to = "engine.ts")]
455
+ pub struct EngineInvokeStartResult {
456
+ pub ok: bool,
457
+ #[serde(default)]
458
+ #[ts(optional)]
459
+ pub error: Option<String>,
460
+ pub stream_id: String,
461
+ }
462
+
463
+ /// `Engine.StreamChunk` notification — a generic streamed data block for
464
+ /// any output modality (audio frame, sensor sample batch, tensor slice…).
465
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
466
+ #[ts(export, export_to = "engine.ts")]
467
+ pub struct EngineStreamChunk {
468
+ pub stream_id: String,
469
+ /// MIME type of this block (e.g. "audio/wav", "application/json").
470
+ pub mime: String,
471
+ /// "base64" | "binary-frame" | "json" | "utf-8".
472
+ pub encoding: String,
473
+ #[serde(default, skip_serializing_if = "Option::is_none")]
474
+ #[ts(optional)]
475
+ pub data: Option<serde_json::Value>,
476
+ /// Optional shape hint for tensor/sensor blocks.
477
+ #[serde(default, skip_serializing_if = "Option::is_none")]
478
+ #[ts(optional)]
479
+ pub shape: Option<Vec<usize>>,
480
+ /// Set on the final block.
481
+ #[serde(default)]
482
+ pub is_complete: bool,
483
+ #[serde(default)]
484
+ #[ts(optional)]
485
+ pub usage: Option<EngineUsage>,
486
+ }
487
+
488
+ // ═══════════════════════════════════════════════════════════
489
+ // Embeddings
490
+ // ═══════════════════════════════════════════════════════════
491
+
492
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
493
+ #[ts(export, export_to = "engine.ts")]
494
+ pub struct EngineEmbeddingsParams {
495
+ pub model: String,
496
+ pub input: Vec<String>,
497
+ }
498
+
499
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
500
+ #[ts(export, export_to = "engine.ts")]
501
+ pub struct EngineEmbeddingsResult {
502
+ pub model: String,
503
+ pub embeddings: Vec<Vec<f32>>,
504
+ }
505
+
506
+ // ═══════════════════════════════════════════════════════════
507
+ // Models / stats / shutdown
508
+ // ═══════════════════════════════════════════════════════════
509
+
510
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
511
+ #[ts(export, export_to = "engine.ts")]
512
+ pub struct EngineModelInfo {
513
+ pub id: String,
514
+ #[serde(default)]
515
+ #[ts(optional)]
516
+ pub context_length: Option<usize>,
517
+ #[serde(default)]
518
+ pub embedding: bool,
519
+ }
520
+
521
+ /// `Engine.Models` result.
522
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
523
+ #[ts(export, export_to = "engine.ts")]
524
+ pub struct EngineModelsResult {
525
+ pub models: Vec<EngineModelInfo>,
526
+ }
527
+
528
+ /// `Engine.Stats` result — live telemetry for capacity-aware placement.
529
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
530
+ #[ts(export, export_to = "engine.ts")]
531
+ pub struct EngineStatsResult {
532
+ /// Per-GPU utilisation percentages (0-100), same shape as the agent
533
+ /// control-plane heartbeats.
534
+ #[serde(default)]
535
+ pub gpu_utilization: Vec<u32>,
536
+ #[serde(default)]
537
+ pub uptime_secs: u64,
538
+ /// Model id currently loaded, when the engine pins a single model.
539
+ #[serde(default)]
540
+ #[ts(optional)]
541
+ pub model_loaded: Option<String>,
542
+ }
543
+
544
+ /// `Engine.Shutdown` params — graceful stop requested by the gateway.
545
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
546
+ #[ts(export, export_to = "engine.ts")]
547
+ pub struct EngineShutdownParams {
548
+ #[serde(default, skip_serializing_if = "Option::is_none")]
549
+ #[ts(optional)]
550
+ pub reason: Option<String>,
551
+ }
552
+
553
+ // ═══════════════════════════════════════════════════════════
554
+ // Binary transfer (JSON-RPC control plane + WS binary frames)
555
+ // ═══════════════════════════════════════════════════════════
556
+
557
+ /// `Engine.BinaryStart` notification params — the announce packet sent
558
+ /// BEFORE any binary frame. Every payload is labelled with a MIME type.
559
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
560
+ #[ts(export, export_to = "engine.ts")]
561
+ pub struct EngineBinaryStartParams {
562
+ /// Correlation id shared by announce / frames / finish.
563
+ pub transfer_id: String,
564
+ /// MIME type of the whole payload (e.g. "audio/wav",
565
+ /// "application/octet-stream"). Empty means unspecified binary.
566
+ pub mime: String,
567
+ pub total_bytes: u64,
568
+ /// Expected number of binary frames (for early truncation checks).
569
+ pub chunk_count: u32,
570
+ /// Optional SHA-256 hex digest of the payload.
571
+ #[serde(default, skip_serializing_if = "Option::is_none")]
572
+ #[ts(optional)]
573
+ pub checksum: Option<String>,
574
+ /// Optional association with a stream (generic streaming).
575
+ #[serde(default, skip_serializing_if = "Option::is_none")]
576
+ #[ts(optional)]
577
+ pub stream_id: Option<String>,
578
+ }
579
+
580
+ /// `Engine.BinaryEnd` notification params — sent after the final binary
581
+ /// frame; the receiver validates and returns to normal RPC operation.
582
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
583
+ #[ts(export, export_to = "engine.ts")]
584
+ pub struct EngineBinaryEndParams {
585
+ pub transfer_id: String,
586
+ /// Bytes actually received across all frames.
587
+ pub bytes_received: u64,
588
+ /// Whether the checksum matched (None when no checksum was announced).
589
+ #[serde(default, skip_serializing_if = "Option::is_none")]
590
+ #[ts(optional)]
591
+ pub checksum_ok: Option<bool>,
592
+ }
593
+
594
+ /// `Engine.BinaryAbort` notification params — cancels an in-flight
595
+ /// transfer; the receiver discards buffered bytes and returns to normal
596
+ /// RPC operation.
597
+ #[derive(Debug, Clone, Serialize, Deserialize, TS)]
598
+ #[ts(export, export_to = "engine.ts")]
599
+ pub struct EngineBinaryAbortParams {
600
+ pub transfer_id: String,
601
+ pub reason: String,
602
+ }