Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.31.0 · main

Asking an agent

tier 2 — classes

The chat, extended for an agent's turn: what it thought, which tools it ran, an answer that streams in, where the answer came from, and what the reader can do with it next. The composer is sized for asking rather than chatting. Every word is the app's, and none of it needs a script.

An agent's turn

.chat-message--agent sets the answer as prose on the thread's ground rather than in a bubble: an agent's answer is read like a document, and a bubble around four paragraphs and a code block reads as a wall. The reader's own messages stay --mine bubbles. Inside the body, in order: details.reasoning, the .agent-steps it took, the answer, its sources, and what to do next.

.citation is a numbered button inline in the answer, opening a .popover on the source through popovertarget, so it needs no script. Give each an aria-label; the digit alone reads as a number. .citation-sources lists the same sources under the answer as .source-card links, with .source-card-n matching the citation.

.chat-feedback is the row under the answer: copy, regenerate, a rating with aria-pressed, and .chat-versions to page between regenerated answers. Its first glyph lines up with the text above it. .chat-quick-replies offers the likely next questions.

Ops agent 76k / 200k
Why did the EU-West order sync fail last night?
Ops agent
Thought for 4 s

The sync log for that window comes first. If the sync timed out, something else held the tables.

  1. Read 3 log files on build-runner-042.4 s
    02:14:07 sync  ERROR  timed out after 600 s waiting for lock on orders
  2. Queried src-db-14 for locks held over 60 s1 row · 0.8 s
    SELECT relation, mode, duration FROM lock_history WHERE duration > interval '60 s';

The sync timed out at 02:14 waiting for a lock on the orders table. The nightly vacuum on src-db-14 held it from 02:00 to 02:25.

Moving the vacuum to 03:30, after the sync window, would keep the two apart.

runbook.example.com

Order sync: timeouts and retries

build-runner-04

sync.log, 02:00 to 02:30

<div class="card" style="max-width: 680px">
    <div class="card-head">
        <span class="sedna-row sedna-gap-2"><span class="agent-avatar agent-avatar--sm" role="img" aria-label="Ops agent"></span><strong>Ops agent</strong></span>
        <span class="card-head-right sedna-row sedna-gap-2">
            <span class="context-ring" style="--value: 38" role="meter" aria-valuenow="38" aria-valuemin="0" aria-valuemax="100" aria-label="Context used"></span>
            <span class="text-muted text-sm text-nums">76k / 200k</span>
        </span>
    </div>
    <div class="card-body">
        <div class="chat" role="log" aria-live="polite" aria-label="Conversation with Ops agent">
            <div class="chat-message chat-message--mine">
                <div class="chat-body">
                    <div class="chat-bubble">Why did the EU-West order sync fail last night?</div>
                    <div class="chat-meta"><time class="chat-time" datetime="2026-09-14T09:12">09:12</time></div>
                </div>
            </div>

            <div class="chat-message chat-message--agent">
                <span class="agent-avatar" role="img" aria-label="Ops agent"></span>
                <div class="chat-body">
                    <div class="chat-meta"><span class="chat-author">Ops agent</span><time class="chat-time" datetime="2026-09-14T09:12">09:12</time></div>

                    <details class="reasoning">
                        <summary><i class="ri-arrow-right-s-line reasoning-chevron"></i><span>Thought for 4 s</span></summary>
                        <div class="reasoning-body"><p>The sync log for that window comes first. If the sync timed out, something else held the tables.</p></div>
                    </details>

                    <ol class="agent-steps">
                        <li data-state="done">
                            <span class="agent-step-icon"><i class="ri-check-line"></i></span>
                            <details class="agent-step">
                                <summary><span class="agent-step-label"><i class="ri-file-text-line"></i><span>Read 3 log files on build-runner-04</span></span><span class="agent-step-meta">2.4 s</span></summary>
                                <pre class="code-block"><code>02:14:07 sync  ERROR  timed out after 600 s waiting for lock on orders</code></pre>
                            </details>
                        </li>
                        <li data-state="done">
                            <span class="agent-step-icon"><i class="ri-check-line"></i></span>
                            <details class="agent-step">
                                <summary><span class="agent-step-label"><i class="ri-database-2-line"></i><span>Queried src-db-14 for locks held over 60 s</span></span><span class="agent-step-meta">1 row &middot; 0.8 s</span></summary>
                                <pre class="code-block"><code>SELECT relation, mode, duration FROM lock_history WHERE duration &gt; interval '60 s';</code></pre>
                            </details>
                        </li>
                    </ol>

                    <div class="chat-bubble">
                        <p>The sync timed out at 02:14 waiting for a lock on the orders table<button class="citation" type="button" popovertarget="turn-source-1" aria-label="Source 1">1</button>. The nightly vacuum on src-db-14 held it from 02:00 to 02:25<button class="citation" type="button" popovertarget="turn-source-2" aria-label="Source 2">2</button>.</p>
                        <p>Moving the vacuum to 03:30, after the sync window, would keep the two apart.</p>
                    </div>
                    <div class="popover" id="turn-source-1" popover>
                        <strong class="popover-title">runbook.example.com</strong>
                        <p>Order sync: timeouts and retries</p>
                    </div>
                    <div class="popover" id="turn-source-2" popover>
                        <strong class="popover-title">build-runner-04</strong>
                        <p>sync.log, 02:00 to 02:30</p>
                    </div>

                    <div class="citation-sources">
                        <a class="source-card" href="#">
                            <span class="source-card-host"><span class="source-card-n">1</span><span>runbook.example.com</span></span>
                            <span class="source-card-title">Order sync: timeouts and retries</span>
                        </a>
                        <a class="source-card" href="#">
                            <span class="source-card-host"><span class="source-card-n">2</span><span>build-runner-04</span></span>
                            <span class="source-card-title">sync.log, 02:00 to 02:30</span>
                        </a>
                    </div>

                    <div class="chat-feedback" role="toolbar" aria-label="Answer actions">
                        <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Copy"><i class="ri-file-copy-line"></i></button>
                        <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Regenerate"><i class="ri-refresh-line"></i></button>
                        <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Good answer" aria-pressed="true"><i class="ri-thumb-up-fill"></i></button>
                        <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Bad answer" aria-pressed="false"><i class="ri-thumb-down-line"></i></button>
                        <span class="chat-feedback-sep" aria-hidden="true"></span>
                        <span class="chat-versions">
                            <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Previous version"><i class="ri-arrow-left-s-line"></i></button>
                            <span>2 / 2</span>
                            <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Next version" disabled><i class="ri-arrow-right-s-line"></i></button>
                        </span>
                    </div>

                    <div class="chat-quick-replies">
                        <button class="chip" type="button">Draft the change</button>
                        <button class="chip" type="button">Show me the lock</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

While the answer streams

.chat-bubble--streaming puts a caret after the last block while text is still arriving. The app appends the text and removes the class when the answer is complete; mark the bubble aria-busy meanwhile, so a screen reader in the log waits for the whole answer rather than reading every word as it lands. An agent status under it says what the agent is doing.

Research agent

Most of the 214 tickets trace back to one failure: the 02:14 sync on orders-console-01. Of the rest, 31 are duplicates of the same report, and

Writing0:06
<div class="chat" role="log" aria-live="polite" aria-label="Conversation with Research agent" style="max-width: 600px">
    <div class="chat-message chat-message--agent">
        <span class="agent-avatar" data-tone="aurora" data-state="thinking" role="img" aria-label="Research agent"></span>
        <div class="chat-body">
            <div class="chat-meta"><span class="chat-author">Research agent</span><time class="chat-time" datetime="2026-09-14T10:02">10:02</time></div>
            <div class="chat-bubble chat-bubble--streaming" aria-busy="true">
                <p>Most of the 214 tickets trace back to one failure: the 02:14 sync on orders-console-01. Of the rest, 31 are duplicates of the same report, and</p>
            </div>
            <span class="agent-status"><span class="orb" data-orb="writing" role="img" aria-label="Writing"></span><span class="agent-status-label text-shimmer">Writing</span><span class="agent-status-time">0:06</span></span>
        </div>
    </div>
</div>

The prompt

.chat-composer--prompt draws a .chat-composer as one rounded field: the text on top, .chat-composer-tools under it for attachments, toggles as button.chip with aria-pressed, and a model choice, and .chat-composer-send holding the send button at the end. The field's focus ring is the composer's, so the whole box lights up as one control.

<form class="chat-composer chat-composer--prompt" style="max-width: 640px">
    <div class="chat-composer-row">
        <textarea class="form-input" rows="1" placeholder="Ask Ops agent" aria-label="Message"></textarea>
    </div>
    <div class="chat-composer-tools">
        <button class="btn btn--sm btn--ghost btn--icon" type="button" aria-label="Attach a file"><i class="ri-attachment-2"></i></button>
        <button class="chip" type="button" aria-pressed="true"><i class="ri-global-line"></i><span class="chip-label">Search</span></button>
        <button class="chip" type="button" aria-pressed="false"><i class="ri-lightbulb-line"></i><span class="chip-label">Think longer</span></button>
        <span class="chat-composer-send">
            <button class="btn btn--sm btn--ghost" type="button"><span>Balanced</span><i class="ri-arrow-down-s-line"></i></button>
            <button class="btn btn--ghost btn--icon btn-mic" type="button" aria-label="Talk"><i class="ri-mic-line"></i></button>
            <button class="btn btn--brand btn--solid btn--icon" type="submit" aria-label="Send"><i class="ri-arrow-up-line"></i></button>
        </span>
    </div>
</form>

While the agent writes

Put .beam on the composer while an answer is being written, and swap send for stop. The reader can keep typing the next question.

Writing the answer
<form class="chat-composer chat-composer--prompt beam" style="max-width: 640px">
    <div class="chat-composer-row">
        <textarea class="form-input" rows="1" placeholder="Ask Ops agent" aria-label="Message"></textarea>
    </div>
    <div class="chat-composer-tools">
        <span class="agent-status"><span class="orb" data-orb="writing" role="img" aria-label="Writing"></span><span class="agent-status-label text-shimmer">Writing the answer</span></span>
        <span class="chat-composer-send">
            <button class="btn btn--icon" type="button" aria-label="Stop"><i class="ri-stop-fill"></i></button>
        </span>
    </div>
</form>