Loading the documentation…
DataMind Oda's screens and its extension packages call the host that serves them through one layer: short calls that carry a request and an answer, long-lived streams that carry items in both directions, and events the host forwards to the interface. This page is the reference for that layer, from writing a Remote method in a package to the messages on the wire, so you can author a method, read a trace, debug a connection or build a client of your own. The message-level view is also on The Oda Remote API, and what a build generates for a Remote-capable package is on Plugin artifacts and the catalog. Oda also defines and runs packages in a chat itself, covered by The host half of a dynamic package and The browser half of a dynamic package.
An extension package exposes a method to the other side of the boundary by marking it as Remote and binding its owner to a service; the runtime then carries calls, streams and cancellation between the two sides. Markers are written in TypeScript on the method itself:
| Marker | What it declares |
|---|---|
@Remote | A direct call, under the method's own name. |
@Remote('name') | A direct call, exported under another name; the method keeps its own name on the class. |
@Remote({ mode: 'stream' }) | A streaming call. |
@RemoteScope('context') | A contextual call, whose receiver comes from the named context instead of the bound service, with an optional second name, as in @RemoteScope('context', 'name'). |
Remote decorators require a public instance method with a string name.Remote method "<method>" has conflicting invocation markers; keep the single marker that says how the method is called.{ mode: 'stream' }; anything else fails with Remote options must contain exactly mode: "stream". Use the plain or named marker for a method that does not stream.T | undefined still counts as required; only an optional parameter may be left out of a call.A marked method becomes callable once its owner is bound:
1. Markers read back in declaration order, and a descriptor under another version is refused.ctx. invocation describes the call in progress: the request it belongs to (its namespace, its method and the arguments as received), the service it reached, the peer that made it and the signal. Outside a Remote call the same property reads as undefined, so code shared with other paths can check it first.signal: AbortSignal as the method's final parameter to cooperate with cancellation; the runtime provides the signal, and it never travels as a call argument. A signal declared anywhere else fails with Remote cancellation signal must be the final parameter. React to it to stop your work when the caller goes away.@Remote({ mode: 'stream' }) and returns an Iterable<Out>, an AsyncIterable<Out> or a RemoteStream<Out, In>. Another return shape, a promise included, is refused with stream Remote methods must return Iterable<Out>, AsyncIterable<Out>, or RemoteStream<Out, In>. Each yielded item travels on the logical stream as the method produces it, so a caller sees items while the method still runs.send(item), end() and dispose(): send adds one item to the caller's side of the stream, end half-closes it so no further items follow, and dispose cancels the stream and ends the caller's iteration.send throws. It throws when the item is not a lossless JSON value, when the uplink has already been ended, or when the stream has terminated; nothing is delivered.ctx. invocation. uplink(). It is available once per call, and asking for it a second time throws.The same lossless checks cover clients, gateways and mocks, so a value that cannot round-trip unchanged is refused wherever it would cross: call arguments, results, uplink items and forwarded-event payloads.
null, strings, booleans, finite numbers, plain objects with enumerable fields and dense arrays.undefined fields, big integers, NaN, infinities, -0, cycles, class instances and other non-plain objects, functions, accessor or non-enumerable fields, symbol keys and sparse arrays.undefined crosses as an item with no value; nested values are still checked.Uint8Array) may cross only as the single result of a unary call, directly or nested inside it; parameters, events and stream items stay JSON only, and another use is refused with Remote Uint8Array is only supported in unary results.<namespace>/ <method>; a registered invocation carries the full id <package>#<namespace>/ <method>.<package>#<name>, and one package's contribution per side as <package>#<side>, where the side is host or client. The # marks where the package part ends._, $, . and -. A lone . or . . is refused, and a # is refused inside a name; a name outside the set is refused with a message that ends must contain only RPC endpoint segment characters.{ ok: true, value }, or failure as { ok: false, error }.gateway/ cancelled with the message Remote invocation "<endpoint>" was aborted, not a failure from the method.Streams are logical: they share the one WebSocket connection the client keeps open, each under its own stream id.
On a stream, each side sends a small set of frames:
| From | Frame | What it does |
|---|---|---|
| Client | open | Opens a logical stream, naming the method it calls and its arguments. |
| Client | item | Sends one uplink item; its value may be omitted. |
| Client | end | Half-closes the uplink: no further items follow. |
| Client | cancel | The consumer has left; the stream is torn down. |
| Host | item | Delivers one downlink value. |
| Host | error | Ends the stream with a failure: a code, a message and details. |
| Host | end | Ends the stream normally. |
end fails that stream with gateway/ protocol; the connection stays open for its other streams.open for a stream id that is already open.403, and one it cannot authenticate gets 401.follow and control streams are how a chat stays current in the interface; both obey the reconnect rules above.null when the grant to the account is absent or has changed.gateway/ uplink-overflow, and later items are dropped; uplink items are never replayed.gateway/ input-invalid, and the failure names the uplink field.A connection ends with a close code and, usually, a reason:
| Code | Reason | What it means |
|---|---|---|
1000 | disposed | The client was disposed; the connection is closed for good. |
1001 | peer left | The peer's session ended, and its streams close with it. |
1003 | text messages required | A binary message arrived; the connection carries text only. |
1008 | invalid Remote stream request | A malformed message, an unknown message type, or a duplicate open. |
1011 | Remote stream failure could not be delivered | A stream failed and its failure could not be written, so the connection closed. |
4000 | reconnect requested | The client is making a fresh connection attempt; a new connection follows on its own. |
4002 | an invalid frame | A frame did not parse; every active stream fails and the connection closes. |
RemoteError carrying a stable code of the form <domain>/ <reason>, the endpoint it belongs to and, when one field is at fault, that field's name.gateway/ bad-request when the request does not satisfy the endpoint, and that failure can carry an issues list describing what was wrong; gateway/ cancelled when the call was cancelled; and gateway/ internal for a failure the layer could not classify.code, never on an instance check.gateway/ cancelled for an aborted call, gateway/ uplink-overflow for an overrun uplink, gateway/ protocol for an uplink item after the stream had ended, and gateway/ input-invalid for a value or field that failed validation. A failure the layer cannot classify reports gateway/ internal with empty details.session assistant stream skipped revision <n> and session event stream emitted an entry before its opening cursor.Nothing is replayed. When the client reconnects, each stream reopens on the new connection and re-reads the state it needs; items and notifications from the old connection are never sent again. While the replacement is being established, the interface keeps showing the state it already had. Connection states and recovery covers the recovery itself.
1008?The reason invalid Remote stream request covers a malformed message, an unknown message type, or a second open for a stream id that is already open. Check the frames against the shapes on this page; a duplicate open is the one violation that closes the whole connection.
The value used something the JSON subset does not admit, such as an undefined field, a big integer, NaN, an infinity, negative zero, a cycle, a class instance, or an accessor, hidden or symbol field. Send plain objects, dense arrays and ordinary JSON values instead. The stream the item was sent on fails with gateway/.
No. A logical stream serves exactly one consumer, and opening a second one fails. When several parts of the interface need the same data, the client shares one stream between them instead of opening another.
It is pinged every two seconds by default, and that interval is also the deadline for the answer. A peer that misses two intervals in a row loses the connection. The interval is a deployment setting, so it can be raised where the default is too quick for the machines involved.
262,144 bytes (256 KiB) of frames by default. Over that, the stream fails with gateway/ and later items are dropped. Uplink items are never replayed, so anything that did not get through is lost.
One-way events are not: a notification missed while disconnected is gone. A request that is still waiting for an answer is different; it is sent again to the replacement connection so it can still be answered.
No. A marker needs a public, non-static instance method with a string name and a concrete implementation; private or static methods, methods without an implementation and generic methods are refused with Remote decorators require a public instance method with a string name. Mark a public instance method instead.
Because the signal is injected, not sent: the runtime provides it at call time, and a declaration that does not end with it is refused with Remote cancellation signal must be the final parameter. Declare signal: AbortSignal as the method's last parameter and react to it when the caller goes away.
send() call throw?send throws when the item is not a lossless JSON value, when the uplink has already been ended, or when the stream has terminated. Nothing is delivered when it throws, so fix the value or stop sending to an ended or finished stream.
Because an instance check does not survive a bundle boundary: a failure is recognised structurally, not by its class. Read the stable <domain>/ code and branch on that.
They are different surfaces. This layer connects DataMind Oda's own screens and extensions to the host that serves them. The SDK is for a program of your own: a client process talks to a runtime over standard input and output, with the messages in The Oda SDK wire protocol. Start from the Oda SDK overview, or read Automation with Oda for the serving profiles.