diff --git a/README.md b/README.md index e4bdcb5..0bacce2 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ boundary - without an opinion about what the agent is for. A setup is one holds the primitives, [beaver-agent](https://git.kotikot.com/beaver/beaver-agent) is a full setup built on them. +![The admin: what is in motion, what waits for an answer, the context and the quotas, the graph around the master](preview/shots/admin-now.webp) + ## What it gives a setup - **agents/** - what an agent is: a Claude agent with a prompt per @@ -32,7 +34,13 @@ is a full setup built on them. - **storage/** - SQLModel tables and the Postgres session store. `config.py` defines `Gateway` and loads a setup; `app.py` builds the runtime -from it; `cli.py` is the entrypoint. Every string the gateway puts in front of +from it; `cli.py` is the entrypoint. The admin SPA in `ui/` is the browser +window on all of it: conversations by day, the thread with the tree of tool +calls, the context of the last turn, memory, jobs, usage, tokens and the audit. + +![A conversation in the admin: the thread, the queue, the details](preview/shots/admin-thread.webp) + +![The Context tab: what the model held on the last turn - harness, prompt granules, skills, the notes the thread reached](preview/shots/admin-context.webp) Every string the gateway puts in front of a model or a user has an English default and is overridable (`ConversationTexts`, `TelegramTexts`). diff --git a/preview/shots/admin-context.webp b/preview/shots/admin-context.webp new file mode 100644 index 0000000..69fbd8c Binary files /dev/null and b/preview/shots/admin-context.webp differ diff --git a/preview/shots/admin-now.webp b/preview/shots/admin-now.webp new file mode 100644 index 0000000..557b063 Binary files /dev/null and b/preview/shots/admin-now.webp differ diff --git a/preview/shots/admin-thread.webp b/preview/shots/admin-thread.webp new file mode 100644 index 0000000..13b122a Binary files /dev/null and b/preview/shots/admin-thread.webp differ diff --git a/ui/src/lib/panel/conversation-view.svelte b/ui/src/lib/panel/conversation-view.svelte index 90f4b50..e4587b0 100644 --- a/ui/src/lib/panel/conversation-view.svelte +++ b/ui/src/lib/panel/conversation-view.svelte @@ -18,6 +18,7 @@ onOpen, showTitle = true, initialView = "chat", + onViewChange, }: { client: ApiClient; id: string; @@ -27,6 +28,8 @@ showTitle?: boolean; // "activity" beside a deep chat's own note: the file is the thread. initialView?: string; + // The page keeps the open tab in the URL so it survives a reload. + onViewChange?: (view: string) => void; } = $props(); const host = usePanelHost(); @@ -50,6 +53,10 @@ historyKey += 1; } + $effect(() => { + onViewChange?.(view); + }); + $effect(() => { const { running } = feed.model; if (!running) { diff --git a/ui/src/routes/conversations/[id]/+page.svelte b/ui/src/routes/conversations/[id]/+page.svelte index 6630ee2..c57976a 100644 --- a/ui/src/routes/conversations/[id]/+page.svelte +++ b/ui/src/routes/conversations/[id]/+page.svelte @@ -1,5 +1,5 @@ Conversation ยท Beaver @@ -17,7 +33,9 @@ client={session.client} {href} {id} + {initialView} onOpen={(target) => goto(href(target))} + onViewChange={remember} /> {/key} {/if}