SIGN IN SIGN UP

feat(byoa): name the project in the wake digest

A BYOA agent that sits in several project groups had no way to tell them apart.
`loadInbox` selected the conversation's title, kind and topic but not its
project, so the wake digest's per-conversation header never mentioned one —
while the cloud turn has rendered `Project: <name>` all along (turn.ts). The
migration that added `conversations.project_id` even says it is "surfaced to
agents in the wake prompt"; for BYOA it was not.

The consequence is the one reported in #45: with nothing distinguishing the
groups, everything the agent has learned reads as one undifferentiated context,
and work from one project gets quoted as background in another.

Carry `project_id` / `project_name` on the inbox row and render a `Project:`
line above the existing `Topic:` line. The join is a PK lookup on a tiny table,
placed inside the CTE that already walks the agent's conversations, so it does
not disturb the query whose plan the surrounding comments were written to
protect.

The fields are optional on InboxRow rather than required-nullable: not every row
source selects them, and direct chats are genuinely project-less. A conversation
without a project renders byte-identically to before.

The header build moves into a small exported `conversationHeader()` so it can be
tested without booting a runner — the same reason `renderInboxDigest` and
`resolveEngineModel` are exported in this file.

This is the prerequisite for scoping memory by project, not that scoping itself;
see the issue thread for why the mechanism proposed there needs rework.
X
Xialie Zhuang committed
bbaf6aec0ee0e1d50ce17d0a59309bf2b8977c1d
Parent: 2c9d17e