You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
What is the deterministic way to monitor a session is completed #3815
We are using the opencode/sdk and we are observing one issue, where we monitor a session based on session event, and we receive the session idle event sooner, but it seems session might still be working, is there is any deterministic way where we can say agent has stop working and might be asking for user input. Also let me know if it is possible to done by experimental hooks, please provide any sample code if there to check for session completetion
Feature Request: Session Information: State #3169: Feature Request: Session Information: State - Requests session state information to indicate whether sessions are active or idle, which would help with deterministic session monitoring
plugin events are fired multiple times #3744: plugin events are fired multiple times - Shows session.idle events being fired multiple times, indicating potential issues with session event reliability
Feel free to ignore if none of these address your specific case.
Sooner than what? I could see you getting session idle event and then the title generation completing after and sending session updated, but maybe you are encountering some other issues?
@rekram1-node I am also observing the same , session idle is coming sooner the agent is completing work
Also the issues which you have tagged, message api doesn't even guaranteed that agent has finished the worked, What I have observed is, when a session hang, message api is stuck forever so we never know when is the actual session getting completed
@krishna1s Did I tag an issue? I dont think I did?
session idle is coming sooner the agent is completing work
are you using subagents or something, can u provide any steps to reproduce this? The only case this should be possible is for one of the background things like title gen to still be going, otherwise we send that event after the agent loop is finished
I have a plugin that fires toasts whenever the session idle happens, I've never seen it fire a toast before the session was complete but maybe there is some edge case that I don't encounter that you guys have?
So one workaround is that it is possible to query the internal opencode DB for the session in question and then get the last message and see if its last_finish == ‘stop’.
opencode status --session ses_2e30ba9fbffe9wtQTgcG0aGZoQ | to json
#!/usr/bin/env nu# OpenCode session utilities## Query the local OpenCode SQLite database for session information.# The database lives at ~/.local/share/opencode/opencode.db.## Commands:# opencode status - Get the status of a session by ID## Examples:# opencode status --session ses_2e30ba9fbffe9wtQTgcG0aGZoQ# Get the status of an OpenCode session## Queries the local SQLite database to determine whether a session is# completed, interrupted, or idle. Status is derived from the last# assistant message's finish reason and timing data.## Statuses:# completed - Agent finished normally (last message finish = "stop")# interrupted - Agent was mid-tool-call and stopped (finish = "tool-calls")# filtered - Response was blocked by content filter# in-progress - Last assistant message has no completion time (still running)# idle - No assistant messages yet (user prompt with no response)## Examples:# opencode status --session ses_2e30ba9fbffe9wtQTgcG0aGZoQ# opencode status --session ses_2e30ba9fbffe9wtQTgcG0aGZoQ | get status## Returns: Record with session details and status
export def "opencode status" [
--session: string # The session ID (e.g., ses_xxx)
]: nothing -> record {
let db = $"($env.HOME)/.local/share/opencode/opencode.db"let raw = (
sqlite3 -json $db $" SELECT s.id, s.title, s.slug, s.version, s.time_created, s.time_updated,\(SELECT count\(*\) FROM message WHERE session_id = s.id\) as message_count,\( SELECT json_extract\(m.data, '$.finish'\) FROM message m WHERE m.session_id = s.id AND json_extract\(m.data, '$.role'\) = 'assistant' ORDER BY m.time_created DESC LIMIT 1\) as last_finish,\( SELECT json_extract\(m.data, '$.time.completed'\) FROM message m WHERE m.session_id = s.id AND json_extract\(m.data, '$.role'\) = 'assistant' ORDER BY m.time_created DESC LIMIT 1\) as last_completed FROM session s WHERE s.id = '($session)';"
)
if ($raw| str trim | is-empty) {
error make { msg: $"Session ($session) not found" }
}
let row = ($raw| from json |first)
let status =if ($row.last_finish | is-empty) and ($row.last_completed | is-empty) {
"idle"
} elseif ($row.last_completed | is-empty) {
"in-progress"
} elseif$row.last_finish =="stop" {
"completed"
} elseif$row.last_finish =="tool-calls" {
"interrupted"
} elseif$row.last_finish =="content-filter" {
"filtered"
} else {
$"unknown \(($row.last_finish)\)"
}
{
id: $row.id
title: $row.title
slug: $row.slug
version: $row.version
messages: ($row.message_count | into int)
status: $status
last_finish: $row.last_finish
}
}
For V2, the deterministic API is client.v2.session.wait(...). Queue the prompt, then await wait for that session. It returns only after the V2 session agent loop becomes idle.
import{createOpencode}from"@opencode-ai/sdk/v2"constopencode=awaitcreateOpencode()constclient=opencode.clientconstsessionID="ses_..."awaitclient.v2.session.prompt({
sessionID,prompt: {text: "Do the task",},})awaitclient.v2.session.wait({ sessionID })console.log("V2 agent loop is idle")
If your integration also needs to know whether the agent is blocked waiting for user input, subscribe to events separately and handle permission.asked / question.asked while the wait is pending:
import{createOpencode}from"@opencode-ai/sdk/v2"constopencode=awaitcreateOpencode()constclient=opencode.clientconstsessionID="ses_..."constevents=awaitclient.event.subscribe()constmonitor=(async()=>{forawait(consteventofevents.stream){if(event.type==="permission.asked"&&event.properties.sessionID===sessionID){console.log("waiting for permission",event.properties)}if(event.type==="question.asked"&&event.properties.sessionID===sessionID){console.log("waiting for an answer",event.properties)}}})()awaitclient.v2.session.prompt({
sessionID,prompt: {text: "Do the task",},})awaitclient.v2.session.wait({ sessionID })awaitevents.stream.return?.(undefined)awaitmonitor
So for V2, do not infer completion from session.idle; use client.v2.session.wait({ sessionID }). Use the event stream only for progress/user-input notifications.
Activity
github-actions commentedon Nov 3, 2025
This issue might be a duplicate of existing issues. Please check:
Feel free to ignore if none of these address your specific case.
rekram1-node commentedon Nov 3, 2025
@krishna1s
Sooner than what? I could see you getting session idle event and then the title generation completing after and sending session updated, but maybe you are encountering some other issues?
rekram1-node commentedon Nov 7, 2025
Hm I guess the title generation is kinda a "background" thing so it doesnt follow the normal rules but I see what you are saying
krishna1s commentedon Nov 10, 2025
@rekram1-node I am also observing the same , session idle is coming sooner the agent is completing work
Also the issues which you have tagged, message api doesn't even guaranteed that agent has finished the worked, What I have observed is, when a session hang, message api is stuck forever so we never know when is the actual session getting completed
rekram1-node commentedon Nov 10, 2025
@krishna1s Did I tag an issue? I dont think I did?
are you using subagents or something, can u provide any steps to reproduce this? The only case this should be possible is for one of the background things like title gen to still be going, otherwise we send that event after the agent loop is finished
I have a plugin that fires toasts whenever the session idle happens, I've never seen it fire a toast before the session was complete but maybe there is some edge case that I don't encounter that you guys have?
danrasmuson commentedon Mar 24, 2026
So one workaround is that it is possible to query the internal opencode DB for the session in question and then get the last message and see if its last_finish == ‘stop’.
rekram1-node commentedon May 22, 2026
Agent-written answer for the V2 SDK path:
For V2, the deterministic API is
client.v2.session.wait(...). Queue the prompt, then awaitwaitfor that session. It returns only after the V2 session agent loop becomes idle.If your integration also needs to know whether the agent is blocked waiting for user input, subscribe to events separately and handle
permission.asked/question.askedwhile the wait is pending:So for V2, do not infer completion from
session.idle; useclient.v2.session.wait({ sessionID }). Use the event stream only for progress/user-input notifications.bilogic commentedon Jun 30, 2026
@rekram1-node
is that a plugin? My plugin is trying to determine if a session has "completed"
bilogic commentedon Jul 2, 2026
@rekram1-node
StartedandEndedare sent almost together, any idea why the session.wait doesn't hold back theEnded? Thanks!