[fix] Keep reference families and propose session names from the runner - #5992
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Organization UI (inherited) Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe runner now derives session names from readable user messages and serializes typed workflow references. It sends these values through all alive-watchdog heartbeats and stores typed references in session-turn ledger entries. ChangesSession proposals and workflow references
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant server_ts
participant proposeSessionName
participant buildWorkflowReferenceList
participant startAliveWatchdog
participant sendHeartbeat
participant SessionHeartbeatEndpoint
server_ts->>proposeSessionName: derive session name from AgentRunRequest
server_ts->>buildWorkflowReferenceList: build typed workflow references
server_ts->>startAliveWatchdog: pass SessionProposal
startAliveWatchdog->>sendHeartbeat: forward proposal
sendHeartbeat->>SessionHeartbeatEndpoint: send name and references
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Railway Preview Environment
Updated at 2026-08-12T20:10:27.373Z |
mmabrouk
left a comment
There was a problem hiding this comment.
Inline notes to explain the intent behind each part of this change. They are explanations for the reviewer, not change requests.
| return Array.from(text).slice(0, NAME_MAX_CODE_POINTS).join(""); | ||
| } | ||
| return undefined; | ||
| } |
There was a problem hiding this comment.
This picks the title for a session that no browser will ever render.
It takes the first user message that HAS text, not simply the first user message. A run can start with an image or an attachment, and titling from that would produce an empty string.
The cut counts code points, not UTF-16 units. A plain slice can split a surrogate pair and leave half an emoji at the end of the title.
When no message carries readable text, the function returns undefined and proposes nothing. An untitled session is better than one titled with an empty string.
| ...reference, | ||
| key: key as ReferenceKey, | ||
| })); | ||
| } |
There was a problem hiding this comment.
This is the fix for the dead-route half of the bug.
buildWorkflowReferences returns a map keyed by family: workflow, workflow_variant, workflow_revision. Every persisted shape (the turn append, the heartbeat) stores a flat list instead. The old code serialized that map with Object.values(...), which drops the keys and leaves the reader with three bare uuids.
This builder keeps each element's key, so a reader can tell the workflow from its variant instead of guessing.
| name: proposeSessionName(request), | ||
| references: buildWorkflowReferenceList(request.runContext?.workflow), | ||
| }, | ||
| ); |
There was a problem hiding this comment.
Where the two proposals get attached to the run.
The name matters because the browser is the only other title writer, and no browser renders a headless run. The references matter because today they ride only on a fire-and-forget turn append, so a dropped append leaves a row the UI cannot open. The heartbeat happens on every run, which is why both facts travel with it.
|
@coderabbitai review |
✅ Action performedReview finished.
|
deb3f69 to
c361a2a
Compare
4075881 to
3439b9e
Compare
… heartbeat The turn ledger no longer drops the workflow reference family keys (Object.values): every serialized reference element now carries key = workflow | workflow_variant | workflow_revision. The session heartbeat additionally carries a proposed name (first user message with text, trimmed to 60 code points, surrogate-safe) and the run's typed references; the API fills both once, only while the stored values are NULL, so renames and browser titles always win.
c361a2a to
c5f0362
Compare
3439b9e to
06477eb
Compare
Context
This is the second PR of the untitled-sessions stack. It sits on top of #5991.
What the user sees. Clicking a session row sometimes goes nowhere. The link points at a route that does not exist.
Why it happens. The runner creates the session row for every headless run. It stored references that carried no label.
A "reference" is a pointer to a stored entity, such as
{"id": "..."}. A run has three of them: the workflow, its variant and its revision.buildWorkflowReferencesbuilds them as a map with those three names as keys. The old code then serialized the map withObject.values(...), which throws the keys away. The stored list held three bare ids and nothing that says which is which.So the frontend had to guess. It treated the first UUID as the agent. Sometimes that UUID was a variant id, and a variant id is a dead route.
Changes
Each reference keeps its family name
Every serialized reference element now carries a
key.Look at the reference list the runner sends for one run.
Before:
[{"id": "wf-1"}, {"id": "var-1"}, {"id": "rev-1"}]After:
[{"id": "wf-1", "key": "workflow"}, {"id": "var-1", "key": "workflow_variant"}, {"id": "rev-1", "key": "workflow_revision"}]The heartbeat proposes a session name
The heartbeat is the periodic call the runner makes to the API to say a run is still alive.
A new function,
proposeSessionName(request), builds a title. It takes the first user message that has text. It joins that message's text parts, trims the result, and cuts it to 60 Unicode code points. It counts code points rather than UTF-16 units, so it never splits an emoji in half. It proposes nothing when the input carries only attachments.The proposal and the run's typed references ride every heartbeat, including the final beat that releases the session. The API from #5991 fills each field only while the stored value is NULL. So a repeat is a no-op, and a rename always wins.
One difference between the two writers is on purpose. The API's browser path reads the first user message only, which matches what the browser does. The runner instead skips ahead to the first message that has text. Fill-once makes the two work together: a session whose first message is an image still gets a title from its first readable message.
Tests
pnpm run typecheckis clean.proposeSessionNameand the typed serialization. One of them checks the code-point cut on a string that ends in an emoji.