The Oda SDK client is the TypeScript package that starts an Oda runtime for you and drives its turns from your own program. Use it when a script or an internal tool of yours should call DataMind Oda on its own.
The family and its reading order start at Oda SDK overview; the raw messages the client and the runtime exchange are in The Oda SDK wire protocol, and the serving side is in Running an Oda runtime server.
sdk. The profiles Oda ships are listed on Oda profiles and launchers.Timeouts are in milliseconds.
| Option | Default | What it does |
|---|---|---|
initializeTimeoutMs | 10000 | How long the client waits for the start-up handshake. |
requestTimeoutMs | none | With no value, a request waits indefinitely, because a turn can run long. |
shutdownTimeoutMs | 1000 | How long the shutdown request may take before the close moves on. |
disposeEofGraceMs | 6000 | How long the runtime may take to finish after its input closes. |
disposeGraceMs | 3000 | How long it may take to exit after the client sends a stop signal. |
maxTokens, and the runtime checks that exact route before it accepts a prompt: a provider, model or effort it cannot serve fails the handshake instead of falling back to a default.env: when you pass it, it replaces the environment completely for the runtime. The client reads it at the moment the runtime starts.Because env replaces the environment rather than adding to it, list every variable the runtime needs when you use it.
maxTokens: optional. It caps each request the root agent makes, and sub-agents that run in the same process inherit the cap.reasoningEffort: optional. An omitted effort keeps the model's own default.provider and model: the route the client asks for. When you leave them out, the client sends its built-in default route.run() with your prompt. The first call also starts the runtime.run() returns a result that separates your session from the wider tree:
sessionId: the session the run worked in.finalResponse: the last committed assistant text of the session.events: the events of your session.notifications: activity from the wider session tree the run belongs to, sub-agents included. The client tells the two apart along sub-agent lineage.finalResponse is not assigned to the prompt you sent: it is the session's last committed assistant text, and it is empty when there is none.JsonRpcResponseError for an error the other side reported (it keeps the wire code, message and data), RequestTimeoutError for a request that passed its timeout, SdkProtocolError for an answer that did not fit the protocol, and TransportClosedError for using the client after it closed or after the runtime is gone.<method> timed out after <ms>ms waiting for <description>; stderr tail: and carries the newest 400 lines of the runtime's error output.initialize returned no server identity:, session/ prompt returned no message id: and turn/ end carried no reason envelope:. The detail follows the colon.close(), or let await using call it for you when the block ends. close() is safe to call again, and the client starts no new runtime after it: using the client again fails as closed.shutdownTimeoutMs), then closes the runtime's input, then sends a stop signal on systems other than Windows, and finally forces the process to end. It finishes only when the process has exited.runtime process did not exit within <ms>ms after SIGKILL was accepted|refused or SIGKILL failed.close(), notifications that were not delivered yet are dropped.The start-up handshake gets 10 seconds; a request waits indefinitely unless you set requestTimeoutMs; the shutdown request gets 1 second; and the closing stages get 6 seconds after the input closes and 3 seconds after the stop signal.
Yes, set requestTimeoutMs. Leaving it out is the default, because a turn can run long. A timeout ends the wait on your side; it does not stop the runtime.
The client resolves the folder before the start-up handshake. An absolute path keeps that resolution final, so the folder cannot be resolved twice.
By default the client finds the runtime executable itself. Point it at a specific executable when your setup keeps the runtime elsewhere. A runtime of the same version as the client is required either way.
run() start a new runtime each time?No. The runtime starts on the first call and is kept for later runs. Each run works in a session of its own, so session ids differ.
The client waits for a receipt that carries your prompt's message id before it starts collecting events. That id means the prompt was accepted; it does not point to the answer, the end of the turn or the result.
finalResponse the reply to my prompt?Not exactly. It is the last committed assistant text of the session, and it is not assigned to a specific prompt. If the session has not produced an assistant message yet, it is empty.
The client cleans up that attempt and prepares a fresh one, so your next call can try again. If the cleanup fails too, the client reports both failures as one combined error.
So you can see why the runtime stalled or died. The tail holds the newest 400 lines of the runtime's error output, and the exit code shows how the process ended.
close() or a crash?A reader that is open when the runtime dies can still drain what was already delivered, and then it fails. After close(), notifications that were not delivered yet are dropped.
Call close(). It asks the runtime to shut down and escalates in bounded steps until the process has exited, and calling it again is safe. Only a process that will not exit after the force step makes it fail, with a message that names the wait.