Follow live jobs
For: application developers.
Open a feed to follow several conversations and jobs over one stream. It carries agent text, steps, waiting approvals and questions, and job state.
The console shares one feed across the live views in a browser tab. Your application can do the same through the SDKs. After a connection breaks, a feed can resume on any edge without repeating events.
WatchJob follows one job and the jobs below it, and resumes the same way.
Open a feed and subscribe
OpenFeed opens a feed and streams its events; UpdateFeed adds and removes what it follows while it runs, on any edge.
| Subscription | Follows |
|---|---|
conversation | A conversation: its latest turn, and each new one as it starts. Only a chat that is yours, shared with you, or nobody's, and only with the content permission: a feed never opens someone else's private chat (Conversations) |
job | A job and the jobs it delegated to |
my_activity | Your own chats as your list shows them: a new turn, its state (waiting for you included), unread, title, pin and archive. Approvals and questions of jobs that are not chats are not on it: list them with ListApprovals. An app's key and an operator's certificate have no chats |
feed := c.OpenFeed(oz0.ConversationFeed(chatID), oz0.MyActivity())
for ev, err := range feed.Events(ctx) {
if err != nil {
return err
}
if t := ev.GetText(); t != nil {
fmt.Print(t.GetText())
}
}
f = oz0.feed(feed.job(job_id))
for ev in f.events():
if ev.text:
print(ev.text.text, end="")
const feed = oz0.feed(job(jobId));
for await (const ev of feed.events()) if (ev.event.case === "text") process.stdout.write(ev.event.value.text);
feed.Subscribe (subscribe) adds more while it runs, and Unsubscribe ends one by its number. A refused subscription says why: not_found, permission_denied, limit or invalid.
What the console's chat drawer subscribes to. One feed per browser tab, opened the first time the drawer is used in that tab (a tab that never opens it holds no feed, which matters with 20 live feeds a person): my_activity for the list and its badges, and one conversation subscription for each chat that is open in a view or has a badge (running, or waiting for you), at most 20.
The subscriptions belong to the tab, not to a page, so switching chats or pages keeps them and their seqs; a chat that neither runs nor waits for you and that no view shows is unsubscribed. A step chip's full payload is read only on expand, through GetJobStep, which needs the content permission and is audited. See the chat drawer.
Events
Every event has the feed's seq, one more than the one before, and the number of the subscription it is of. Events carry only what changed:
| Event | Says |
|---|---|
opened | The feed's token, how long it is kept for a resume, its limits. The first event of every stream |
subscribed | A subscription started, once: its kind and ID, title, agent, owner, state, the tenant's AI label wording, whether its text comes (the content permission), and the seq its events start at |
job | A job of a subscription, once: its number, its ID, its agent and the job above it. Each subscription numbers its jobs apart: two that follow the same job each get all of it |
text | Text an agent wrote, to append to what came before in that job. The first delta of each run of text comes at once; what follows is gathered for at most 80 ms |
step | A step's summary: its ID, the tool, started, done or failed, how long it took. Never its arguments or results: GetJobStep has those, with the content permission, and each read is audited |
approval, question | Something that waits for a person, or was decided, and whether you may decide it. A question's text comes only with the content permission |
state | A job is running, waiting, done, failed or cancelled. Done, failed and cancelled are its end: nothing more of it comes |
conversation | One of your chats changed (my_activity): the chat as your list shows it, or that it left the list |
resync | What the subscription shows may have a gap: fetch it again (GetConversation, GetJob or ListMyConversations). Its events go on after it |
revoked | You may no longer see the subscription, and why: a share was taken back, a role lowered, the content permission taken away, the account disabled or removed, the key revoked |
heartbeat | Every 25 s when nothing else came, a few bytes |
Text, questions and step details come only with the content permission, as WatchJob's do. Without it a subscription shows the shape: steps, states and approvals. The walls of the tenant's label policies decide too, per job: a job of the tree, or a conversation's turn, in a compartment walled off from yours comes without its text and details, however its subscription started, and a job that comes to hold such a compartment while you follow it stops showing them within about ten seconds (a resync says so).
Each recheck also asks again whether you may follow the job at all, so a role lowered to your own jobs revokes a subscription to someone else's.
Resume
A feed is kept for about a minute after its stream ends. To resume, open it with its token (opened's resume_token), the last seq you received and, for each job, its number, ID, the subscription its job event came in, the pos of the last event you got from it (text, step, approval, question and state carry it), and whether its end came (done). Keep the newest eight ended jobs of each subscription; older ones may be forgotten. The SDKs and the console do this by themselves:
- you get your subscriptions back and only what came after: nothing twice, nothing missing;
- any edge can resume it: a job's position is its stream's own offset, the same everywhere;
- every job you name and have not read to its end is followed again from where you were: a conversation's earlier turn, a child job, also one that ended while you were away (the edge reads an ended job's stream to its end);
- what an edge no longer holds and the job's stream still has is read again from the stream, up to 1,000 events a job;
- further behind than that, or in a job that continued as new and left those events behind, the subscription gets a
resync; so doesmy_activitywhen you resume from before the last change it sent, and a revocation you may not have received is sent again; - a client too slow for its feed (512 queued events or 512 KiB) gets a
resyncfor the subscription that overflowed, not an ever-growing queue.
A feed whose minute ran out answers not_found; the SDKs open a new one with the same subscriptions and say so with a resync.
WatchJob takes since_seq and resume_token (each event's seq and resume_token; the token holds each job's position in the tree, at most 256 jobs) and sends only what came after. A watcher that sends neither gets the whole stream, as before, also when it starts long after the job did.
Limits
| Limit | |
|---|---|
| Subscriptions a feed | 50 |
| Running jobs a feed follows | 500, children included, and the jobs a resume names among them; past it the subscription gets a resync that says so |
| Live feeds a person (or app's key) | 20 |
| Live feeds a tenant | 5,000 |
| Kept for a resume | 60 s after the stream ends |
| A stream's longest run | 30 minutes; the client resumes it, which signs it in again |
A refused feed answers resource_exhausted with the limit. Close a tab, or wait a minute for a closed tab's feed to end.
Related tasks
- Feed access and browser streams: Authenticate feed subscribers and use Server-Sent Events from browsers.
Incomplete or delayed feed data
- Step events do not carry the step's cost; read the trace for that value.
my_activityfollows the first 50 chats in the person's list. UseListApprovalsfor approvals and questions on jobs outside chats.- If no poller read a job's final stream, retrieving it can require a node to replay the job. That query can fail for a long job, causing a
resync. Fetch the job's current state and trace when a subscription asks to resync.
Text passes through Temporal before reaching the feed, so it does not arrive at the model provider's first-token time. Plan for additional latency, especially on a busy deployment.
Previous section links
- Section: across edges
- Section: on the wire
- Section: other origins cors
- Section: server sent events
- Section: stream tickets
- Section: what a kill or a revocation does to streams
- Section: who may