Connect your application
Checked against Orca Agent Engine v0.5.1
Your app joins a conversation
A model API answers one request with one response. A session works another way: your application appends to a log and reads from it, and so does the agent. Pick one.
Append an event
Everything you send to a session is an event appended to its log. The call returns as soon as the event is stored, with the id the Registry gave it.
processed_at is null until the runtime picks the event up. Keep the id: it is how you point at this event later.
CLI
ork agent sessions events send message \
--session "$SESSION_ID" \
--text "Which team did you route it to?" \
-o jsonTypeScript
const message = "Which team did you route it to?";
const sent = await orca.sessions.events.send(session.id, {
events: [
{
type: "user.message",
content: [{ type: "text", text: message }],
},
],
});
console.log(sent.data?.map((event) => event.id));Python
message = "Which team did you route it to?"
sent = client.sessions.events.send(
session.id,
events=[
{
"type": "user.message",
"content": [{"type": "text", "text": message}],
}
],
)
print([event.id for event in sent.data or []])Response
{
"data": [
{
"id": "evt_01H8...",
"type": "user.message",
"processed_at": null
}
]
}An example. The field names are the API reference’s.
Read the stream
The log comes back to you as a stream of events, in order. Step through one turn to see what arrives and what an application does with each event.
Open the stream
One request that stays open. Each event arrives as a frame: id is its place in the log, event its type and data its body.
Your own message comes first
From cursor 0 the stream starts at the first event, so it opens with the message you sent.
The turn starts
Status and span events say the agent is working. Show progress; there is nothing to render yet.
The agent uses a tool
Tool calls and their results are events too. The runtime has already run this one; show it or skip it.
The agent replies
The reply arrives whole, as content blocks. This is the event to show your user.
The session goes idle
stop_reason says why. end_turn means the agent is done and waiting for your next message, so stop reading.
Event log
- Nothing yet.
- user.message Triage this ticket: customer cannot log in.
- session.status_running the turn starts
- span.model_request_start the model is called
- agent.tool_use write · outputs/triage.md
- agent.tool_result written
- agent.message Routed to Identity: login is failing.
- span.model_request_end token usage
- session.status_idle stop_reason: end_turn
An illustration. Event types follow the API reference; the values are examples.
Approve a tool call
A tool whose policy is to ask stops the turn. The session goes idle, says it requires action and names the call that is waiting. Your application answers. Choose.
MCP tools ask by default and built-in tools run without asking. Both are a policy on the agent’s tools.
CLI · Allow
# EVENT_ID is in the idle event, under stop_reason.event_ids.
ork agent sessions events send tool-confirmation \
--session "$SESSION_ID" \
--tool-use-id "$EVENT_ID" \
--decision allowTypeScript · Allow
type StopReason = { type: string; event_ids?: string[] };
// Call it from your stream loop, with each session.status_idle.
async function answer(idle: Record<string, unknown>) {
const stop = idle["stop_reason"] as StopReason;
if (stop.type !== "requires_action") return;
await orca.sessions.events.send(session.id, {
events: (stop.event_ids ?? []).map((id) => ({
type: "user.tool_confirmation",
tool_use_id: id,
result: "allow",
})),
});
}Python · Allow
from orca.types import SessionEvent
# Call it from your stream loop, with each session.status_idle.
def answer(idle: SessionEvent) -> None:
stop = idle.model_dump()["stop_reason"]
if stop["type"] != "requires_action":
return
client.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "allow",
}
for event_id in stop.get("event_ids", [])
],
)CLI · Deny
# EVENT_ID is in the idle event, under stop_reason.event_ids.
ork agent sessions events send tool-confirmation \
--session "$SESSION_ID" \
--tool-use-id "$EVENT_ID" \
--decision deny \
--deny-message "Ask the customer for the details."TypeScript · Deny
type StopReason = { type: string; event_ids?: string[] };
// Call it from your stream loop, with each session.status_idle.
async function answer(idle: Record<string, unknown>) {
const stop = idle["stop_reason"] as StopReason;
if (stop.type !== "requires_action") return;
await orca.sessions.events.send(session.id, {
events: (stop.event_ids ?? []).map((id) => ({
type: "user.tool_confirmation",
tool_use_id: id,
result: "deny",
deny_message: "Ask the customer for the details.",
})),
});
}Python · Deny
from orca.types import SessionEvent
# Call it from your stream loop, with each session.status_idle.
def answer(idle: SessionEvent) -> None:
stop = idle.model_dump()["stop_reason"]
if stop["type"] != "requires_action":
return
client.sessions.events.send(
session.id,
events=[
{
"type": "user.tool_confirmation",
"tool_use_id": event_id,
"result": "deny",
"deny_message": "Ask the customer for the details.",
}
for event_id in stop.get("event_ids", [])
],
)Response · Allow
- user.tool_confirmation result: allow
- session.status_running the turn resumes
- agent.mcp_tool_result ticket #4821
- agent.message Routed to Identity: login is failing.
- session.status_idle stop_reason: end_turn
An illustration. Event types follow the API reference; the values are examples.
Response · Deny
- user.tool_confirmation result: deny
- session.status_running the turn resumes
- agent.mcp_tool_result an error, carrying your deny message
- agent.message What does the ticket say? I cannot open it.
- session.status_idle stop_reason: end_turn
An illustration. Event types follow the API reference; the values are examples.
Pick up where you left off
Connections drop. The log stays, every event has a place in it, and a stream can start from any place. Reconnect and read on: the agent kept working while you were away.
The cursor is the frame’s id. The CLI prints it, so a shell resumes at a number. The SDKs hand you the event alone, so SDK code replays from 0 and skips the ids it has handled.
CLI
# Each line the stream prints carries its frame's "id".
# The cursor is inclusive: resume one past the last you handled.
ork agent sessions events stream \
--session "$SESSION_ID" \
--from-cursor 5 \
--timeout 60sTypeScript
const handled = new Set<string>();
// Call it again after a dropped connection: it replays the
// log and skips what it has already handled.
async function read() {
const replay = await orca.sessions.events.stream(session.id, {
from_cursor: "0",
});
for await (const event of replay) {
if (handled.has(event.id)) continue;
handled.add(event.id);
console.log(event.type);
if (event.type === "session.status_idle") return;
}
}
await read();Python
handled: set[str] = set()
# Call it again after a dropped connection: it replays the
# log and skips what it has already handled.
def read() -> None:
replay = client.sessions.events.stream(session.id, from_cursor="0")
for event in replay:
if event.id in handled:
continue
handled.add(event.id)
print(event.type)
if event.type == "session.status_idle":
return
read()Response
- agent.tool_result written while you were away
- agent.message written while you were away
- span.model_request_end token usage
- session.status_idle stop_reason: end_turn
An illustration. Event types follow the API reference; the values are examples.
Come back tomorrow
A session has no connection to keep alive and no process to keep running. When a turn ends it goes idle, and it is yours to pick up in a minute or tomorrow. Step through what happens in between.
The turn ends
The session goes idle. Its sandbox stays warm for a minute, in case you answer straight away.
The sandbox is released
After 60 seconds with nothing to do, the Harness Server destroys the sandbox. Scratch files go with it.
The log stays
The session is still idle in the Registry, and every event is still in the Event Store. Nothing is running on its behalf.
You send the next message
Tomorrow, or next month. It is one more event appended to the same log.
A fresh sandbox is acquired
The Harness Server picks the event up and acquires a new sandbox from the session’s environment. The harness resumes from the log.
The agent answers, with the past in hand
The sandbox never held the conversation. The log does, so the agent picks up where the session left off.
Event log
- Nothing yet.
- session.status_idle stop_reason: end_turn
- user.message Which team did you route it to?
- session.status_running the turn starts
- span.model_request_start the model is called
- agent.message Identity. I routed it there yesterday.
- span.model_request_end token usage
- session.status_idle stop_reason: end_turn
An illustration. Event types follow the API reference; the values are examples.