Speaks the Claude Code subscription wire protocol against api.anthropic.com
using the OAuth creds claude already stored, so Souveraine can drive a
Claude.ai subscription directly alongside Bifrost/openai-oauth. Translates
OpenAI-shaped internal requests to/from Anthropic /v1/messages, applies the
subscription shaping, refreshes the OAuth token under a mutex.
tuie now cloned from Fimeg/tuie (rgb patch committed) at a pinned SHA
instead of upstream HEAD — no floating ref decides if we build.
aarch64-artifact job runs on the new archdev host-mode runner, cross-
builds against ~/aarch64-sysroot (linker --sysroot flag proven to find
aarch64 libmvec), publishes the binary to a rolling edge prerelease
when CI is green. Phone updates itself from there.
Both live on Overlay; z-order was creation order, so an ii restart buried
the pill under the dock and it stopped taking touch. The pill now has
namespace quickshell:pill; a matching 'order = 1' hl.layer_rule in the
phone's hyprland.lua keeps it on top across restarts (rule is phone
config, not surface tree).
- dock icons center in the bar (DockApps top-margin was shoving the row
down; DockAppButton block now reserves half the dot strip)
- pill double-tap fullscreens the real app: routes through dock IPC
fullscreen() targeting Hyprland.activeToplevel.address, mode 0, so the
pill tap focusing the shell no longer redirects it
- pill must be re-created after the dock or it sits under it and drops
touch (both on Overlay; z-order = creation order)
- HOW-IT-WORKS.md: the concise contract for pill/dock/fullscreen
Both were stranded untracked in the Pixel3Arch tree; they source from
this repo, so they live here. deploy-phone.sh ships the cross-built
binary + user unit (seed-id excluded, machine binding stays doctrine);
PKGBUILD builds from a synced local checkout, no network fetch.
Dock fan-out stacks, drag-to-combine, pill gesture rewrite, and the ii
patch set (TaskbarApps stacks API, Config dock.stacks schema) — pulled
from the live phone and made canonical here. deploy.sh grew a manifest
and a --phone mode: rsync the surface over, symlink ii into it, so live
edits land in a git tree instead of drifting.
ToolCard styled the status glyph with a hardcoded byte offset 0..3, but
✓/✗/⟳ are 3-byte chars sitting at bytes 2..5 of " {glyph}". The split
landed mid-char and panicked at render in tuie's style slicing. Compute
the glyph region as 2 + glyph.len_utf8() instead.
Then took cargo clippy -- -D warnings from 312 failures to clean:
- scoped #![allow(dead_code)] on WIP scaffolding (federation, sensorium,
gitea_memory, model_router, session, subagent…); gate stays live on
active code so new orphans still fail
- scoped #![allow(deprecated)] on the legacy ratatui render path, marked
pending removal at tuie parity — no migration on code we're deleting
- declare the gui feature (forwards to tuie/gui) — the cfg was real intent
- real fixes: duplicate SaveAndGoBack arm + dead Err arm, base64::encode,
4 unused imports, dead assignment, private-type leak, dedup'd if/else
branches, manual clamp/strip, &PathBuf→&Path, collapsible matches
agents: wire the rest of the detail widgets into the section children so
PRIMARY badge etc actually render; drop the HARDCODED debug text.
settings: test needs a provider in the map before navigating to Providers
cat.
clippy: pin CI to rust 1.94 per Cargo.toml rust-version; also apply what
cargo clippy --fix could auto-fix for 1.96 compat.
new TUI work that had been living in a detached re-init'd checkout, replayed
onto the public lineage. keeps CI + skills. src-only; local scaffolding stays local.
Dep bump swapped StyledText's public .text field for as_ref/as_str
accessors and set_fg/set_bg/drop_end mutators; Theme now built from
(fg, bg, [16] indexed) instead of named slots. Mechanical port across
the widget set plus an ANSI-index mapping of the chat palette.
Vanguard gets his own inference pipe. z.ai GLM Coding Plan, /paas/v4.
BifrostClient learned to leave versioned URLs alone. ProviderRegistry
dispatches per agent; every engine asks who it's talking to before
dialing. Annie and Hal none the wiser.
- Replace unsafe raw-pointer render callback in message_list with Arc<Mutex<>>
- Replace unsafe Cell::as_ptr() in splash with safe Copy derive + .get()
- Add .max(0.0) guards on all f32→u16 casts in bloom animation
- Replace unreachable!() with bail/return/match in 4 locations
- Replace expect() with Result propagation in 4 constructors (RemoteBackend, VoiceClient, BifrostClient, OpenAiOAuth)
- Wire manager kill/restart keys to signals (backend TODO remains)
- Pass agent name through TUI message render chain (was hardcoded "Ani")
- Implement scroll_page in message_list using List::scroll_by
14 widgets now match the demo source exactly (import paths rewritten):
accordion, button, checkbox, counter, flat_button, focus_pane,
global_chords, horizontal_rule, link, page_layout, point_picker,
progress_bar, radio_group, segmented_control.
Theme simplified: accent color is now Color::YELLOW, which
apply_atmosphere resolves through harmonious to the agent's
current primary — no thread-local, no RGB arithmetic.
Added chord_macro + axis2d deps matching tuie-demo's Cargo.toml.
All 27 broken call sites (field_grid, settings, cockpit,
dropdown, model_picker, text_editor) migrated to demo APIs:
FlatButton::new().child(x), Button::new().children([x]),
Checkbox::new(label).set_checked(b), Counter::new(),
PointPicker::new().point(...), etc.
Replace the flat org-grouped list with a real tree. Sources and orgs
collapse (arrows expand/collapse, Enter toggles headers / selects models),
fuzzy search auto-expands matches, and the cursor opens pinned to the
currently-selected model's branch.
New tuie widgets (buttons, dropdown, slider, accordion, checkbox, etc.),
settings screen port with model picker + text editor, and screen ports
for chat, cron, presence, welcome, and the agents manager.
Introduce a parallel TUI built on the tuie widget toolkit, selectable at
runtime with `--engine tuie`. ratatui remains the default and its
crossterm/ratatui deps are retained, so default users see no behavior
change; this lands the full screen/widget tree for the new engine
side-by-side with the existing one.
Engine wiring:
- Cargo: add `tuie` (harmonious, images features)
- main.rs: `--engine` flag + run_tuie_tui() entry path
- src/ui: new `screens`, `widgets`, `theme`, `tuie_app` modules
- app::recent_commits made pub(crate) for dashboard reuse
Screens: splash (procedural bloom), welcome dashboard, chat (live
streaming), agents picker, settings, plus cron/presence stubs.
Widgets: brand_title, portrait, menu_list, message_list, chat_bubble,
chat_input, cockpit, phase_bar, tool_card, and a reusable `responsive`
container.
Responsive welcome: the welcome screen now offers two viewable modes the
way the old ratatui dashboard did — a side-by-side portrait/stats layout
at >=100 cols and a stacked single column below it — switched by the new
`Responsive` widget. It holds both subtrees and lays out / paints only the
one that fits, while exposing both to id lookups so the menu selection
survives a resize across the breakpoint. Flourishes: a breathing title
colour pulse and a portrait border that surfaces subconscious state.
Covered by a layout test driving a TestTerminal across the breakpoint.
PR #1 (LlmProvider + OAuth) fixes:
- Move provider selection from [bifrost].provider to [inference].provider
(Bifrost is a provider, not the parent category — they are peers)
- Add Inference category to settings TUI with provider picker
- Wire build_provider() into CLI/chat/model-refresh paths so OAuth
works outside server mode
- Update ServerConversation to use Arc<dyn LlmProvider> for consistency
PR #2 (web UI) assessment:
- Remove entire web/ directory — not aligned with substrate ethos
(client-side compaction model contradicts Constitution Article IV;
autoCommit toggle misunderstands git-backed memory physics;
vocabulary doesn't match project architecture)
- Keep the 3 new REST endpoints (config, compaction-logs, token metrics)
- Revert run_reflect path change (keep canonical ~/.souveraine/agents/)
- Delete souveraine_fixes.patch (dev artifact)
- Restore demo example (was commented out as workaround for missing file)
- Copy examples/demo.rs from primary branch (was never pushed to public)
Tests: 184 passed, 0 failed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce an `LlmProvider` trait (the engine<->LLM seam, sibling to the
`Backend` harness<->engine trait) so inference can route to providers
beyond the Bifrost gateway. Two impls behind it:
- `BifrostClient` - existing OpenAI-compatible gateway (default).
- `OpenAiOAuthProvider` - rides the Codex CLI's ChatGPT login
(`~/.codex/auth.json`) and drives `chatgpt.com/backend-api/codex/responses`
(Responses API) with no API key. Self-refreshes the token (single-flight,
write-back, CLI re-read fallback) and translates the engine's OpenAI-chat
request to/from the Responses API + SSE accumulation.
Selected via `[bifrost] provider` ("bifrost" | "openai-oauth"). The engine
keeps speaking the existing ChatCompletionRequest/CompletionResult/
InferenceStrain currency, so all six inference call-sites are unchanged -
only the field type flips to `Arc<dyn LlmProvider>`.
Model ids are translated at the provider boundary (oauth/catalog.rs::resolve):
Bifrost-namespaced ids (`openai/...`, `-precision`) map onto served ChatGPT
models; `-fast` -> priority service tier.
Verified live to the wire level: builds+links, server boots in oauth mode
(reads the Codex token), and chatgpt.com accepts the request (auth, endpoint,
headers, payload all valid). The SSE->CompletionResult accumulation is NOT yet
verified against a successful completion (blocked by a subscription usage limit
at test time) - needs one live turn to confirm end-to-end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the primary's pattern at `src/server/turn.rs:445-471` — bifrost
returns the full response in one shot, we chop it into 10-char chunks
and emit each as a `SubconsciousToken` with a 20ms sleep between, so
the subconscious appears to be typing in real time.
The TUI side needed a small refit so the chunks don't each become their
own line in the stream Vec (a 10-char-wide vertical waterfall):
- `ChatState.subconscious_current: String` — the live-building line.
Token chunks `push_str` here. The render path shows it as the
brightest bottom line, ahead of finalized history lines above.
- `SubconsciousToolCall`/`SubconsciousToolResult` flush the buffer to
`subconscious_stream` (as a finalized line), clear it, then push
their own line. New `SubconsciousPass(active=true)` clears both.
- Render packs the visible window from newest backward, including the
live line as the bottom entry; old history lines above fade upward
toward `agent_dim` and gain the `DIM` modifier in the upper third.
If the stream receiver is dropped mid-chunk, the inner loop bails out
but the round itself completes — bifrost is already done, only the UX
narration is interrupted.
TUI — subconscious live stream:
- Add `subconscious_stream: Vec<String>` to `ChatState`; events.rs already
pushes here via `SubconsciousToken`/`SubconsciousToolCall`/`SubconsciousToolResult`.
- Render below the phase bar during `TurnPhase::Subconscious`. Newest at
bottom in `palette.surfacing`; older lines fade upward toward
`palette.agent_dim` via `lerp_color`; the oldest visible line gets the
`DIM` modifier on top. Render path now builds `Vec<Line>` instead of
collapsing into `Line::from(spans)` (the latter forced everything onto
one horizontal row, which was the visible "1 2 3 across" bug). Lines
are width-clipped with an ellipsis fallback.
Seeds — journal/ledger routing:
- Aster mandate Phase 3 now records journal entries via the `memory`
tool at `journal/YYYY/MM/DD.md` (relative to memory root), never via
`write`/`edit`. Previous phrasing read as a filesystem path and the
model resolved it against cwd — when souveraine runs from the project
tree that landed `journal/` and `memory/ledger/` directly in the
source tree instead of the agent's memfs.
- "How I record" section likewise anchors ledger reads/writes to the
`memory` tool's `append`/`read`/`write` commands.
Cargo:
- 0.1.0 → 1.0.0.
- tower-http 0.5 → 0.6, notify 6 → 7, crossterm 0.27 → 0.28.
Bug docs (`docs/bugs.md`):
- B-012: TUI doesn't auto-refresh after tool calls complete (event
pipeline missing a redraw kick after `BackendEvent::ToolResult`).
- B-013: Tool calls stop working mid-conversation — model returns
`tool_calls=0` for the rest of the session, no error or log.
Suspected: recent `defs.rs` env-var refactor changing `retain`
behavior on env vars, possibly nuking tool defs during schema gen.
- B-014: Subagent `run_in_background: true` blocks primary — the flag
is parsed but never read; `Subagent::execute` unconditionally awaits;
`SubagentPool` is a stub.
Task queue:
- Split old `live-subconscious-stream.md` into two scoped task files:
`subconscious-live-reasoning-stream.md` (broader design — ticker,
cockpit event log, agency-driven surfacing) and
`subconscious-streaming-line-render.md` (this PR's narrow render
wiring).
- New tasks: `prompt-override-system.md` (move hardcoded seeds into a
project `prompts/` truth source the agent can override via
`souveraine_operations/`), `pasted-content-formatting.md` (visual
marker for pasted vs composed text).
- `CLAUDE.md` high-impact bullet: replace stale `rename-aster-to-subconscious`
with `mid-turn-subconscious-checkpoint.md` (matches the c1f851f redesign
that ripped out the watchdog in favour of `halt`/`intrusive` tools).
Public-branch prep:
- `docs/audit/external-security-audit-prompt.md` — scoped prompt for an
external creds/PII/license sweep of the `public` branch.
- `commit-history-public.txt` — full public-branch log dump used as
reference for that audit pass.
Replace real name, email, and gendered pronouns in doc comments with
neutral phrasing ("the user", "Souveraine Contributors", "TestUser").
Remove absolute home directory path from tool defs doc comment.
Add souveraine.toml to .gitignore so it won't be re-tracked.