Watch
1
0
Fork
You've already forked souveraine
0

panel: put Souveraine controls at the hand

Move agent and conversation authority into truthful footer controls, keep external sessions observed-only, and give recent server conversations an explicit picker.\n\nProject itinerary state into a persistent ribbon, invalidate it on every itinerary mutation, and keep failures in the transcript while successful route chatter yields to the ribbon. Long tool returns now scroll in place.\n\nThe SAF now owns the full Panel contract and parity boundary; the abandoned green observer card is removed.
This commit is contained in:
Fimeg 2026-08-17 11:51:43 -04:00
commit 2bd61ec4b3
17 changed files with 1292 additions and 166 deletions

View file

@ -23,6 +23,7 @@ How Souveraine is built, and why. For whoever reads it, human or agent. When thi
- [motion](surface/01-motion.md). One clock, two strategies, and why the shell never transforms a window.
- [session start](surface/02-session-start.md). One package-owned dependency graph from compositor socket to authority to shell.
- [USB Hands](surface/03-usb-hands.md). An armed wire, an optional trackpad, and one acknowledged hand into the attached host.
- [agent panel](surface/04-agent-pane.md). One Souveraine conversation, typed thought and action, and truthful controls at the hand.
Coming: the rest of identity (the memory filesystem, the ledgers), the nervous system (schedules, the event log), the consciousness cycle, the runtime, federation, a glossary.

View file

@ -0,0 +1,205 @@
# The agent panel
The Panel is Souveraine's close conversation surface on the desktop. It is not
a generic provider picker laid over somebody else's chat client. The agent in
its footer is the agent this pane can address; the transcript is the substrate's
typed conversation; thought, action, itinerary, subconscious work, and context
pressure remain distinct even when they share a narrow column.
The present shell code is split across
`surfaces/quickshell/services/Souveraine.qml`, the `Ai.qml` compatibility
adapter, and `modules/souveraine/agent/`. The outer list and composer still live
in the borrowed `modules/ii/sidebarLeft/AiChat.qml`. That is a migration seam,
not permission for the borrowed component to become a second owner of state or
product meaning.
## The anatomy
The vertical order is deliberate:
1. the conversation occupies the primary, scrollable field;
2. transient subconscious activity may touch the lower edge of that field;
3. the itinerary remains as a thin persistent ribbon above the composer while
a route exists;
4. the composer owns text entry, send/cancel/interject behavior, attachment
state, and commands;
5. the footer holds compact controls for the agent and the few secondary
surfaces that actually work.
The agent menu opens from its footer chip. It does not live above the
conversation as permanent green furniture. The itinerary does remain above the
composer because it answers a different question: not which process happens to
be alive, but where the current agent is in the work Casey asked her to carry.
Every footer chip must either expose a state the pane owns or perform the act it
advertises. A decorative tool selector is worse than no selector when tools are
configured in the agent's sensorium and cannot be switched here. Remove it.
Commands may still name unavailable work in documentation; they do not earn a
button on the live surface until their path exists.
## One controlled agent, other sessions observed
`Souveraine.currentAgentId` is the controlled position. The selector is
populated from `/v1/agents` through the Souveraine service and may switch only
between those agents. A running turn pins that selection until it finishes or
is cancelled, because changing the face while a response is arriving would
mislabel both transcript and authority.
Codex and Claude processes discovered by `AgentSessions` are not models behind
this pane. Their status may appear under a separate **observed only** heading in
the opened footer menu, with provider and session label, but those rows do not
accept selection and never imply that the composer can steer them. Observation
and control can share a menu only when the boundary is visible in words and in
behavior.
The Panel eventually needs richer active-agent status: substrate reachability,
conversation attachment, primary/subconscious phase, queued interjections, and
the last terminal event. Those facts belong to the Souveraine connection, not
to heuristics over terminal processes.
## A transcript is typed
The substrate emits typed segments. The Panel preserves them:
- assistant speech is ordinary message content;
- reasoning is Thinking and renders in `ThinkingCard`;
- tool invocation and return are one correlated `ToolCard`;
- interstitial text is visibly provisional rather than final speech;
- system/interface notices are not forged as the agent's voice.
Reasoning may be retained for audit, reflection, and context accounting without
being replayed to a generic model as `[Reasoning]:` assistant prose. Storage,
model replay, and visible rendering are three projections of the same typed
record. None may flatten Thinking into speech merely because a provider lacks a
native reasoning input block.
Tool cards collapse after success, stay open while running or failed, and show
status without requiring expansion. Their payload is selectable and scrolls
inside a capped body; a long grep or build result must not be clipped at 240
pixels or stretch one message across the entire conversation. The inner tool
body consumes scrolling while it has overflow. Outside it, the transcript
keeps the wheel.
Tool output is evidence. It may be summarized in the header, but the original
return remains available until transcript retention or compaction explicitly
changes it.
## The itinerary is state, not chat debris
`memory/system/dynamic/itinerary.md` is the canonical route. The Panel does not
parse that file and does not keep a second QML itinerary that dies on reload.
The authenticated `GET /v1/agents/:id/itinerary` endpoint projects its title,
current stop, statuses, descriptions, linked todo ids, nature, and energy.
An itinerary stream event is an invalidation edge. On set, advance, or clear,
the service refreshes that projection. Empty is meaningful: it removes the
ribbon. A shell reload or agent switch asks the substrate again, so current
work survives the surface without turning surface memory into authority.
The compact ribbon shows the title, current stop, phase, progress, and stale
state. Opening it reveals a bounded scrollable list of stops and the todo
metadata the substrate supplied. A successful itinerary tool card is hidden
from the transcript because the persistent ribbon already carries that state;
an itinerary failure remains visible because failed action is evidence.
The itinerary is not the todo ledger. It may reference a todo, but clearing a
route does not erase a commitment and completing a route does not silently
invent one.
## Conversation life
Conversation identity lives on the server. A shell reload may offer the latest
conversation but must not silently attach it unless the configured policy says
so. New, resume, list, filter, and fork all act on server conversation ids; a
local array of bubbles is a projection, never the canonical thread.
The complete Panel needs these conversation acts on glass:
- a clearly named new conversation;
- an explicit resume offer with enough metadata to distinguish the thread;
- a filterable recent-conversation list, with indexed content search as the
larger corpus demands it;
- fork from a selected message or turn boundary;
- visible attachment to a conversation id without exposing token material;
- honest recovery when the shell reattaches to an active stream.
The current adapter carries server resume and active-stream reattachment. The
footer conversation menu now exposes new, continue, recent-thread browsing,
and a local id/date filter. Indexed content search and fork remain parity work
until their owned Panel components and server paths are connected. A slash
command is not the finished interaction merely because it reaches the same
function.
## Composer and input
Send creates a user turn only when no turn is active. During a turn the same
space must make cancel and queued interjection legible rather than accepting
text whose delivery phase is unknowable. `202 Accepted` for an interjection
means queued, not read; the surface keeps that distinction until the next
round boundary or terminal event.
Paste and attachment parity requires a typed input envelope: text, image or
file identity, media type, size, provenance, and any transformation performed
before send. A filename chip connected only to an old Gemini path is not
Souveraine multimodality. Until the substrate accepts and persists the input,
the Panel must call the feature unavailable rather than imply delivery.
Keyboard, touch, and pointer must all reach the same acts. Focus returns to the
composer after a menu choice; Page Up and Page Down move the transcript; tool
payload scrolling and text selection do not accidentally send or collapse a
card. Small icon-only hit targets need tooltips and at least the shell's normal
touch target.
## Context, reflection, and parity
Feature parity means parity with Souveraine's domain, not a pixel copy of the
TUI and not inherited buttons from ii. Both surfaces should be able to expose:
- actual used tokens, model limit, and pressure as separate values;
- the memory budget and the compaction that changed it;
- primary completion followed by subconscious activity;
- reflection/audit records without leaking them into agent speech;
- the current itinerary and commitment links;
- correlated tool calls, returns, duration, and failure;
- server health, cancellation, interjection, and reconnection;
- conversation creation, discovery, resume, and fork;
- typed multimodal inputs once the substrate owns them.
These may open as drawers, menus, expanded cards, or a dedicated secondary
Panel. They must consume the same server facts. The TUI is useful evidence of a
working domain action; it is not a second specification and its screen-local
state does not become the QML contract.
## Ownership migration
The borrowed `AiChat.qml` currently owns list lifecycle, focus, and composer
geometry. Souveraine-owned `AgentMessage`, `ThinkingCard`, `ToolCard`,
`AgentPaneMenu`, and `ItineraryRibbon` already own the semantics drawn inside
it. The next structural cut is an owned Panel host and conversation viewport
that consume the same service without copying the adapter's state.
That cut should be rehearsed beside the live pane, then swapped once. A QML
write-reload loop over the only working chat surface turns each syntax fault
into a session-wide interruption and can abandon a live turn. Candidate and
live panes may share a server and a conversation only if one is read-only;
they must never become concurrent writers disguised as visual testing.
## Proof boundary
Panel work has distinct proof layers:
1. Source: Rust formatting, QML parsing, lint, manifest coverage, and focused
projection tests are clean.
2. CI: Gitea compiles and tests the Rust/QML package path on ArchDev and emits
the versioned artifact.
3. Installed: the package or development compose points at the intended source
revision and QuickShell loads it without binding or component errors.
4. On glass: the footer menu controls only Souveraine agents; observer rows are
inert; a long tool body scrolls; set/advance/clear updates the ribbon; a
failed itinerary remains in chat; switching, resume, cancel, and interject
have the states their labels claim.
A parsed QML file is not on-glass proof. A green Rust job is not an installed
surface. A hot reload that leaves the previous pane drawn is not success. Keep
those borders sharp; the Panel is where hidden ambiguity becomes something
Casey has to touch.