Watch
1
0
Fork
You've already forked souveraine
0

lock surface: restore redacted chat on unlock + announce replies

Redacted messages stayed '[content hidden until unlock]' forever — nothing
restored them on unlock. Redaction now records the message and unlock
restores content from rawContent. Turns finishing while locked are also
redacted and surfaced. New LockAgentCard shows a one-line preview of
replies that landed during the lock window on the lock glance, like a
notification; body stays hidden until unlock.

sessiond auth: getpwuid_r buffer is u8 with a c_char cast so it compiles
on aarch64 (c_char = u8 there) and x86 (c_char = i8).
This commit is contained in:
Fimeg 2026-07-16 21:16:28 -04:00
commit e10efbbd56
6 changed files with 160 additions and 9 deletions

View file

@ -131,20 +131,71 @@ Singleton {
// raw content is preserved in the message's rawContent so it can be
// shown again after unlock (the message stays in history), but the
// displayed content is cleared.
//
// The redaction is reversible: every message whose content we replaced
// with the placeholder is recorded in `redactedOnLock`, and on unlock
// its content is restored from `rawContent` (the streaming message too,
// if it is still in flight). A finished message redacted at lock then
// surfaced on the lock screen counts as "delivered" and is restored
// normally on unlock; one that never surfaced stays for the chat to
// replay once unlocked.
property var redactedOnLock: []
function _redactMessage(msg) {
if (!msg || msg.content.length === 0) return;
if (msg.content === Translation.tr("[content hidden until unlock]")) return;
msg.rawContent = msg.content;
msg.content = Translation.tr("[content hidden until unlock]");
if (!root.redactedOnLock.includes(msg)) {
root.redactedOnLock = [...root.redactedOnLock, msg];
}
root.redactedMessagesChanged();
}
function _restoreRedacted() {
if (root.redactedOnLock.length === 0) return;
for (const msg of root.redactedOnLock) {
if (msg && msg.rawContent && msg.rawContent.length > 0) {
msg.content = msg.rawContent;
}
}
root.redactedOnLock = [];
root.redactedMessagesChanged();
}
// Messages redacted while locked, newest first, for the lock surface to
// surface as ambient notifications. Strips to a one-line preview the
// lock screen shows "the agent replied", not the personal-tier body.
function redactedPreviews() {
return root.redactedOnLock
.filter(m => m && m.rawContent && m.rawContent.length > 0 && m.role === "assistant")
.map(m => {
const firstLine = m.rawContent.split("\n").find(l => l.trim().length > 0) || "";
return firstLine.slice(0, 80);
})
.reverse();
}
signal redactedMessagesChanged()
Connections {
target: GlobalStates
function onScreenLockedChanged() {
if (!GlobalStates.screenLocked) return;
if (!root.streamingMessage) return;
// Lock fired mid-stream. Redact the displayed content.
// rawContent preserves the actual response for post-unlock.
if (root.streamingMessage.content.length > 0) {
root.streamingMessage.content = Translation.tr("[content hidden until unlock]");
console.log("[ai] lock fired during stream — redacted personal output");
if (GlobalStates.screenLocked) {
// Lock fired mid-stream redact the in-flight content.
if (root.streamingMessage) {
root._redactMessage(root.streamingMessage);
console.log("[ai] lock fired during stream — redacted personal output");
}
} else {
// Unlock: restore every message we hid, so the chat shows
// what actually came back rather than lingering placeholders.
root._restoreRedacted();
}
}
}
// Streaming message shaping
property AiMessageData streamingMessage
property bool inThinkBlock: false
@ -171,6 +222,12 @@ Singleton {
}
root.streamingMessage.thinking = false;
root.streamingMessage.done = true;
// If the turn finished while the session is locked, the finished
// assistant message is personal-tier output the user hasn't seen
// redact it for the chat view and let the lock surface announce it.
if (GlobalStates.screenLocked && root.streamingMessage.content.length > 0) {
root._redactMessage(root.streamingMessage);
}
if (root.postResponseHook) {
root.postResponseHook();
root.postResponseHook = null;