Watch
1
0
Fork
You've already forked souveraine
0
Commit graph

531 commits

Author SHA1 Message Date
Fimeg
3aa7019eb5 turns: survive the surface disappearing 2026-08-11 15:01:18 -04:00
Fimeg
783f09606a island: fix three defects found by actually loading the shell
The island had never been load-tested. All three were fatal or noisy and
none were visible by reading.

StyledToolTip takes `text`, not `content`. Two occurrences, each one a
hard "Cannot assign to non-existent property" that failed the entire
module chain up to shell.qml — the bar would not have come back.

The provider-health tooltip could never have shown even once fixed: a
MaterialSymbol has no `hovered`, and our own StyledToolTip fix treats a
non-hoverable parent as never-show. That block exists specifically so an
unavailable provider is not mistaken for "no sessions", so a tooltip was
the wrong carrier. Stated visibly now.

Island read root.QsWindow.window through a non-optional access; inside a
Loader the attached object is not resolved at construction and it threw.

Revealer takes implicitHeight from childrenRect, so anchoring its child
to its own centre closes a cycle once it sits in a Loader. ii-base hid it
by letting the layout drive the Revealer height directly.

Verified: qs -c souveraine reaches "Configuration Loaded" with no warning
naming any of these files.
2026-08-11 14:46:41 -04:00
Fimeg
391e239010 bar: one BarContent for every device, layout as data
ii-base and ii-phone each carried a 357-line BarContent to express eight
differences, all of them either "is this widget shown" or "which slot is
it in". No behaviour differed. Every bar edit had to be made twice or
diverge silently against the pin.

Widgets are now declared once as Components in a registry; each slot is a
Repeater over a list of widget names, so placement and order are data.
Config names a slot or a device profile picks it. "auto" derives the
profile from the same cramped-ness test behind useShortenedForm, so the
phone keeps its arrangement with no config file. Loaders are active only
when placed, so an unplaced widget is never constructed.

The agent island becomes a placeable widget rather than a mount smuggled
into UtilButtons: it reaches every device bar from one declaration.

ClockWidget is unified for the same reason. Its phone fork existed solely
to add customFormat, which BarContent now sets on every device.

bar.agentSessions was landing under background.widgets, not bar, so
Config.options.bar.agentSessions was always undefined and only the ?? true
fallback made the island appear wired. Moved, and merged into the existing
bar.indicators rather than declaring a second one.
2026-08-11 14:40:07 -04:00
Fimeg
d87d6117a4 patches: queue 0005, mount the agent island 2026-08-11 14:22:42 -04:00
Fimeg
b21184217e agent sessions: use shellPath convention for collector path 2026-08-11 14:21:37 -04:00
Fimeg
47ce2d1970 agent sessions: one collector, one island
Collector emits a single JSON envelope for every agent session (Souveraine,
Claude Code, Codex) and always exits 0; a failing provider reports
available:false rather than taking the envelope down.

AgentSessions.qml owns the cadence and projects the envelope. Transport is not
the contract: when the TASK-69 daemon lands, only the source changes.

Island is a morph host for the bar - dot on a cramped bar, pill where there is
room, derived from the same threshold BarContent uses.

Inert until registered in services/qmldir and mounted; see patches/0005.
2026-08-11 14:19:45 -04:00
Fimeg
928bd8d143 face: recreate a refused rig socket
Quickshell retains the failed QLocalSocket object, so toggling its requested state cannot reconnect. Replace the Socket body until the host answers.
2026-08-11 14:08:12 -04:00
Fimeg
e7929e41ca agentsd: collapse the scan predicate 2026-08-11 14:05:51 -04:00
Fimeg
d739f3597d agentsd: drop dead limit collection from the session parser 2026-08-11 14:05:28 -04:00
Fimeg
60a29782d1 agentsd: one collector for claude, codex and souveraine sessions 2026-08-11 14:03:57 -04:00
Fimeg
5afa132ff4 face: keep knocking until the rig answers 2026-08-11 14:03:23 -04:00
Fimeg
e314d4fa23 face: expose the two live gaze links
Let the remote shell deploy accept the assets its caller already staged, so a successful composition does not fail after switching the tree.
2026-08-11 14:02:02 -04:00
Fimeg
8fd1a72edd face: reconnect to the rig after host startup 2026-08-11 13:33:09 -04:00
Fimeg
9149c409b7 subconscious: carry her thread across restarts
One unreadable conversation.json aborted the whole listing, so hydration
returned an error, the caller read it as no prior thread, and a fresh
conversation opened on every boot. Skip the damaged record and warn.

Pick the thread by activity, not registration order: list_for_agent().last()
is the oldest conversation once hydration has pushed them newest-first.
Same fix for the scheduled background turn.
2026-08-11 13:18:58 -04:00
Fimeg
cfad6881d1 api: carry ordered text and image parts across the message boundary
`Message.content` was `String`, so an attachment could not reach the
substrate even though everything downstream was ready for it: the session
already stores `ContentBlock::Image`, `run_turn` already builds OpenAI
multi-part content, and the provider client already speaks `image_url`.
The whole pipe was plumbed and the tap was welded shut at the door.

`MessageContent` is untagged — a bare JSON string is exactly the wire it
has always been, an array carries ordered parts in the substrate's own
`ContentBlock` vocabulary. Same shape `ContentValue` uses facing the
provider, turned inward. No new noun.

Only `text` and `image` cross. `tool_use`, `tool_result` and `reasoning`
are the engine's to write; a surface posting one gets a 400 rather than a
forged turn quietly filed in history. And the vision gate now answers at
the door: an image sent to a text-only model returns 422 naming the model
instead of being stripped to "[Image: attached by user]" downstream,
which reads to the human like she looked and said nothing. Conversion
happens for every message before any of them is stored, so a rejection in
the second cannot leave the first half-committed.

Nine tests: legacy string, typed text, mixed order, round trip, replay
out of persistence, refusal of engine-owned blocks, the gate's question.

The surface half is untouched. `Ai.qml::attachFile()` still refuses, and
its comment is now out of date — that is the next checkpoint, and it is
Casey's to compose and restart.
2026-08-11 12:30:17 -04:00
Fimeg
c367a4dbd9 rescue: preserve unversioned source copy found on archdev
/home/casey/souveraine on the build box is a full copy of this repo with no
.git at all, newest sources 2026-08-09 21:07. Audited by hashing every file
and querying this repo's object database: 1545 checked, ten unknown after
excluding the vendored upower submodule.

Eight carry real deltas; roughly fifty substantive lines have no upstream
equivalent. They are the follow-up to b2774c8 that was never committed: a
logind capability query before firing a power verb, and a failure-path release
of the in-flight power latch without which a refused suspend deafens the verb
until the next boot.

Preserved verbatim, not merged. The tree has moved 100+ commits and the
sessiond power seam wants a human.
2026-08-11 11:06:11 -04:00
Fimeg
517d8c1927 subagent: send task as user message, not folded into system prompt 2026-08-11 10:48:55 -04:00
Fimeg
e498e72b77 memory: gate write over an existing file, report the delta
write replaces a whole file and reported success identically whether the
file grew, held, or was erased. Replacing an existing file now requires
overwrite: true, and the refusal names append/edit as the likely intent.
Every write returns a WriteDelta so the result carries magnitude; a write
that drops a file below half its former size says so loudly.

Internal callers (archivist, compaction, subconscious ledgers, REST) keep
the ungated path — their replacement is structural.
2026-08-11 10:33:20 -04:00
Fimeg
d290b12441 patches: queue QML compatibility fix 2026-08-11 09:37:09 -04:00
Fimeg
23cebc531d shell: keep typed segments QML-compatible 2026-08-11 09:35:53 -04:00
Fimeg
8c434dfb74 shell: render typed chat segments
Keep assistant, reasoning, tool-call, and tool-return frames structured through the sidebar delegate. Tool cards bind returns by wire id and fold their output by default.

Reasoned from the SSE contract and existing TUI renderer. Diff check passed. Untested against a running shell: Casey applies and reloads staged patches.
2026-08-11 09:13:50 -04:00
Fimeg
a8f7f62471 shell: stop sensor calls closing an open reasoning fence
A tool_call/tool_return arriving while a <think> block was open emitted its
own self-closing fence, whose </think> closed the outer block early. Every
reasoning token after that point rendered as prose, and the stray close left
the trailing assistant text mis-segmented. Append inside the open fence
instead; only open one when arriving in prose.
2026-08-11 09:13:50 -04:00
Fimeg
52f379f230 patches: queue typed chat segments 2026-08-11 08:56:08 -04:00
Fimeg
f17fa7c31b patches: queue think-fence collision fix 2026-08-11 08:10:39 -04:00
Fimeg
aaea09fdd6 shell: patches queue — agent stages, human applies
The composed config is symlinked into the repo and quickshell reloads on
write, so an agent editing QML edits the live shell mid-turn. Stage changes
as patches instead; the human applies and reloads on their own beat.

First in the queue: 0001-resume-offer — the agent-established hook offers
the latest thread instead of silently attaching it. Explicit resume paths
and the e4e6594 amnesia fix are untouched. Untested against a running
shell.
2026-08-10 23:04:59 -04:00
Fimeg
ce6741bf77 compaction: target the active conversation, not the oldest one 2026-08-10 22:50:24 -04:00
Fimeg
28ed6f2151 lens: explicit status guard instead of optional chains 2026-08-10 22:35:37 -04:00
Fimeg
3175700ea2 subconscious: a failed pass must not report as a quiet one
The Err branch surfaced the same 'no anomalies detected' text as the
success path, at Low urgency. A subconscious erroring on every turn was
indistinguishable from one noticing nothing. The only record of the real
cause was a tracing::warn on a stderr whose socket has no reader.

Carry the error in-band at High, and say the pass failed.
2026-08-10 22:27:53 -04:00
Fimeg
29ec9fe223 fingerprint-preview: root in Singleton, not QtObject
The reload killer: FileView under a QtObject root — QtObject has no
default property, so the whole service tree failed to compile. Every
working service singleton roots in Quickshell's Singleton type.
2026-08-10 21:41:24 -04:00
Fimeg
cb7dd73d2b sidebar: the garden's micro-view — Lens service, vault card in the right panel
The vault card lives where notifications were — header with sync pill
(clean/ahead/behind/dirty), today's note entry, five freshest notes,
cold start and push rows — over the notification list. Lens.qml owns
souveraine-lens the way Face owns the rig: one socket, event lines up,
no vault access of its own. Kiyoshi's slash grammar and tool registry
are noted for the fuller app; the micro view only opens and plants.
2026-08-10 21:24:23 -04:00
Fimeg
658217e72e polkit: add temporary FPC confirmation 2026-08-10 20:46:28 -04:00
Fimeg
902a724855 lock: add FPC wiring preview 2026-08-10 20:10:50 -04:00
Fimeg
6eda491496 turn: the cache split reaches the message, not just a log line
a10ef5e added the counters and nothing read them — assistant_with_usage
had zero callers, so ConversationMessage.usage was always None and the
only proof caching worked was a grep. The turn now sums usage across its
rounds and commits it with the reply, and the per-round log carries the
split on every provider rather than only the Anthropic bridge.

memory: expose sliding_reflect. The tool offered sliding_window, whose
own doc comment says to use SlidingReflect unless you want blind
truncation, and hid the safe variant entirely.
2026-08-10 19:35:34 -04:00
Fimeg
30e1c34a34 providers: the provider follows the model, not the agent
Subconscious, reflection and archivist each pick a model independently
of the primary, then took the primary's provider to run it on. A
subconscious on deepseek-v4-flash under a claude-subscription primary
was sent to api.anthropic.com asking for a model it has never heard of,
so a split like Opus-5 primary with a DeepSeek subconscious could not
work at all.

ProviderRegistry now carries the [models.<name>] provider map and
resolves by model, falling back to the agent's provider when a model
has no entry — the single-provider case every agent used before.
2026-08-10 14:36:51 -04:00
Fimeg
eee26fcf57 turn: keep the reply a raced interjection used to erase
A finished text response arriving with a queued interjection was pushed
nowhere: not streamed, not added to messages, and overwritten in
final_content on the next round. The surface showed nothing and she
answered the interjection blind to what she had just said. It now
streams, enters history, and is carried into the single commit.

compact: summary dropped the system anchor. Index 0 is excluded from
to_summarize because it must not be compressed, then was left out of
keep_indices too, so the primary lost persona and covenant on her first
compaction. Every other strategy seeds vec![0]; culled_count already
counted as though this one did.
2026-08-10 14:24:32 -04:00
Fimeg
dfc89d09ea claude: log the cache split per call
A cache hit and a short prompt are indistinguishable from outside;
this provider logged nothing per request at all, which is how an
uncached bridge went unnoticed across 93 turns.
2026-08-10 13:33:54 -04:00
Fimeg
a10ef5e3ed claude: cache the prefix the bridge was re-billing every turn
No cache_control was ever set, so tools, system and the whole transcript
were charged in full on every round. Breakpoints now sit on the tail of
each stable span; message content is always a block array so the marked
tail cannot change the prefix bytes as it moves.

Mid-conversation system notes — the turn loop's pulse and interjections,
the subagent round warnings — were hoisted into the top-level system
field, rewriting the front of the prefix on the round they fired. They
stay where they happened now. Usage carries the cache split; input_tokens
alone understates the prompt by whatever was served cheaply.
2026-08-10 13:33:00 -04:00
Fimeg
bf469c4216 lock: answer touchKeypad on a cold start, not just a reload
PersistentProperties is in-process, so it carries the surface choice
through a reload and not through a fresh shell. sessiond locks before the
shell exists, so a greetd-started shell adopts a live lock with
Config.ready still false and binds the desktop surface — and
WlSessionLock.surfaceComponent cannot change once active. A lock screen
that will not take your PIN, on the iPhone 7 tonight.

Reads the environment, which answers at construction. Unset behaves
exactly as before, so nothing changes on blueline until it sets it.
2026-08-09 22:21:14 -04:00
Fimeg
eb0292c342 sessiond: dim by percent, not an absolute
brightnessctl set 10 is 4% on blueline's 255-step backlight and 0.5% on
the iPhone 7's 2047-step Apple DWI, where it reads as fully off. Measured
on d10 2026-08-09: brightness=1 with the panel still powered, so the
screen went black on a dim that never blanked.
2026-08-09 21:19:30 -04:00
Fimeg
b2774c8aac sessiond: a power seam that cannot report an outcome it does not have
Power is the one irreversible transition and it bypassed the authority
entirely: both menus called Session.poweroff() straight into systemctl, so
there was no Action, no trail entry and no refusal path.

The daemon gains the verb and the shell gains a transport for it. The
request rides its own short-lived connection, never the heartbeat, because
suspend can hold the daemon's handler until resume while the heartbeat
must stay free to carry locked_ack and the EOF that means shell death. A
disconnect after the request was sent reports outcome_unknown rather than
guessing.

Session.qml keeps the legacy executor until this is exercised on hardware.
Unbuilt and unrun; CI is the first reader.
2026-08-09 19:31:33 -04:00
Fimeg
c802f7db48 packaging: sessiond ships on both arches, enabled on neither
CI already built the x86_64 daemon and then threw it away — the binary and
its user unit were copied into the package only under aarch64, so the
laptop could never receive the thing every lock, blank and button report
assumes is running.

Both now ship, and the package still neither enables nor starts the unit.
Phone-only reporters and surfaces stay aarch64.
2026-08-09 19:31:33 -04:00
Fimeg
32c019875c usb: land the HID controller the doorway was already calling
78d6743 committed PowerMenu's half and never added the file, so the
singleton in qmldir resolved to nothing and the shell would not load on a
tree that had not been hand-linked.

HidController streams to usb-hid-inject and acknowledges every accepted
report, closes both endpoint fds on the busy edge before usb-signaller
tears the functions down, and refuses non-ASCII on the glass instead of
clearing a field whose first report was never written.

The face reports the agent and host results back to the page, and
deploy.sh stages the tracked face assets before the QML switch so a new
control cannot arrive without the page that implements it.
2026-08-09 19:31:07 -04:00
Fimeg
abdf31087d docs: the working agreement for this repo
Points at the personal agreement and the cross-repo map, names this tree
as the substrate rather than a harness, and starts architecture at
saf/INDEX.md.
2026-08-09 19:30:56 -04:00
Fimeg
78d6743776 usb: make the HID controller doorway honest 2026-08-07 17:36:25 -04:00
Fimeg
9ceaf61fa6 memory: base scoped commits on HEAD 2026-08-07 16:06:41 -04:00
Fimeg
0c9db09888 memory: persist every git index mutation 2026-08-07 16:02:16 -04:00
Fimeg
8cd2adb828 usb: build the full KVM composite surface 2026-08-07 14:48:07 -04:00
Fimeg
16c6ee1033 multitasking: one owner for the transition, and the release travels
ZoneTransition owns the card rect, the four overview verbs, the end-target
table and the gesture clock. ZoneOverview and the rail read it; neither
derives a geometry.

Deletes poseActiveZone/clearPose/_posed, zoneCard.scale and
zonePullProgress. The card's ColumnLayout went too — its margin was a second
opinion about where a window sits in its zone; panes are the compositor's
tiling scaled.

The swipe felt wrong because the release committed where the thumb left it.
settleTo() travels to the destination first and commits on arrival, with
quickstep's numbers (350ms cap, min(1/0.7, 1/0.3)). Abandoned half-swipes
settle to last_zone instead of snapping. Dwell gate restored — quickstep
gates the same way and "ever paused" is a latch.

Card box is measured from the surface, not assumed from the screen: the
overview panel respects exclusive zones and its height moves.

Strand repro passes on hardware.
2026-08-07 12:26:12 -04:00
Fimeg
72171accec the card is the window's size through the whole pull
Two fixes to the same seam. columnLayout was still gated on
missionControlOpen, so a peek raised a panel whose contents were
invisible — the same line that had been left behind once before, with
its own comment saying so.

The card scaled 0.6 -> 1.0 while pose runs 1.0 -> 0.6 against the same
progress, so at the handoff the window was at 0.6 of the glass and the
card arrived near full-bleed. Same curve now, and the 26/12 inset is
gone: scale is the inset, and stacking both squashed the aspect because
the bottom label margin has no counterpart at the top.
2026-08-07 08:52:58 -04:00
Fimeg
452acea0ad restore every window the drag posed, not the ones the poll names
Going home from a pull left the app scaled down until the pill was
dragged back to clear it. clearPose walked ViewtopControl.windows, which
is the 2 s poll's list, so a window that list no longer named never got
its 1.0 back. Track the ids actually posed and clear those too.
2026-08-07 08:52:58 -04:00