Skip to content

Agent message

In review
Live · web

A live thread: agent leads (left), user trails (right)

Large 2.110:21 AM

The Gateway routes every session through the credential-store proxy for credentials. Want me to wire the retry/backoff next?

Yes. Exponential backoff, cap at 30s. Show me the diff first.

Large 2.110:23 AM

On it. Reading SseClient.kt, then patching the reconnect loop:

Read SseClient.kt0.4s

Here’s the backoff helper:

backoff.kt
fun nextDelay(attempt: Int): Long =
  minOf(30_000, 1_000 shl attempt)
    .plus(Random.nextLong(250))
  • Backoff is 1s → 30s with jitter.
  • Replays from lastSeq on reconnect.

Applying the patch and re-running the SSE tests

Streaming…

System divider + a failed turn (status is warm + icon + text, never color alone)

Local · Qwen3 Coder10:25 AM

Reconnected. The stream is healthy again.

Bash ./gradlew test6s
Large 2.110:26 AM
Stream failed. The gateway closed the connection after 3 retries.

Retry fired 0 times · tab to Copy / Thinking / Retry. The island is live.

Design status: not shipped

The bottom-of-chat streaming / typing indicator (the “Streaming…” dots shown while tokens arrive) is documented here for the design system of record, but is not currently implemented in the products: live bottom-of-chat streaming indicators proved inconsistent and hard to get right, so the apps have moved away from them. The design stays as reference; the streaming prop remains in the spec, unshipped.

// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform;
// this is the intended @dizyx/nockerl-react API (not yet exported; promotion tracked),
// modeled on the shipped Compose MessageBubble.)
export function Thread() {
return (
<>
<AgentMessage role="assistant" model="Large 2.1" timestamp="10:21 AM">
<p>Reading the reconnect loop, then patching the backoff:</p>
<ToolCallBlock family="shell-fs" name="Read SseClient.kt" status="done" elapsed="0.4s" />
</AgentMessage>
<AgentMessage role="user">
<p>Cap it at 30s.</p>
</AgentMessage>
{/* Live SSE target. The typing dots render after the body */}
<AgentMessage role="assistant" model="Large 2.1" showHeader={false} streaming>
<p>Applying the patch…</p>
</AgentMessage>
{/* Failed turn: a filled status-error disc + icon + text + a real Retry */}
<AgentMessage role="assistant" model="Large 2.1" error onRetry={resend}>
<Failure>Stream failed after 3 retries.</Failure>
</AgentMessage>
</>
);
}
PropTypeDefaultDescription
role *'assistant' | 'user'Speaker. assistant → leading/left (soft lifted plane); user → trailing/right (cyan jewel card).
modelstringResolved model name in the identity header (assistant only), e.g. "Large 2.1".
timestampstringShort time string in the header (e.g. "10:23 AM").
showHeaderbooleantrueShow the identity header. Pass false on a consecutive same-speaker turn to collapse it.
streamingbooleanfalseLive SSE target. Renders the typing dots after the body (frozen under reduced-motion).
errorbooleanfalseFailed turn: a filled status-error disc + the error material. Always paired with an icon + text, never color-alone.
onRetry() => voidRetry handler for a failed turn; renders a real, focusable Retry button.
childrenReactNodeOrdered body: markdown text, ToolCallBlock, code, list, a reasoning block.
Cross-platform drift (tracked for reconciliation)
  • Swift: no chat surface ships yet; Voice is transcription-only. The Swift API above is intended, not implemented.
  • Bubble radius: Compose hardcodes the bubble at 18.dp with a 6.dp tail via rememberBubbleShape; the tokens are 20 / 6 (radius.bubble / radius.bubble-tail, used by the web demo). Compose should consume the token.
  • Copy affordance (resolved): each platform keeps its interaction idiom. Android’s bubble long-press opens the multi-action menu (Copy becomes an item); web exposes an explicit, focusable Copy button (no hover / long-press on a pointer-less keyboard path).
  • Failed turn (resolved): the in-thread failed bubble wins on every platform, styled to the Banner inline-alert grammar of an inset status-error disc, the intent whispered into the border, a lifted neutral surface (never a red/pink fill, never a bare inset well), text + Retry. Android moves stream errors off the floating status pill and into the thread.