Skip to content

Finalize a session before logging on session.idle - #59

Open
CristinaT21 wants to merge 1 commit into
langfuse:mainfrom
CristinaT21:fix/idle-turn-race
Open

CristinaT21 wants to merge 1 commit into
langfuse:mainfrom
CristinaT21:fix/idle-turn-race

Conversation

@CristinaT21

Copy link
Copy Markdown

On OpenCode 1, the session.idle handler awaits client.app.log("Flushing spans") before it
finalizes the session:

if (event.type === "session.idle") {
  yield* log("info", "Flushing spans");
  finalizeSessionTracing(event.properties.sessionID);

app.log is an HTTP round trip to the OpenCode server. If the next prompt for the same
session arrives in that window, chat.message starts the new turn first, and
finalizeSessionTracing then ends that new turn and clears the session's trace state. The
new turn's generations are dropped, because traceGeneration returns when there is no turn.
The trace shows a turn that lasts a few milliseconds with no generation, while OpenCode ran
and billed the step.

This PR finalizes before logging, so nothing is awaited between session.idle and ending the
turn. The OpenCode 2 handler already ends the turn without awaiting anything first.

How we hit it: we run OpenCode 1.18.32 as a server and send prompts with prompt_async.
We ran a two-turn session against a local Langfuse 4.66.0, sending the second prompt as soon as
the first turn returned. In both runs, the second turn's generation was missing (3 of 4
generations recorded), and the Langfuse cost was short by that step. With this change, every
generation was recorded in each of 5 runs: back to back, with a second prompt sent while the
first turn was still running, and with a 3 s pause. In each run the Langfuse cost matched
OpenCode's exactly.

Test: keeps a turn that starts while the previous idle is still logging holds the fake
client's app.log open, starts the next turn while the idle is still logging, and checks that
the next turn's generation is exported. Without the change it fails with
expected [ 'idle-race-assistant-1' ] to include 'idle-race-assistant-2'.

Verification:

  • pnpm run format:check, pnpm run lint, pnpm run knip, tsc --noEmit -p tsconfig.test.json: clean
  • pnpm run test:integration: 69 passed. Locally, decodes the tags plugin option hit the
    default 5 s timeout on its first dynamic import (Docker on Windows). It passes with
    --testTimeout=60000, and the change doesn't touch it.

🤖 Generated with Claude Code

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@bezbac

bezbac commented Nov 3, 2026

Copy link
Copy Markdown
Member

Thanks for the contribution @CristinaT21. Could you sign our CLA, so that we can accept the changes? (see #59 (comment))

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants