Context
We run periodic background automation on the home server that mirrors operational state into a pinned HAPI session (status text, optional markdown image). That automation must not enqueue or dispatch messages to the agent CLI — it is display-only context for the web UI.
While wiring this up on a mixed worktree (unrelated scroll-guard work), we added hub/web changes locally. This issue tracks extracting that work onto a dedicated branch for review.
Branch
feat/hub-interior-life-notes — pushed to heavygee/hapi
https://github.com/heavygee/hapi/tree/feat/hub-interior-life-notes
What changed (summary)
| Area |
Change |
Why |
| Hub API |
POST /api/sessions/:id/interior-note |
Append assistant message to session store without sendMessage / CLI dispatch |
MessageService |
addInteriorNote() |
Same persistence + SSE as normal messages, codex-shaped payload the web already understands |
SyncEngine |
addInteriorNote() wrapper |
Route handler entry point |
| Hub static |
GET /jessica-mood/portrait.{webp,png} |
Markdown  in session notes — browser <img> cannot attach JWT Bearer headers |
| Web |
normalizeAgent.ts + normalize.ts |
Hub-authored { type: 'text', text } was rendered as JSON via safeStringify; interior notes looked like raw blobs |
| Tests |
normalize.test.ts |
Regression for text + codex interior envelopes |
Design notes
- Interior notes use
meta.sentFrom = 'interior-life' and meta.interiorLife = true so the UI can style/filter later if needed.
- Portrait route reads from
SOUL_SESSION_DIR (default /home/heavygee/coding/SOUL/.session) — server-local path, not committed to the repo.
- No protocol/CLI changes — reuses existing message store and SSE events.
Follow-ups (optional)
Unrelated
Scroll-guard / PR tiann#717 work remains on fix/web-scroll-guard-unwrap-race — do not mix with this branch.
Context
We run periodic background automation on the home server that mirrors operational state into a pinned HAPI session (status text, optional markdown image). That automation must not enqueue or dispatch messages to the agent CLI — it is display-only context for the web UI.
While wiring this up on a mixed worktree (unrelated scroll-guard work), we added hub/web changes locally. This issue tracks extracting that work onto a dedicated branch for review.
Branch
feat/hub-interior-life-notes— pushed toheavygee/hapihttps://github.com/heavygee/hapi/tree/feat/hub-interior-life-notes
What changed (summary)
POST /api/sessions/:id/interior-notesendMessage/ CLI dispatchMessageServiceaddInteriorNote()SyncEngineaddInteriorNote()wrapperGET /jessica-mood/portrait.{webp,png}in session notes — browser<img>cannot attach JWT Bearer headersnormalizeAgent.ts+normalize.ts{ type: 'text', text }was rendered as JSON viasafeStringify; interior notes looked like raw blobsnormalize.test.tsDesign notes
meta.sentFrom = 'interior-life'andmeta.interiorLife = trueso the UI can style/filter later if needed.SOUL_SESSION_DIR(default/home/heavygee/coding/SOUL/.session) — server-local path, not committed to the repo.Follow-ups (optional)
/jessica-mood/*to a neutral path (e.g./session-assets/portrait.webp) before upstreamingtiann/hapiwhen ready, or keep fork-only until upstream accepts the patternUnrelated
Scroll-guard / PR tiann#717 work remains on
fix/web-scroll-guard-unwrap-race— do not mix with this branch.