[Bug] Renaming a session to a title containing `:closed:` makes it reject every message
Description
Renaming a session to a title containing :closed: makes that session reject every later message with 409 Session is closed, truncates the title, and marks the session closed in the API. Nothing is reported at rename time.
sys_session_close historically freed the (parent_conversation_id, title) unique slot by appending ":closed:<the closed child's id>" to a child's title. has_closed_title_marker in omnigent/util/session_lifecycle.py still detects that legacy state by testing for the bare substring:
return bool(title and CLOSED_TITLE_INFIX in title)PATCH /v1/sessions/{id} writes the caller's title straight to the store, so any title carrying those characters is read back as internal state. Everything downstream follows: title_without_closed_marker truncates the title at the marker for display, labels_with_closed_status synthesizes omnigent.closed=true into API responses, and the events route refuses new user messages.
Observed on main @ f8e1e4bf through the real routes:
| Title set | Title returned | Labels | Next message |
|---|---|---|---|
release:closed:beta |
release |
{"omnigent.closed": "true"} |
409 Session is closed |
notes about a :closed: door |
notes about a |
{"omnigent.closed": "true"} |
409 Session is closed |
It is recoverable: renaming to a title without those characters restores the session, since the label is derived rather than persisted. Typing :closed: is uncommon, though titles are not always hand-written, as the background title generator also writes them.
Proposed fix: the marker always ends with the row's own id, so match that exact suffix rather than the substring, threading the conversation id into the four helpers and their call sites. A user title can then never be mistaken for internal state, and every legacy row is still recognised. Validating titles at rename time would be an alternative, but it would not help rows whose titles were set by other paths.
Steps to reproduce
- Create a session and note its id.
- Rename it:
curl -X PATCH "$OMNIGENT_URL/v1/sessions/$SESSION_ID" \
-H 'content-type: application/json' \
-d '{"title": "release:closed:beta"}'The response shows "title": "release" and "labels": {"omnigent.closed": "true"}.
- Send a message to that session:
curl -X POST "$OMNIGENT_URL/v1/sessions/$SESSION_ID/events" \
-H 'content-type: application/json' \
-d '{"type": "message", "data": {"role": "user", "content": [{"type": "input_text", "text": "hi"}]}}'The response is 409 Session is closed. Start a new sub-agent session to continue.
Expected: a title is user text. Renaming a session leaves it open and displays the title as written.
Version
main @ f876b854
OS
macOS
Harness
No response
Harness mode
No response
Platform or device
macOS
Observed impact
None
Authentication type
No response
Source: omnigent-ai/omnigent