synapse: a reaction continues its conversation, and the request becomes a record
...
Every Synapse event used to open its own service session, so two messages of
one Telegram chat landed in two unrelated chats. The dispatcher now returns a
`session_key` — derived from the payload according to the user's routing
document — and a later event carrying the same key joins the session that
opened it, within `reaction_session_ttl_minutes` of idle time (0 restores the
old always-fresh behaviour). A session that is still running is never joined:
`create_run` overwrites `state.run`, so a second run would orphan the first
one's subscribers — a busy thread gets a fresh session instead. When the
dispatcher names a different profile for the event, the session's profile is
switched through `set_profile`, never through `save()`, which does not rewrite
`name` or `profile_id`: the thread keeps its name and its history.
What the user reads in the transcript is now only the request. The event
record is a system message (`metadata.source == "synapse_event"`) drawn by a
new SynapseEventNotice.vue in the visual language of the recall badge, and the
framing plus the JSON envelope become a hidden user turn written once, on the
first event of a thread — not on every one. The instructions from Settings
reach the model through a new `current_reaction_instructions` ContextVar and
the `[Reaction session]` block of the system prompt, so they appear in no
transcript at all. The stored system record still has to reach the model, so
ContextBuilder's allowlist becomes `_LLM_SYSTEM_SOURCES = {task_note,
synapse_event}`.
The dispatcher gets a document of its own — how to route, rather than what to
do — stored beside the reaction instructions, versioned separately, and edited
in its own field in the settings panel. `synapse_instruction_versions` gains a
`doc` discriminator, so existing rows keep `'reaction'` and their history
verbatim. The new columns are added by `_MIGRATE`, and the index over `doc`
lives there too: `_DDL` runs first, so an index over a column the migration has
not added yet kills the whole batch on an existing database — which is what
the prod database is. A guard test keeps it that way.
Tests: 1776 passed, 1 skipped (backend), 242 passed (webclient, 28 files).
Eugene Sukhodolskiy
committed
12 hours ago