Watch
1
0
Fork
You've already forked souveraine
0

panel: a message typed mid-turn reaches her

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.
This commit is contained in:
Fimeg 2026-08-17 09:16:11 -04:00
commit da8d69af2f
2 changed files with 59 additions and 2 deletions

View file

@ -32,6 +32,13 @@ Singleton {
property Component aiModelComponent: AiModel {}
readonly property string interfaceRole: "interface"
// Notes handed to the turn already running, still waiting to be read.
// The server takes them at a round boundary (`src/server/turn.rs:260`
// and `:453`), and 202 from `/interject` means *queued*, never *read*
// so this counts what was sent, not what landed. Cleared when the turn
// ends, by which point the drain has run.
property int queuedInterjections: 0
signal responseFinished()
property var messageIDs: []
@ -121,6 +128,10 @@ Singleton {
Connections {
target: Souveraine
function onTurnActiveChanged() {
if (!Souveraine.turnActive) root.queuedInterjections = 0;
}
function onAgentsRefreshed() {
const map = {};
Souveraine.agentList.forEach(id => {
@ -717,6 +728,18 @@ Singleton {
function sendUserMessage(message) {
if (message.length === 0) return;
// A turn is already running: this is an interjection, not a new turn.
// The TUI has always worked this way `src/ui/app/mod.rs:1006`,
// "Submit always when busy, this becomes an interjection". The
// Panel instead dropped the text on the floor and reported the server
// unreachable, because `Souveraine.send` returns false for a busy turn
// exactly as it does for a dead server.
if (Souveraine.turnActive) {
root.interjectUserMessage(message);
return;
}
root.addMessage(message, "user");
const result = Souveraine.send(message);
if (result === "step-up") {
@ -740,12 +763,28 @@ Singleton {
return;
}
if (!result) {
root.addMessage(Translation.tr("Souveraine server unreachable at %1 — start it with `souveraine server`").arg(Souveraine.serverBase), root.interfaceRole);
root.addMessage(Souveraine.serverUp
? Translation.tr("No agent selected — pick one before sending.")
: Translation.tr("Souveraine server unreachable at %1 — start it with `souveraine server`").arg(Souveraine.serverBase),
root.interfaceRole);
return;
}
root._startStreaming();
}
/* Slip a note into the turn already in flight.
It is read at the next round boundary, so a note sent during a long
tool round waits for that round to finish `src/server/turn.rs:600`
runs every tool in a round without checking. That latency is real and
is not something this function can fix. */
function interjectUserMessage(message) {
if (message.length === 0) return;
root.addMessage(message, "user");
root.queuedInterjections = root.queuedInterjections + 1;
Souveraine.interject(message);
}
// Set up the streaming assistant message container. Called after a
// successful send (or after a step-up auth retry succeeds).
function _startStreaming() {