The Oda SDK client is the TypeScript package that starts an Oda runtime and drives its turns from your own program, so a script or an internal tool of yours can call DataMind Oda. Start with the Oda SDK overview if you are new to the family; the raw JSON-RPC messages are in SDK wire protocol.
The opposite role, a process that serves SDK clients instead of driving one, is described in Oda SDK: the runtime server.
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, requests wait indefinitely, because a turn can legitimately run long. |
shutdownTimeoutMs | 1000 | How long the shutdown request may take before the client 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 the stop signal. |
env: when you pass it, it replaces the environment the runtime would otherwise inherit, completely. The client reads it at the moment the runtime starts.maxTokens: optional. It caps each request the root agent makes, and subagents 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.env replaces the environment of the runtime instead of adding to it, so list every variable the runtime needs when you use it.
run() with your prompt. The first call also starts the runtime.run() returns when the agent next goes idle, and the result 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, subagents included, in wire order.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, so you can see why it stalled.initialize returned no server identity:, session/ prompt returned no message id:, and turn/ end carried no reason envelope:. The detail follows the colon.close() fails as closed, and a request against a runtime process that is gone fails with an error that says the runtime is not running.Keep the stderr tail and the exit code in your logs: they are what a problem report needs.
close(), or let await using call it for you when the block ends.close() stops the runtime in stages: it sends a shutdown request first, 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 when the process has exited, and it is safe to call again.close() fails with runtime process did not exit within <ms>ms after SIGKILL was accepted|refused or SIGKILL failed.close() the client starts no new runtime, and using it again fails as closed.close(), anything not delivered yet is dropped.The handshake has 10 seconds, a request waits indefinitely unless you set requestTimeoutMs, the shutdown request has 1 second, and the teardown stages have 6 seconds after the input closes and 3 seconds after the stop signal.
Only if your own program needs a bound. Requests wait indefinitely by default, because a turn can legitimately run long. A timeout ends your wait; it does not stop the runtime.
An absolute one. The client resolves the folder before the start-up handshake, and an absolute path keeps that resolution final, so the folder cannot be resolved twice.
Yes, pass maxTokens. It caps each request the root agent makes, and subagents that run in the same process inherit the cap.
Yes. Pass env and the runtime starts with exactly that environment instead of an inherited one, so include every variable the runtime needs.
finalResponse the answer 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.
events and notifications?events are the events of your own session. notifications follow the wider session tree, subagents included, in wire order.
The client cleans up that attempt and prepares a fresh one, so your next call may succeed. 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.
Use 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.
A reader that is open when the runtime dies can still drain what was already delivered, then it fails. After close(), notifications that were not delivered yet are dropped.