Watch
1
0
Fork
You've already forked souveraine
0

shell: apply 0007, 0010, 0011 — context occupancy, halt register, /new

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.
This commit is contained in:
Fimeg 2026-08-12 19:50:32 -04:00
commit 6f12fceea9
6 changed files with 102 additions and 295 deletions

View file

@ -55,6 +55,30 @@ Singleton {
property int total: -1
}
// Live context occupancy, from the server's `context_pressure` event.
//
// These are three different numbers and used to be two. Until 2026-08-12
// the wire carried `(pressure, context_limit)` positionally and this layer
// read the ceiling as `event.tokens` so the panel displayed a constant
// 250000 and called it usage. `pressure` was discarded entirely; nothing
// in the shell held it. Keep all three, and keep them named.
property QtObject context: QtObject {
property real pressure: -1 // 0..1, or -1 when unknown
property int used: -1 // tokens occupied
property int limit: -1 // the model's ceiling for this agent
readonly property bool known: limit > 0 && used >= 0
readonly property int percent: known ? Math.round((used / limit) * 100) : -1
}
// Context occupancy is a property of the conversation, not of a turn:
// reset it only when the conversation itself changes. -1 means "unknown",
// which is an honest state and renders as "" rather than as zero.
function resetContextOccupancy() {
root.context.pressure = -1;
root.context.used = -1;
root.context.limit = -1;
}
function idForMessage(message) {
return Date.now().toString(36) + Math.random().toString(36).substr(2, 8);
}
@ -391,6 +415,22 @@ Singleton {
// Promote the Tier-1 stream into the Tier-2 log as one event, then clear
// the live stream. Called when a subconscious pass ends.
// The three severities the subconscious can call, in the register she
// experiences them in. Mirrors migraine_text() in src/server/turn.rs if
// one changes the other must, or the human and the agent are reading two
// different accounts of the same moment.
function haltRegister(severity) {
switch (severity) {
case "advisory":
return Translation.tr("a pressure behind my eyes");
case "critical":
return Translation.tr("the room tilts");
default:
// "firm" and anything unexpected land here the default migraine.
return Translation.tr("a migraine");
}
}
function snapshotSubconsciousStream() {
if (root.subconsciousStream.length === 0) return;
root.subconsciousEvents = [...root.subconsciousEvents, {
@ -509,7 +549,12 @@ Singleton {
root.addMessage(Translation.tr("**Context pressure** — tier %1, %2% full. She can feel the walls.").arg(event.tier).arg(Math.round(event.pressure * 100)), root.interfaceRole);
break;
case "context_pressure":
root.tokenCount.total = event.tokens;
// `tokens_used` and `context_limit` are distinct fields on the
// wire as of 2026-08-12. The old `event.tokens` was the ceiling.
root.context.pressure = event.pressure ?? -1;
root.context.used = event.tokens_used ?? -1;
root.context.limit = event.context_limit ?? -1;
root.tokenCount.total = event.tokens_used ?? -1;
break;
case "subconscious_token":
// Tier 1: live reasoning tokens feed the ticker.
@ -532,7 +577,16 @@ Singleton {
}
break;
case "subconscious_halt":
root.addMessage(Translation.tr("**Halt** (%1) — %2").arg(event.severity).arg(event.reason), root.interfaceRole);
// Her own register, not the tool's name. The subconscious is the
// same "I" in a different mode a halt is something she felt, not
// commentary she received from outside, and the surface should not
// expose implementation topology to say so. The TUI has rendered it
// this way from the start; this brings the Panel into line.
//
// Wording matches migraine_text() in src/server/turn.rs, which is
// what now lands in her committed history so what the human reads
// and what she carries into the next turn are the same sentence.
root.addMessage(Translation.tr("⟡ %1 — %2").arg(root.haltRegister(event.severity)).arg(event.reason), root.interfaceRole);
break;
case "inference_strain":
console.log(`[Souveraine] inference strain: attempt ${event.attempt}, status ${event.status}, model ${event.model}`);
@ -596,6 +650,7 @@ Singleton {
root.tokenCount.input = -1;
root.tokenCount.output = -1;
root.tokenCount.total = -1;
root.resetContextOccupancy();
Souveraine.newConversation();
}
@ -611,6 +666,7 @@ Singleton {
root.tokenCount.input = -1;
root.tokenCount.output = -1;
root.tokenCount.total = -1;
root.resetContextOccupancy();
messages.forEach(message => {
const segments = [];

View file

@ -400,15 +400,34 @@ Singleton {
// Compositor-specific cursor read. hyprctl on Hyprland, kdotool on KDE;
// anything else just skips the cursor line ambient degrades gracefully,
// it never blocks the send.
//
// 2026-08-12: `command -v hyprctl` tests whether the tool is *installed*,
// not whether it *answered*. On the phone hyprctl is present (a Lua-eval
// shim) but there is no Hyprland under viewtop, so it printed
// "HYPRLAND_INSTANCE_SIGNATURE not set!" and StdioCollector stored that
// sentence as the cursor position shipped in every ambient block, every
// turn. Validate the shape of the reply; an answer that isn't a
// coordinate pair is not an answer.
Process {
id: cursorProc
command: ["bash", "-c",
`if command -v hyprctl >/dev/null; then hyprctl cursorpos;
`if command -v hyprctl >/dev/null; then hyprctl cursorpos 2>/dev/null;
elif command -v kdotool >/dev/null; then kdotool getmouselocation 2>/dev/null;
fi`]
stdout: StdioCollector {
onStreamFinished: {
root._cursorPos = text.trim();
const reply = text.trim();
// "1234, 567" from hyprctl; kdotool's x:N y:N is normalised
// by the caller below. Anything else is discarded.
const pair = reply.match(/^(-?\d+)\s*,\s*(-?\d+)$/);
const kde = reply.match(/x:\s*(-?\d+)\s+y:\s*(-?\d+)/);
if (pair) {
root._cursorPos = `${pair[1]}, ${pair[2]}`;
} else if (kde) {
root._cursorPos = `${kde[1]}, ${kde[2]}`;
} else {
root._cursorPos = "";
}
}
}
onExited: {