saf: give the panel's next cuts an owner
This commit is contained in:
parent
2bd61ec4b3
commit
184a449731
7 changed files with 441 additions and 12 deletions
|
|
@ -24,6 +24,8 @@ How Souveraine is built, and why. For whoever reads it, human or agent. When thi
|
|||
- [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.
|
||||
- [agent presence](surface/05-agent-presence.md). One observed session projection, kept rigorously separate from control and permission.
|
||||
- [surface lifetimes](surface/06-surface-lifetimes.md). A small resident shell, optional workers that truly leave memory, and proof before a rewrite.
|
||||
|
||||
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.
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,10 @@ 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.
|
||||
|
||||
[Agent presence](05-agent-presence.md) owns the collector contract, provider
|
||||
health, activity heuristics, and the future path toward one session service.
|
||||
This chapter owns only how that projection enters the conversation surface.
|
||||
|
||||
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
|
||||
|
|
@ -178,6 +182,10 @@ geometry. Souveraine-owned `AgentMessage`, `ThinkingCard`, `ToolCard`,
|
|||
it. The next structural cut is an owned Panel host and conversation viewport
|
||||
that consume the same service without copying the adapter's state.
|
||||
|
||||
Moving the Panel out of the resident shell is a separate lifetime boundary,
|
||||
not a reason to fork its conversation model. [Surface lifetimes](06-surface-lifetimes.md)
|
||||
owns that extraction and its memory/fault proof.
|
||||
|
||||
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
|
||||
|
|
|
|||
224
saf/surface/05-agent-presence.md
Normal file
224
saf/surface/05-agent-presence.md
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
# Agent presence
|
||||
|
||||
Agent presence answers one narrow question: **which agent sessions can this
|
||||
machine currently observe?** It does not decide which agent a conversation
|
||||
controls, approve a tool on another process's behalf, or turn a terminal
|
||||
session into a model behind the Panel.
|
||||
|
||||
That distinction matters most where the UI is smallest. A Codex process can be
|
||||
running beside Souveraine while the Panel composer still addresses only the
|
||||
selected Souveraine agent. Showing both is useful. Making them look equally
|
||||
selectable is a lie.
|
||||
|
||||
## Three different facts
|
||||
|
||||
Keep these independent even when one menu draws them together:
|
||||
|
||||
- **controlled agent** — the Souveraine agent selected through `/v1/agents`;
|
||||
the Panel can create or resume its conversations and send it a turn;
|
||||
- **observed session** — a recent Souveraine, Claude Code, or Codex record found
|
||||
by the local collector; it may be active, recent, or idle, but observation
|
||||
grants no action;
|
||||
- **provider health** — whether a provider's source was actually inspected.
|
||||
Unavailable is not the same as available with no sessions.
|
||||
|
||||
The controlled agent is a server relationship. Observed activity is a local
|
||||
projection. Provider health is evidence about that projection. Do not infer
|
||||
one from another.
|
||||
|
||||
## What exists now
|
||||
|
||||
The current path is deliberately small and transitional:
|
||||
|
||||
1. `scripts/agent/agent-sessions.sh` inspects recent local records for
|
||||
Souveraine, Claude Code, and Codex and emits one JSON envelope.
|
||||
2. `services/AgentSessions.qml` runs that collector on one cadence, retains the
|
||||
last good envelope, and projects sessions, provider health, active count,
|
||||
staleness, and compact labels.
|
||||
3. `AgentPaneMenu.qml` shows Claude Code and Codex sessions beneath
|
||||
**Running elsewhere · observed only**. Those rows are inert.
|
||||
|
||||
The shell also retains `modules/souveraine/island/Island.qml` and
|
||||
`IslandExpansion.qml`, written against the same service. They are not mounted
|
||||
in the current bar or Panel. They are reusable observer components, not a
|
||||
second live presence surface and not a reason to put agent chrome back above
|
||||
the conversation.
|
||||
|
||||
They are also not abandoned. Their next proper home is the phone's later
|
||||
dynamic top-bar system, once the bar can host more than a fixed row of widgets.
|
||||
The present Panel move lets that larger work wait without making the Island
|
||||
components disposable.
|
||||
|
||||
This is source and current composed-surface behavior. It is not the proposed
|
||||
Rust session daemon. The collector still starts a shell process from QML on
|
||||
each poll.
|
||||
|
||||
## The envelope contract
|
||||
|
||||
Transport may change; these meanings must not:
|
||||
|
||||
- `sessions` is newest first and contains provider identity, stable session
|
||||
identity, a useful label source, last activity, activity state, and only the
|
||||
usage fields the provider actually supplied;
|
||||
- `providers` reports availability, errors, source-specific limits, and the
|
||||
number of sessions admitted from each source;
|
||||
- `active` counts records whose observed activity falls inside the active
|
||||
window;
|
||||
- `ts` says when the envelope was collected, not when every underlying source
|
||||
was updated;
|
||||
- a failed provider degrades inside the envelope instead of aborting the other
|
||||
providers;
|
||||
- a failed whole refresh keeps the last good model and marks it stale instead
|
||||
of replacing it with an authoritative-looking empty list.
|
||||
|
||||
Unknown is a real value. The current shell uses `-1` for an unavailable token
|
||||
measurement and renders an em dash, never zero. Souveraine now persists
|
||||
per-turn usage with assistant messages, but `conversation.json` does not expose
|
||||
an aggregate for the collector, so the presence projection still reports
|
||||
Souveraine session usage as unknown. That projection may be improved; the UI
|
||||
must not manufacture a total in the meantime.
|
||||
|
||||
Claude's local usage is read from a bounded recent tail and is labelled as
|
||||
windowed. Codex's latest token-count record is cumulative for that rollout.
|
||||
Those numbers are not interchangeable merely because both fit in one field.
|
||||
Any combined view must retain the source semantics or omit the comparison.
|
||||
|
||||
## Activity is a heuristic, not cognition
|
||||
|
||||
The present collector derives:
|
||||
|
||||
- `active` from activity within two minutes;
|
||||
- `recent` from activity within one hour;
|
||||
- `idle` from older admitted records.
|
||||
|
||||
These are filesystem/event-age classes. They do not prove that an inference is
|
||||
running, a tool is still alive, an agent is waiting for Casey, or a response is
|
||||
complete. In particular, there is no synthetic `waiting` state. Waiting for a
|
||||
permission decision requires a real request lifecycle; timestamps cannot stand
|
||||
in for it.
|
||||
|
||||
The Panel's own active-turn, primary/subconscious phase, cancellation, and
|
||||
terminal event must continue to come from the Souveraine stream described in
|
||||
[the agent panel](04-agent-pane.md). Presence may corroborate that state. It
|
||||
must not overwrite it.
|
||||
|
||||
## Where presence belongs
|
||||
|
||||
The close conversation surface puts observed external sessions inside the
|
||||
footer's agent menu because that is where mistaken control is easiest to
|
||||
prevent: selectable Souveraine agents first, an explicit divider, then inert
|
||||
external rows.
|
||||
|
||||
A bar, island, overview, or diagnostics page may project the same service when
|
||||
the device has room and the projection answers a real glance question. On a
|
||||
phone, permanent agent-session chrome competes with system state and was
|
||||
rejected on glass. Absence from the bar is therefore a product decision, not a
|
||||
collection failure.
|
||||
|
||||
No surface may:
|
||||
|
||||
- label a discovered process as controllable without an admitted action path;
|
||||
- show unavailable providers as zero sessions;
|
||||
- imply that recency means work is still running;
|
||||
- expose conversation text, prompts, repository paths, or tool payloads merely
|
||||
to make presence richer;
|
||||
- start its own provider poll when `AgentSessions` already owns the projection.
|
||||
|
||||
## The later dynamic bar
|
||||
|
||||
The phone's top bar should ultimately be an extensible host whose components
|
||||
can enter, compact, expand, yield, and leave according to live state. Agent
|
||||
presence is one such component, beside notifications, media, connectivity,
|
||||
power, privacy, and other device facts. A collection of independent widgets
|
||||
all changing width is not that system.
|
||||
|
||||
Before the Island is mounted again, the bar needs a shared contract for:
|
||||
|
||||
- component identity, priority, disclosure class, and device eligibility;
|
||||
- resting, compact, attention, expanded, and hidden forms;
|
||||
- one arbiter for horizontal space and collisions;
|
||||
- preemption and return when a higher-priority event arrives;
|
||||
- dwell, decay, and animation timing so rapid events do not thrash geometry;
|
||||
- tap, long-press, dismiss, and expansion ownership;
|
||||
- lock-state redaction and a safe minimal form;
|
||||
- a stable overflow or secondary surface when several components deserve
|
||||
attention at once;
|
||||
- component failure that removes one projection without taking down the bar.
|
||||
|
||||
`Island.qml` and `IslandExpansion.qml` are useful first clients for that host:
|
||||
they already separate a compact morph from expanded detail and consume the
|
||||
shared presence projection. They do not yet define the host protocol, arbitrate
|
||||
against neighbours, or prove the phone form on glass. The dynamic bar is a
|
||||
larger surface task carried here until it has enough implementation scope to
|
||||
need its own SAF chapter; the old fixed bar placement is not its prototype by
|
||||
default.
|
||||
|
||||
## Permission is a different authority
|
||||
|
||||
The earlier island exploration considered Claude hook installation and
|
||||
Allow/Deny controls. None of that is present in Souveraine's current path. The
|
||||
collector does not edit `~/.claude/settings.json`; there is no permission
|
||||
bridge; observer rows do not jump to or command external terminals.
|
||||
|
||||
If permission control is built later, it needs more than an attractive card:
|
||||
|
||||
- the request must name the exact provider session, tool, arguments, and age;
|
||||
- the producer and target must be authenticated strongly enough for the
|
||||
consequence of the action;
|
||||
- allow-once, deny, expiry, cancellation, and duplicate delivery need explicit
|
||||
states;
|
||||
- the decision and its delivery result must enter the audit trail;
|
||||
- provider failure must fall back to that provider's normal safe behavior;
|
||||
- a broad bypass or allow-all posture requires a separately admitted policy,
|
||||
not a more convenient button.
|
||||
|
||||
Until that path is owned and attested, presence stays read-only.
|
||||
|
||||
## The intended service
|
||||
|
||||
One long-lived session service remains the destination. It should replace the
|
||||
poll subprocess, not change the envelope under every surface. The proposed
|
||||
Unix socket at `$XDG_RUNTIME_DIR/souveraine-sessions.sock` is a candidate
|
||||
transport, not proof that the daemon exists.
|
||||
|
||||
The service needs:
|
||||
|
||||
- one cadence and one adapter per provider;
|
||||
- event-driven updates where a provider supports them, with bounded scans as a
|
||||
fallback;
|
||||
- provider-specific provenance and measurement semantics preserved in the
|
||||
normalized record;
|
||||
- last-good/stale behavior at both provider and whole-envelope level;
|
||||
- stable session identity across refreshes;
|
||||
- bounded resource use and no transcript-content collection merely for a
|
||||
presence label;
|
||||
- an authenticated, separate action channel if external control is ever
|
||||
admitted.
|
||||
|
||||
Souveraine's own session and usage facts should eventually be projected from
|
||||
the substrate directly rather than rediscovered through files written for
|
||||
persistence. Claude and Codex adapters remain local observers unless their
|
||||
providers expose a stronger owned event path.
|
||||
|
||||
## Proof boundary
|
||||
|
||||
Presence work is proven in layers:
|
||||
|
||||
1. **source** — each provider parser is fixture-tested, malformed timestamps do
|
||||
not become plausible epoch-zero state, and provider failure still emits a
|
||||
valid envelope;
|
||||
2. **projection** — one service retains last-good state, marks staleness, and
|
||||
preserves unknown/windowed/cumulative measurement semantics;
|
||||
3. **surface** — external sessions render under an explicit observed-only
|
||||
boundary and cannot be selected or controlled;
|
||||
4. **installed** — the versioned package owns the collector/service/component
|
||||
files used by the running shell;
|
||||
5. **live provider** — a real session enters and leaves each activity class,
|
||||
and a deliberately unavailable provider is visibly different from an empty
|
||||
one;
|
||||
6. **authority**, if ever added — an attested decision reaches only the named
|
||||
request and is recorded end to end.
|
||||
|
||||
The current implementation has the polling envelope and Panel projection. The
|
||||
Rust daemon, aggregate Souveraine usage projection, provider permission path,
|
||||
and cross-provider control are not yet true.
|
||||
194
saf/surface/06-surface-lifetimes.md
Normal file
194
saf/surface/06-surface-lifetimes.md
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
# Surface lifetimes
|
||||
|
||||
The shell needs a memory budget and failure boundaries, not a superstition
|
||||
about which UI language is pure. Permanent state, optional presentation, and
|
||||
authority must have named owners and observable lifetimes. If closing a heavy
|
||||
surface leaves its objects and pages resident in the only process holding the
|
||||
lock screen, the surface did not meaningfully go away.
|
||||
|
||||
## Evidence that opened this question
|
||||
|
||||
The daily-driver phone has 3.5 GB RAM. Three measurements from August 2026
|
||||
frame the risk without pretending to be a fresh baseline:
|
||||
|
||||
- QuickShell reached about 610 MB RSS during the Live2D experiment;
|
||||
- the summoned WebKit face added about 283 MB RSS in its own process;
|
||||
- a separate dynamic-QML/per-stream rendering storm drove QuickShell to about
|
||||
952 MB anonymous RSS before the kernel killed it.
|
||||
|
||||
These observations do not prove that ordinary QML costs 952 MB, that WebKit is
|
||||
always too expensive, or that a Rust surface is automatically small. They do
|
||||
prove that an optional renderer can consume a material fraction of the phone's
|
||||
memory and that a storm inside the resident QuickShell process shares a
|
||||
failure domain with essential session UI.
|
||||
|
||||
The numbers must be remeasured against exact package revisions and process
|
||||
trees before they become a budget.
|
||||
|
||||
## The resident core
|
||||
|
||||
The resident shell keeps only surfaces and connections that must survive the
|
||||
loss of optional presentation:
|
||||
|
||||
- sessiond heartbeat and the secure session-lock handoff;
|
||||
- the lock surface and lock-tier disclosure gate;
|
||||
- display, idle, and device-state projections needed to remain safe;
|
||||
- the small gesture and power paths required to recover or close a surface;
|
||||
- a minimal system-status/recovery indication.
|
||||
|
||||
The resident core is not a second authority. Sessiond remains the decision
|
||||
owner for lock, power, and admitted device action; the shell renders its state
|
||||
and carries named requests. [Session start](02-session-start.md) owns the
|
||||
package and systemd graph that brings compositor, authority, and shell up in
|
||||
order.
|
||||
|
||||
The secure lock path must not be conditionally absent while a shell reload is
|
||||
in flight. Optional-surface work may not reopen the already-known race where a
|
||||
new shell requests a lock before the old holder has transferred or released
|
||||
it.
|
||||
|
||||
## Optional means separately disposable
|
||||
|
||||
The agent Panel, Live2D presence, web content, large explorers, and future
|
||||
special renderers are optional surfaces. Each needs:
|
||||
|
||||
- a named package and executable or explicit in-process owner;
|
||||
- one startup trigger and one authority that may request presentation;
|
||||
- a shutdown rule after close, idle, lock transition, or resource pressure;
|
||||
- a state restoration key whose authority lives outside the renderer;
|
||||
- a disclosure class and locked-state behavior;
|
||||
- a resource line measured while closed, opening, active, and closing;
|
||||
- a supervisor policy and a visible failure event.
|
||||
|
||||
A QML `Loader` is useful for avoiding construction and destroying object
|
||||
graphs. It is not proof that allocator pages returned to the operating system,
|
||||
and it cannot stop a rendering storm from killing the process that owns it.
|
||||
Call that path lazy only after object lifetime is verified; call memory
|
||||
released only after the OS measurement falls.
|
||||
|
||||
For expensive surfaces, a supervised process is the preferred boundary. When
|
||||
the process exits, its private memory and faults leave with it. The resident
|
||||
shell may retain a small launch/status projection, never the heavy renderer's
|
||||
state graph.
|
||||
|
||||
## The Panel is the first extraction
|
||||
|
||||
The agent Panel is the strongest first worker candidate because its authority
|
||||
already lives elsewhere:
|
||||
|
||||
- the Souveraine server owns agents, conversations, messages, itinerary,
|
||||
cancellation, interjection, and replay;
|
||||
- sessiond owns session/lock/device authority;
|
||||
- the Panel owns presentation, input collection, transient selection, and
|
||||
viewport state.
|
||||
|
||||
An extracted `souveraine-agent-surface` must therefore consume the same typed
|
||||
server contract described in [the agent panel](04-agent-pane.md). It must not
|
||||
copy conversations into a new local authority or become a second writer during
|
||||
candidate testing.
|
||||
|
||||
Its lifecycle should be explicit:
|
||||
|
||||
1. an unlocked, admitted presentation request launches the version selected by
|
||||
the installed surface package;
|
||||
2. the worker authenticates to the local Souveraine endpoint and attaches only
|
||||
after Casey selects or explicitly resumes a conversation;
|
||||
3. close detaches presentation and gives the worker a short bounded grace for
|
||||
non-authoritative UI state;
|
||||
4. the supervisor ends the worker after that grace;
|
||||
5. reopen reconstructs from substrate truth and may restore harmless viewport
|
||||
state separately;
|
||||
6. lock immediately hides Personal-class content and follows the declared
|
||||
stop/blank policy even if the worker is stuck.
|
||||
|
||||
During an active stream, a killed worker must not cancel the server turn unless
|
||||
Casey issued cancel. The substrate continues or records its terminal state;
|
||||
reopening reattaches and backfills. Conversely, a worker must not remain alive
|
||||
forever merely because a server stream forgot to close. Presentation lifetime
|
||||
and inference lifetime are related state machines, not one process lifetime.
|
||||
|
||||
## Fault containment
|
||||
|
||||
The resident shell needs a small supervisor projection for each optional
|
||||
surface: stopped, starting, ready, active, stopping, failed, and backoff. A
|
||||
crash becomes an event with the package version and last lifecycle edge. It
|
||||
must not silently relaunch in a tight loop or take the session lock with it.
|
||||
|
||||
The decisive fault tests are physical:
|
||||
|
||||
- kill the Panel during streamed text and during a large tool return;
|
||||
- feed a candidate QML version that fails to load;
|
||||
- wedge the worker so it ignores close;
|
||||
- lock while Personal-class content is visible;
|
||||
- exhaust the worker's memory budget without exhausting the resident core.
|
||||
|
||||
In every case the lock surface, power path, sessiond heartbeat, and recovery
|
||||
control must remain available. A screenshot after restart is not fault proof.
|
||||
|
||||
## Versioned delivery on every body
|
||||
|
||||
The laptop and phone receive published packages from Gitea. A surface split is
|
||||
complete only when the package graph installs compatible versions and the
|
||||
systemd/session start path selects them consistently. Hand-placing a worker on
|
||||
one device proves a diagnosis at most; it is not the fix future installs will
|
||||
receive.
|
||||
|
||||
The core and optional-surface protocol must be versioned. Compatibility needs
|
||||
an explicit range or handshake so a newer Panel cannot silently interpret an
|
||||
older substrate event shape. Package upgrade stages a candidate; adoption and
|
||||
restart remain explicit where a live conversation or lock handoff could be
|
||||
disturbed. The last working surface stays available until the candidate reaches
|
||||
ready state.
|
||||
|
||||
Gitea Actions and ArchDev own compilation and package artifacts. Local checks
|
||||
may parse QML, format Rust, validate manifests, and exercise fixtures; they do
|
||||
not replace the package result.
|
||||
|
||||
## Native Rust is a measured option
|
||||
|
||||
A Rust Wayland client could replace some or all of QuickShell, but it still has
|
||||
to own layer-shell/session-lock protocols, rendering, text, input, animation,
|
||||
accessibility, service integration, and recovery. Rust does not remove the
|
||||
need for resident/optional process boundaries, and a native screenshot says
|
||||
nothing about steady memory or fault behavior.
|
||||
|
||||
The useful spike is deliberately small: one native layer-shell card, one typed
|
||||
sessiond-fed state, one package, and measurements for RSS/PSS, launch latency,
|
||||
and failure. It does not take the lock, replace the shell, or grow a parallel
|
||||
settings authority. Compare it against the extracted QuickShell worker under
|
||||
the same workload before choosing a rewrite.
|
||||
|
||||
## Measurement record
|
||||
|
||||
Every budget line should name:
|
||||
|
||||
- device and available RAM;
|
||||
- source commit, package version, and protocol version;
|
||||
- process tree and which surfaces are constructed;
|
||||
- RSS and PSS after a defined settle time;
|
||||
- peak during a defined action, including a representative streamed tool turn;
|
||||
- memory after close and after worker exit;
|
||||
- launch-to-ready latency;
|
||||
- result of the relevant kill/lock/reopen test.
|
||||
|
||||
Measure at least resident idle, Panel closed, Panel open and idle, active text
|
||||
stream, large tool payload, Live2D closed/open, and post-close. A single `ps`
|
||||
line without surface state is anecdote, not a budget.
|
||||
|
||||
## Proof and present boundary
|
||||
|
||||
The path is complete only when:
|
||||
|
||||
1. the resident and optional sets are explicit in source and packages;
|
||||
2. closing the Panel ends its worker and the OS-visible memory leaves;
|
||||
3. killing or wedging that worker leaves lock, heartbeat, power, and recovery
|
||||
alive;
|
||||
4. locked-state disclosure is exercised, not inferred from a conditional;
|
||||
5. reopen restores the authoritative conversation without duplicating a turn;
|
||||
6. Gitea emits the versioned core and surface packages used by both laptop and
|
||||
phone;
|
||||
7. a Rust rewrite decision, if made, cites comparable measurements.
|
||||
|
||||
None of that extraction is true yet. The current Panel remains inside the
|
||||
resident QuickShell process; the August numbers are historical observations;
|
||||
and the Rust path remains a bounded experiment, not an adopted shell plan.
|
||||
|
|
@ -132,9 +132,9 @@ Rectangle {
|
|||
}
|
||||
|
||||
// "—" where a provider does not report tokens. Rendering a
|
||||
// 0 would be a measurement claim we cannot back: the
|
||||
// Souveraine substrate records TokenUsage but nothing calls
|
||||
// assistant_with_usage yet (TASK-67). An em dash is the
|
||||
// 0 would be a measurement claim we cannot back. The
|
||||
// substrate persists per-turn usage, but this presence
|
||||
// projection does not aggregate it. An em dash is the
|
||||
// honest glyph for "not measured".
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
|
|
|||
|
|
@ -136,11 +136,12 @@ codex_json() {
|
|||
|
||||
# ------------------------------------------------------------ souveraine ----
|
||||
# ~/.souveraine/server/agents/<agent>/conversations/<conv>/conversation.json
|
||||
# carries updated_at + message_count. It does NOT carry token usage: the
|
||||
# substrate records `TokenUsage` but `assistant_with_usage` has zero callers
|
||||
# (TASK-67). We report -1 for tokens rather than 0, because 0 is a measurement
|
||||
# and -1 is an admission. The day TASK-67 lands, this starts reporting real
|
||||
# numbers and nothing else has to change.
|
||||
# carries updated_at + message_count. It does NOT carry aggregate token usage:
|
||||
# the substrate persists per-turn `TokenUsage` in its message record, while
|
||||
# this bounded presence scan intentionally reads only conversation metadata.
|
||||
# We report -1 rather than 0, because 0 is a measurement and -1 is an
|
||||
# admission. A direct substrate projection can fill the fields later without
|
||||
# changing the surface contract.
|
||||
souveraine_json() {
|
||||
local dir="$HOME/.souveraine/server/agents"
|
||||
[ -d "$dir" ] || { echo '{"available":false,"error":"no ~/.souveraine"}'; return; }
|
||||
|
|
|
|||
|
|
@ -113,10 +113,10 @@ Singleton {
|
|||
}
|
||||
|
||||
// Total tokens for a session, or -1 when the provider genuinely does not
|
||||
// report them. -1 is deliberate: the Souveraine substrate records TokenUsage
|
||||
// but `assistant_with_usage` has zero callers (TASK-67), so a 0 here would
|
||||
// be a measurement claim we cannot back. Surfaces must render -1 as "—",
|
||||
// never as zero.
|
||||
// report them. -1 is deliberate: Souveraine persists per-turn TokenUsage,
|
||||
// but the conversation.json metadata inspected by the presence collector
|
||||
// does not aggregate it. A 0 here would be a measurement claim we cannot
|
||||
// back. Surfaces must render -1 as "—", never as zero.
|
||||
function sessionTokens(s) {
|
||||
if (!s)
|
||||
return -1;
|
||||
|
|
|
|||
Loading…
Reference in a new issue