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:
parent
0733bfaff7
commit
6f12fceea9
6 changed files with 102 additions and 295 deletions
|
|
@ -156,8 +156,8 @@ Item {
|
|||
}
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
description: Translation.tr("Clear chat history"),
|
||||
name: "new",
|
||||
description: Translation.tr("Start a new conversation"),
|
||||
execute: () => {
|
||||
Ai.clearMessages();
|
||||
}
|
||||
|
|
@ -389,13 +389,27 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||
description: Translation.tr("Temperature\nChange with /temp VALUE")
|
||||
}
|
||||
StatusSeparator {
|
||||
visible: Ai.tokenCount.total > 0
|
||||
visible: Ai.context.known || Ai.tokenCount.total > 0
|
||||
}
|
||||
StatusItem {
|
||||
visible: Ai.tokenCount.total > 0
|
||||
icon: "token"
|
||||
statusText: Ai.tokenCount.total
|
||||
description: Translation.tr("Context estimate: %1 tokens\n(Local count of the conversation — not the provider's usage numbers)").arg(Ai.tokenCount.total)
|
||||
// Context occupancy. Until 2026-08-12 this pill showed
|
||||
// `event.tokens`, which was actually the model's
|
||||
// ceiling — a constant 250000 presented as usage. Now
|
||||
// it shows both numbers and the ratio between them,
|
||||
// which is the only reading that means anything.
|
||||
visible: Ai.context.known || Ai.tokenCount.total > 0
|
||||
icon: Ai.context.percent >= 85 ? "error"
|
||||
: Ai.context.percent >= 70 ? "warning"
|
||||
: "token"
|
||||
statusText: Ai.context.known
|
||||
? `${Math.round(Ai.context.used / 1000)}k/${Math.round(Ai.context.limit / 1000)}k · ${Ai.context.percent}%`
|
||||
: (Ai.tokenCount.total > 0 ? `${Ai.tokenCount.total}` : "—")
|
||||
description: Ai.context.known
|
||||
? Translation.tr("Context: %1 of %2 tokens (%3%)\nCeiling comes from this agent's configured context window.\nArchivist begins compacting around 70%.")
|
||||
.arg(Ai.context.used.toLocaleString())
|
||||
.arg(Ai.context.limit.toLocaleString())
|
||||
.arg(Ai.context.percent)
|
||||
: Translation.tr("Context occupancy unknown until the next turn reports it.")
|
||||
}
|
||||
StatusSeparator {
|
||||
visible: Souveraine.turnStartedAt > 0
|
||||
|
|
@ -831,7 +845,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||
dontAddSpace: true
|
||||
},
|
||||
{
|
||||
name: "clear",
|
||||
name: "new",
|
||||
sendDirectly: true
|
||||
},
|
||||
]
|
||||
|
|
@ -894,7 +908,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
|||
messageInputField.cursorPosition = messageInputField.text.length;
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
if (modelData.name === "clear") {
|
||||
if (modelData.name === "new") {
|
||||
messageInputField.text = "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,170 +0,0 @@
|
|||
From 9035751d30a9d1d364faaaa6b38b386353ca0f6c Mon Sep 17 00:00:00 2001
|
||||
From: Souveraine <souveraine@wiuf.net>
|
||||
Date: Wed, 12 Aug 2026 12:11:04 -0400
|
||||
Subject: [PATCH] shell: render context occupancy, and stop reading hyprctl
|
||||
errors as a cursor
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The context pill showed event.tokens, which carried the model's ceiling
|
||||
rather than the usage — a constant 250000 that looked like a measurement.
|
||||
The wire now carries tokens_used and context_limit separately; render both
|
||||
and the ratio, with archivist threshold shading at 70/85%.
|
||||
|
||||
The ambient cursor probe tested whether hyprctl was installed, not whether
|
||||
it answered. Under viewtop the binary exists but there is no Hyprland, so
|
||||
'HYPRLAND_INSTANCE_SIGNATURE not set!' was collected as the cursor position
|
||||
and shipped in every ambient block. Validate the reply's shape instead.
|
||||
---
|
||||
.../modules/ii/sidebarLeft/AiChat.qml | 24 +++++++++++---
|
||||
surfaces/quickshell/services/Ai.qml | 33 ++++++++++++++++++-
|
||||
surfaces/quickshell/services/Souveraine.qml | 23 +++++++++++--
|
||||
3 files changed, 72 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml b/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
index bba12a1..f3ed1b7 100644
|
||||
--- a/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
+++ b/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
@@ -388,13 +388,27 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
description: Translation.tr("Temperature\nChange with /temp VALUE")
|
||||
}
|
||||
StatusSeparator {
|
||||
- visible: Ai.tokenCount.total > 0
|
||||
+ visible: Ai.context.known || Ai.tokenCount.total > 0
|
||||
}
|
||||
StatusItem {
|
||||
- visible: Ai.tokenCount.total > 0
|
||||
- icon: "token"
|
||||
- statusText: Ai.tokenCount.total
|
||||
- description: Translation.tr("Context estimate: %1 tokens\n(Local count of the conversation — not the provider's usage numbers)").arg(Ai.tokenCount.total)
|
||||
+ // Context occupancy. Until 2026-08-12 this pill showed
|
||||
+ // `event.tokens`, which was actually the model's
|
||||
+ // ceiling — a constant 250000 presented as usage. Now
|
||||
+ // it shows both numbers and the ratio between them,
|
||||
+ // which is the only reading that means anything.
|
||||
+ visible: Ai.context.known || Ai.tokenCount.total > 0
|
||||
+ icon: Ai.context.percent >= 85 ? "error"
|
||||
+ : Ai.context.percent >= 70 ? "warning"
|
||||
+ : "token"
|
||||
+ statusText: Ai.context.known
|
||||
+ ? `${Math.round(Ai.context.used / 1000)}k/${Math.round(Ai.context.limit / 1000)}k · ${Ai.context.percent}%`
|
||||
+ : (Ai.tokenCount.total > 0 ? `${Ai.tokenCount.total}` : "—")
|
||||
+ description: Ai.context.known
|
||||
+ ? Translation.tr("Context: %1 of %2 tokens (%3%)\nCeiling comes from this agent's configured context window.\nArchivist begins compacting around 70%.")
|
||||
+ .arg(Ai.context.used.toLocaleString())
|
||||
+ .arg(Ai.context.limit.toLocaleString())
|
||||
+ .arg(Ai.context.percent)
|
||||
+ : Translation.tr("Context occupancy unknown until the next turn reports it.")
|
||||
}
|
||||
StatusSeparator {
|
||||
visible: Souveraine.turnStartedAt > 0
|
||||
diff --git a/surfaces/quickshell/services/Ai.qml b/surfaces/quickshell/services/Ai.qml
|
||||
index c54a185..bc1779b 100644
|
||||
--- a/surfaces/quickshell/services/Ai.qml
|
||||
+++ b/surfaces/quickshell/services/Ai.qml
|
||||
@@ -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);
|
||||
}
|
||||
@@ -509,7 +533,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.
|
||||
@@ -596,6 +625,7 @@ Singleton {
|
||||
root.tokenCount.input = -1;
|
||||
root.tokenCount.output = -1;
|
||||
root.tokenCount.total = -1;
|
||||
+ root.resetContextOccupancy();
|
||||
Souveraine.newConversation();
|
||||
}
|
||||
|
||||
@@ -611,6 +641,7 @@ Singleton {
|
||||
root.tokenCount.input = -1;
|
||||
root.tokenCount.output = -1;
|
||||
root.tokenCount.total = -1;
|
||||
+ root.resetContextOccupancy();
|
||||
|
||||
messages.forEach(message => {
|
||||
const segments = [];
|
||||
diff --git a/surfaces/quickshell/services/Souveraine.qml b/surfaces/quickshell/services/Souveraine.qml
|
||||
index 0898ea8..9b57702 100644
|
||||
--- a/surfaces/quickshell/services/Souveraine.qml
|
||||
+++ b/surfaces/quickshell/services/Souveraine.qml
|
||||
@@ -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: {
|
||||
--
|
||||
2.55.0
|
||||
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
From 18420f9f1d1d2a86587985702d1d12403ed88a8a Mon Sep 17 00:00:00 2001
|
||||
From: Fimeg <casey.tunturi@gmail.com>
|
||||
Date: Wed, 12 Aug 2026 18:52:03 -0400
|
||||
Subject: [PATCH] panel: render a halt in her own register, not the tool's name
|
||||
|
||||
---
|
||||
surfaces/quickshell/services/Ai.qml | 27 ++++++++++++++++++++++++++-
|
||||
1 file changed, 26 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/surfaces/quickshell/services/Ai.qml b/surfaces/quickshell/services/Ai.qml
|
||||
index c54a185..26a9cb3 100644
|
||||
--- a/surfaces/quickshell/services/Ai.qml
|
||||
+++ b/surfaces/quickshell/services/Ai.qml
|
||||
@@ -391,6 +391,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, {
|
||||
@@ -532,7 +548,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}`);
|
||||
--
|
||||
2.55.0
|
||||
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
From f85ccac58e97a8ac138a9547936bed18b1cea16e Mon Sep 17 00:00:00 2001
|
||||
From: Fimeg <casey.tunturi@gmail.com>
|
||||
Date: Wed, 12 Aug 2026 19:37:26 -0400
|
||||
Subject: [PATCH] chat: the reset command is /new, not /clear
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
clearMessages() calls Souveraine.newConversation() — it opens a new
|
||||
server conversation rather than wiping a local view. The label said
|
||||
'clear chat history', which describes a different verb than the one it
|
||||
runs: destructive-sounding, local-sounding, and neither is true.
|
||||
|
||||
Three sites: the command definition, the quick-action chip, and the
|
||||
input-clearing special case that dispatches on the name.
|
||||
---
|
||||
surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml | 8 ++++----
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml b/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
index 0d6e04e..514ad92 100644
|
||||
--- a/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
+++ b/surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml
|
||||
@@ -156,8 +156,8 @@ Item {
|
||||
}
|
||||
},
|
||||
{
|
||||
- name: "clear",
|
||||
- description: Translation.tr("Clear chat history"),
|
||||
+ name: "new",
|
||||
+ description: Translation.tr("Start a new conversation"),
|
||||
execute: () => {
|
||||
Ai.clearMessages();
|
||||
}
|
||||
@@ -831,7 +831,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
dontAddSpace: true
|
||||
},
|
||||
{
|
||||
- name: "clear",
|
||||
+ name: "new",
|
||||
sendDirectly: true
|
||||
},
|
||||
]
|
||||
@@ -894,7 +894,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\)
|
||||
messageInputField.cursorPosition = messageInputField.text.length;
|
||||
messageInputField.forceActiveFocus();
|
||||
}
|
||||
- if (modelData.name === "clear") {
|
||||
+ if (modelData.name === "new") {
|
||||
messageInputField.text = "";
|
||||
}
|
||||
}
|
||||
--
|
||||
2.55.0
|
||||
|
||||
|
|
@ -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 = [];
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue