feat: emit turn_start event carrying provider and model - #726
Open
akandola wants to merge 1 commit into
Open
Conversation
akandola
force-pushed
the
add-turn-start-event
branch
from
July 15, 2026 14:47
a2f73e8 to
64d9031
Compare
akandola
force-pushed
the
add-turn-start-event
branch
from
July 23, 2026 19:45
64d9031 to
84570bc
Compare
Adds an additive turn_start WireEvent emitted once per turn after the model is resolved, so consumers can attribute a turn to its provider and model without inferring from a later provider_switched/provider_error (which only fire on a switch or failure). Model resolution degrades to blank rather than blocking the turn. Handles the new arm in the runtime-client snapshot reducer's metadata group. Adds a protocol type test.
akandola
force-pushed
the
add-turn-start-event
branch
from
July 29, 2026 21:35
84570bc to
e13e2db
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #725.
What
Implements the accepted spec in #725 (emit provider + model on the wire per turn).
Adds an additive
turn_startWireEventcarrying the turn's provider and resolved model, emitted once per turn. Consumers currently cannot attribute a turn's output to a provider/model on protocol v3 — provider and model are internal-only and only surface indirectly via a laterprovider_switched/provider_error, which fire only on a switch or a failure.turn_startgives observers the provider + concrete model up front, deterministically, on every turn.Emission points (one
turn_startper turn)turn_startis emitted exactly once per turn. The two emission sites cover the two mutually-exclusive execution paths — a turn runs through one or the other, never both:packages/runtime/src/session/chat.ts(runTurn, viapublish). This is the long-lived desktop / self-host runtime (conversation-routes.ts→runTurn→execTurn→ backend/session). The model is pin-resolved (resolveModel(pin?.model, pin?.provider)); resolution is best-effort and degrades to blank rather than blocking the turn.packages/runtime/src/turn/turn-session.ts(runPiTurn, viaemit). This is the stateless per-request cloud server (turn/server.ts: one HTTP request = one agent turn). It emits at the first point the concrete model is known, after applying the pin (m.id), so it carries the real model id, not the raw pin.execTurn(local path) never callsrunPiTurn(cloud path); the two are separate code paths, so a given turn produces a singleturn_startcarrying the concrete model it runs against.Backward-compatible / additive
WireEventdiscriminated union — no existing event is changed.packages/runtime-client/src/snapshot.ts) handles the new arm as a metadata marker (grouped withprovider_switched/context_compacted): it does not touchrunning/partial, so live-turn snapshot semantics are unchanged.Rebase
Rebased onto engine-pod-v0.5.24 (
ef469f25), currentmain. Rebases cleanly — no conflicts. Upstream'schat.tsrefactor and this PR'sturn_startemission touch non-overlapping regions, so the three-way merge applies them side by side.resolveModel's signature (resolveModel(override?, providerOverride?)) is unchanged across the range, so the local-path callresolveModel(pin?.model, pin?.provider)still matches, and the cloud path still exposesprovider+ resolvedm.idat the emission point.Tests (all green on
ef469f25)@houston/protocoltypecheck → cleanpackages/protocol/src/wire.test.ts(protocol re-export + discriminant narrowing) → 1/1 pass@houston/runtime→ 737/737@houston/runtime-client→ 136/136Files
packages/protocol/src/wire.ts— newturn_startunion armpackages/protocol/src/wire.test.ts— type testpackages/runtime/src/session/chat.ts— local-runtime emissionpackages/runtime/src/turn/turn-session.ts— cloud-executor emissionpackages/runtime-client/src/snapshot.ts— reducer handles the new arm