Move agent and conversation authority into truthful footer controls, keep external sessions observed-only, and give recent server conversations an explicit picker.\n\nProject itinerary state into a persistent ribbon, invalidate it on every itinerary mutation, and keep failures in the transcript while successful route chatter yields to the ribbon. Long tool returns now scroll in place.\n\nThe SAF now owns the full Panel contract and parity boundary; the abandoned green observer card is removed.
Souveraine.send returns false for a busy turn exactly as it does for a dead
server, and the sidebar rendered every false as "server unreachable" — so
typing while she worked showed the words, then a lie, and dropped the text.
The verbs existed on both sides since 93fe30a and nothing called them.
Type-while-busy now interjects, as the TUI has always done
(src/ui/app/mod.rs:1006). Esc with nothing attached raises a hand. A queued
count sits in the status row and says sent, not read, because 202 from
/interject means only that it was taken.
Deliberately unchanged: the two drain sites and their system/user registers.
Providers disagree about which one they want and that choice is not ready to
be made. Latency is unchanged too — turn.rs:600 runs a whole tool round
without looking, so a note sent into a fifteen-call round waits for it.
The tooltip claimed 'Archivist begins compacting around 70%'. The
archivist does not compact. It reads journal/ and writes
system/synthesized/ - a different substrate from the conversation
entirely (core/archivist/mod.rs:5).
The real advisory tiers are 80/90/95 in ConsciousnessEngine::
on_response, and they never force compaction. Icon thresholds moved
from 85/70 to 90/80 to match.
0007 was fixing a live error: the server has carried named ContextPressure
fields since r463, so event.tokens was undefined and the pill assignment
failed on every turn. Applied and verified against the running shell.
0010 renders a halt in her own register rather than the tool's name.
0011 renames the reset command to /new, which is what clearMessages() does.
Applied 0009 then 0008 (0008's re-synthesize control calls a function 0009
adds). Casey applied and reloaded; verified against a running shell rather
than from a clean load.
0009 Speech.qml
stop() could not stop. playProc ran `sh -c "mpv ... || ffplay ..."` — a
compound command, so sh does not exec-replace itself and SIGTERM killed the
wrapper while the player kept sounding as an orphan. Reproduced directly.
That is the back-to-back TTS slam: every stop left audio playing and the
next speak started a second player over it. Shell dropped; the pid held is
now the pid making noise. ffplay fallback deleted rather than repaired —
needing a fallback is what forced the wrapper that broke the kill.
resynthesize() added, bypassing the cache: the button existed for "that came
out wrong" and, being keyed on the same text, always replayed the identical
file. synthesizing/playing split out of one `speaking` boolean.
0008 AiChat.qml
One line: delegate AiMessage -> AgentMessage. The vendor block named 8 tools;
the registry holds 19. The 11 it could not see were exactly the interiority
surface — outfit, nickname, subagent, atmosphere, reach, consult, itinerary,
todo, schedule, halt, intrusive.
regenerate and edit dropped deliberately, per Casey: text regeneration is not
possible (Ai.regenerate() already returned advice) and there is no in-place
edit (the vendor wrote to a local array the server never sees). delete is
armed and states that it hides locally only. Do not restore them.
Queue entries removed here, per the directory's own discipline.
Tier 2 was written, deployed and mounted by nothing — its own header named the
gap. The chat sidebar hosts it now: ticker long-press, or the psychology
indicator by the input box, which works when no pass is running.
The subconscious three-tier surface (SubconsciousTicker + SubconsciousEventPanel)
is Souveraine-original, not upstream ii. It lived under ii-base/modules/ii only
because it had never been re-homed, so the ii -> souveraine shell migration
silently stranded it: the files survived, but the running shell (qs -c souveraine)
never loaded them, and the feature dropped off the device.
Relocated into modules/souveraine/subconscious/, decoupled from the ii overlay
system it was riveted to:
- SubconsciousTicker: was clean of ii imports but opened the panel through the
ii OverlayContext singleton. Now emits requestOpenPanel(); a host connects it.
- SubconsciousEventPanel: was a StyledOverlayWidget (qs.modules.ii.overlay). Now
plain content a host instantiates; the pin/close/drag affordances were the
overlay host's job, not the content's.
The model (services/Ai.qml) was already substrate-neutral and never moved; only
the views were stranded. GAP NAMED: no souveraine overlay host mounts these yet,
so they do not reach the glass until one exists — the ii overlay host was
intentionally not ported. The dangling ii references (AiChat, OverlayContext,
OverlayWidgetDelegateChooser) are removed.
Speech resolves voice per active agent (agents[].voice_id from the list),
with the system voice as fallback — two agents on a host need not match.
Souveraine.qml carries voice_id into the agents map and adds a turn clock
(turnStartedAt/turnElapsedMs) + a 60s agent-inventory refresh. Ai.qml
declares pendingFilePath and imports qs.modules.common.functions so
StringUtils.ttsClean no longer aborts the stream-finished handler.
Speech.qml singleton: speak(text) reads the who->voice mapping live
from souveraine /v1/config (VoiceConfig tts_url + voice_id), POSTs
VibeVoice /audio/speech ({input, voice, model} -> mp3, same contract
as core/voice/client.rs) and plays via mpv/ffplay. The shell picks no
voice of its own. speech.tts.enable is the kill switch (button hidden
when off); speech.tts.endpoint overrides the mapped URL. Sidebar gains
a volume_up/stop toggle beside send that reads the last visible
assistant reply, skipping lock-redacted content.