feat(desktop): developer mode display style — prompt-first UI with pinned/recent channel list, palette, and split-pane side chats - #3459
Draft
joahg wants to merge 115 commits into
Draft
Conversation
Prompt-first, terminal-style UI toggled in Settings > Appearance or Cmd/Ctrl+Shift+D. Tab cycles the composer between chat and agent modes; Enter on a fresh prompt spawns a stream channel named from the prompt, attaches the selected agent, and mentions it so it starts working there. Arrow keys navigate recent session channels. Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Each prompt in a session channel renders as a card containing the agent's threaded response. With the input empty, Up/Down selects cards; Enter opens the selected card's thread in a 50/50 split side chat with its own composer, so follow-up questions carry that thread's context. Escape unwinds side chat, card selection, then channel. Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…e polish (AGNTOPS-354) - Amp-style command palette (Ctrl+O or / on empty composer): channel search across all sessions, settings/agent-config entries, standard-UI switch, and a name-color picker - Explicit channel navigation: up from the fresh composer slides a channel navigator out from the left with live preview; enter opens, escape backs out; escape inside a channel returns to the navigator - LLM channel naming via the mesh LLM node's OpenAI-compatible endpoint when running, with async rename and slug fallback - Split-pane side chat: draggable (and keyboard-resizable) divider with persisted split, inactive-pane dimming, left/right arrows switch panes - Deterministic per-author name colors with device-local hex overrides, conservative keyword highlighting (inline code, URLs, mentions) - Square corners on prompt cards, pills, and channel rows Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…split view Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…rome polish in dev mode (AGNTOPS-354) - Channel list is always visible, dimmed while a channel is focused; composer covers only the channel pane, full width only for new sessions - Device-local channel categories with per-row assignment menu; uncategorized/new channels always render below all categories - Remove the shared top chrome bar in developer mode; dev-mode gray reaches the window top with macOS traffic-light clearance - Window refocus restores the last-focused input; dead-space clicks no longer blur the composer - Kind-7 reactions render as chips on prompts and replies (doubles as the agent working indicator) - Agent replies align with the prompt's indent; selection hint is absolutely positioned so up/down navigation causes no layout shift - Composer mode pill and caret match the agent's chat name color - Agent-directed prompts carry a visible @agent prefix Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Channel names now come from the tagged agent's own harness instead of a prompt slug. buzz-acp gains a 'complete' subcommand (bounded one-shot ACP prompt returning JSON text); the desktop bridges it through a new generate_agent_completion Tauri command that resolves the managed agent's effective harness command/model/env exactly like spawn. Channels are created under a neutral unique 'new-session' placeholder and renamed asynchronously when the title arrives, so naming failures stay visible instead of masquerading as generated titles. Mesh inference remains the fallback when no managed agent completion is available. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: fix(acp): per-runtime env defaults at spawn — isolate Hermes from configured MCP startup (block#3420) Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Both the channel and side-chat composers now size to their content — wrapped lines included, not just hard newlines — growing upward to 40% of the window before scrolling internally. The top border doubles as a drag handle (pointer + keyboard) that sets a persisted minimum height. AGNTOPS-354 Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Transcript URLs were blue-underlined spans you couldn't click. They now render as links — link icon plus the page's og:/twitter:/<title> title when a new generic fetch_page_title command can fetch it (https-only, 4s timeout, 256KB cap), falling back to the URL without its scheme, truncated at 64 chars. Clicks open the system browser via the opener plugin. Trailing sentence punctuation stays in the prose instead of breaking the href. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…ocus AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…t search AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Pinned chats render in their own section at the top of the channel list; all other chats sit beneath. Both sections order by last activity, newest first, with unread dots + bold names driven by the shared useUnreadChannels state from AppShell. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Message @mentions whose name matches one of the event's p-tag members (e.g. the @agent prompt prefix) render as a bordered pill in that author's chat color — visually matching the composer's mode pill. Multi-word agent names like "amp (local)" are matched longest-first; unknown mentions keep the generic highlight. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The channel name already renders in the app top bar; the in-transcript duplicate wasted space and repeated the first prompt's text as topic. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…elected Selecting a card with ↑ now blurs the message box (readOnly while inactive) so typing can't land in it; the shell owns ↑/↓/Enter/Escape through a window listener until the selection clears. Clicking the box clears the selection and resumes typing. Window-refocus and click-on-message focus restoration skip the composer during card selection so they can't silently re-arm it. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
⌘N jumps to the fresh composer to start a new channel. ⌘T opens a draft side chat in the open channel: the pane opens with no thread yet, the first send posts a root message to the channel exactly like the main composer, and the pane attaches to that new thread for follow-ups. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The gutter layout (time · name · content on one line) produced ragged content columns. Every message now renders a header line — author name in its chat color, timestamp, reaction chips — with the content on its own line at the left edge, Slack-style. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: feat(agent): fix Anthropic prompt caching with Databricks (+ MCP proxy/TLS passthrough) (block#3463) Fix mobile attachment and gallery polish (block#3370) Signed-off-by: Joah Gerstenberg <joah@squareup.com>
AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Typing # in either composer suggests known channels; Tab/Enter inserts a plain-text #channel-name reference (the same wire format the standard UI renders as channel links). Known references in the transcript render as clickable links that open the channel without stealing composer focus. AGNTOPS-354 Amp-Thread-ID: https://ampcode.com/threads/T-019fabca-7cdc-777d-9ccd-ed829c1e60f0 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Tab used to cycle the composer target through chat plus every agent, which gets slow as the agent list grows. Now Tab is a two-way toggle between plain chat and the last agent the composer targeted, and ⌃Tab / ⌘Tab (+⇧ reverses) cycles through the agents only. From chat, the agent cycle resumes at the remembered last agent (persisted in localStorage alongside the last mode) instead of restarting at the top of the list. ⌘Tab is accepted but macOS reserves it for the app switcher, so ⌃Tab is the binding that actually lands there. Selection logic moved out of DevModeShell into a useComposerModeSelection hook (also keeps the shell under the file-size ratchet). Validation: desktop pnpm test (3923 pass), typecheck, biome check + ratchets, and the dev-mode e2e specs (40 pass) including new coverage for the toggle, agent-only cycling, and reload persistence. Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb418-369b-7651-9a16-33e36a2c21be
…atest.json) Amp-Thread-ID: https://ampcode.com/threads/T-019fb4a5-ccf3-75f9-b4d6-d5620bf99f54 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: feat(desktop): improve agent activity header ui (block#3321) perf(presence): reduce heartbeat frequency (block#3783) Tighten continuation message rows (block#3724) Fix video reviews in thread replies (block#3719) feat(release): make desktop releases immutable (block#3568) Make relay reconnect backoff authoritative (block#3774) feat(desktop): add password-protected backups in settings (block#3701) fix(desktop): reuse profiles when joining communities (block#2155) Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: (22 commits) feat(desktop): import local Pocket voices (block#3259) fix(desktop): open profiles from avatars (block#3751) refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands block#2467 + block#3208) (block#3910) docs: add VISION_REMOTE_AGENTS.md (block#3924) feat(desktop): auto-enable huddle transcription for agents (block#3180) feat(agent): optional reply guard reminds a silent turn to publish (block#3763) feat(desktop): upgrade Pocket TTS model (block#3266) feat(desktop): delete a message by clearing its edit to empty (block#3813) feat(relay): raise hosted community limit to five (block#3829) feat(desktop): locally stored NIP-49 encrypted key backup (block#2937) fix(catalog): update Amp tagline (block#3806) fix(desktop): channel topic and membership metadata cleanup (block#3642) fix(desktop): align data deletion labels (block#2230) fix(relay): align NIP-11 max_limit with REQ ceiling (block#3635) fix(desktop): allow linux-only media items as dead code off-linux (block#3811) fix(desktop): report authenticated relay recovery (block#3812) fix(desktop): don't gate hover affordances on the hover media query (block#3657) feat(relay): gate kind 30178 team-catalog reads behind the shared tag (block#3358) test(desktop): click visible thread collapse guide (block#3800) feat(desktop): raise the install ceiling and make installs observable (block#3368) ... Amp-Thread-ID: https://ampcode.com/threads/T-019fb8e1-6ece-72a7-8808-9b12e0f7e833 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> # Conflicts: # desktop/src-tauri/src/linux_media.rs # desktop/src/app/AppShell.tsx
Cmd+[/] alone no longer switches tabs; the shortcut is now shift+cmd+[/] to match browser conventions. Matches on event.code so shifted bracket keys resolve consistently across layouts. Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb8ed-ef8a-733b-8810-5ca398cf89f3
Dev-mode reaction chips now render community/custom emoji images (NIP-30 emoji tags, with community-palette fallback for CLI reactions), sit inline on the author/timestamp header line, and are clickable: clicking a chip toggles your own reaction optimistically, and a hover + affordance opens the emoji picker. Deleted reactions (kind 5/9005) are dropped and duplicate deliveries collapse. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb8ed-7474-778a-81ef-60e0407b3938 Co-authored-by: Amp <amp@ampcode.com>
extract_p_tags returns {pubkey, role} objects, so comparing the whole
value against the caller hex string never matched and every
'channels create --parent' call was rejected as a non-member.
Amp-Thread-ID: https://ampcode.com/threads/T-019fb8fe-b2fb-7462-aa34-1ab2ee551e5d
Co-authored-by: Amp <amp@ampcode.com>
Signed-off-by: Joah Gerstenberg <joah@squareup.com>
ArrowUp-selecting one of your own prompt cards and pressing `e` loads the message into the composer for editing (kind:40003, the same relay edit path as the standard UI — no delete-and-recompose needed). Enter saves; Escape cancels and lands back on the card. Someone else's or a still-pending prompt is a silent no-op. Dev mode now also resolves kind:40003 edits when rendering: roots, inline replies, and the thread panel overlay the latest edit's content and imeta tags (attachments ride along) and show an "(edited)" marker. Original attachments and spoiler state are preserved on save, and only newly added mentions get p tags so a typo fix re-wakes nobody. Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb8ef-ce3c-72fc-9eae-ab89a72c6177
Message-like notifications (DMs, mentions, thread replies) now pick their sound by who sent the message: humans default to ping, agents to the new amp sound (derived from the Amp CLI's completion chime, macOS Submarine). Non-message slots (needs action, job events) keep per-slot sounds. Also relabels the misleading master 'Sound' switch to 'Event alerts' — it gates the entire notification (toast + sound), not just audio, which made it look like sounds were broken when toggled off. Amp-Thread-ID: https://ampcode.com/threads/T-019fb8fe-48d2-734b-a35a-13b5f10b9982 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Dev mode gains the standard UI's agent status indicator, restyled for the terminal aesthetic: while agents work in the channel, a quiet monospace line pinned between the transcript and the composer shows each working agent's name (chat color) and the newest headline from its observer transcript — the command being run, file being edited, or thought in progress. It reads the shared agentWorkingSignal (observer turn events) and reuses the standard BotActivityBar's spine/meaningful headline tiers, but renders a single in-place-updating line per agent: no spinner, no rotating carousel, one fade-in on appearance. Falls back to "working…" until the first observer frame lands, and clears when the turn completes. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb909-becd-72e9-abe2-cdbcbda7350e
…play-style * joah/dev-mode-status-indicators: feat(desktop): agent activity status line in dev mode Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Move the card-selection keydown effect into a useCardSelectionShortcuts hook — DevModeShell crossed the 1000-line file-size ratchet after the status-line and message-edit features landed together. Also convert two upstream string concatenations to template literals; they trip biome's useTemplate lint in the pre-push check. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb909-becd-72e9-abe2-cdbcbda7350e
The pubkey-truncation guard flags workingPubkeys.slice(0, N) in DevAgentStatusLine — an array window over a pubkey list, not string truncation; names render via useMemberNameResolver. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb909-becd-72e9-abe2-cdbcbda7350e
Dev mode is now opted into per session: the display style no longer persists across launches, so the app always opens in the standard sidebar layout. The standard top chrome gains a prominent Dev Mode button (with the ⌘⇧D hint) to switch over; dev mode already surfaces ⌘⇧D in its shortcuts overlay and command palette for the way back. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb92b-b150-70a1-bc16-2cc313923268
…-style * origin/main: docs(nips): add single-coordinate manual-unread override layer and verification model to NIP-RS (block#2864) chore(release): release Buzz Desktop version 0.5.3 fix(release): make immutable desktop release operable (block#3943) Signed-off-by: Joah Gerstenberg <joah@squareup.com>
Restore the localStorage persistence dropped in 17f2387 — only the first launch (nothing stored) should land in standard mode; after that the chosen display style sticks across relaunches. Signed-off-by: Joah Gerstenberg <joah@squareup.com> Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fb92b-b150-70a1-bc16-2cc313923268
Agents that were running when the app quit are now restarted on the next launch even when start_on_app_launch is off. shutdown_managed_agents marks each agent it stops with a one-shot stopped_by_app_shutdown flag; launch restore treats flagged agents as candidates and consumes the flag, so an agent the user explicitly stopped before quitting stays stopped. Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fba6f-56dd-73b1-86d8-30f8bc7dd0c1
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Buzz-Channel: 6f9e6bb7-7b2b-4361-820b-8e3f7f8941d0 Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
Co-authored-by: Joah Gerstenberg <me@joahg.com> Signed-off-by: Joah Gerstenberg <me@joahg.com>
joahg
force-pushed
the
joah/dev-mode-display-style
branch
from
August 1, 2026 03:00
b274ea6 to
682f645
Compare
Text typed into the dev-mode composer used to follow the user across channel switches. The shell now stashes the draft per scope (channel id, or the fresh composer) on exit and restores it on return; a send consumes the origin scope's draft and a failed send restores its prompt to the channel it was sent from. Switching away mid-`e`-edit abandons the edit buffer and keeps the pre-edit draft, matching Escape. In-memory only: drafts survive display-style toggles, not app restarts. Reported in [#buzz-dev-mode--per-channel-message-drafts](buzz://message?channel=125f8109-1b00-41cb-a859-4d3bf4412104&id=aaeb6f582c91a763471e638bf39072d4f1ef9d744f9b2a96c5bd034d43cc4db5) Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fbdef-4c11-755c-9c6c-01545566fcc5 Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: fix(relay): allow open relays to set their NIP-11 workspace icon (kind:9033) (block#3998) docs: note that addressable channel events scope by d, not h (block#4103) docs: fix stale kind count, quick-start numbering, and empty Further Reading (block#2613) fix(desktop): keep thread-open affordance in archived channels (block#4012) Signed-off-by: Joah Gerstenberg <joah@squareup.com>
The stopped_by_app_shutdown field (afcc2ec) grew seven over-limit Rust files by one line each (types.rs by seven), tripping the desktop file-size ratchet against main in Desktop Core CI. Reclaim the lines by splitting packed use groups into rustfmt-stable single-line statements and rewrapping doc comments to the 100-column budget. No code changes. Amp-Thread-ID: https://ampcode.com/threads/T-019fbdef-4c11-755c-9c6c-01545566fcc5 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com>
…-style * origin/main: docs(chart): fix ArgoCD example for native OCI sources (full artifact repoURL + path) (block#3426) docs(readme): clarify which release asset to download per platform (block#3481) Amp-Thread-ID: https://ampcode.com/threads/T-019fbdef-4c11-755c-9c6c-01545566fcc5 Co-authored-by: Amp <amp@ampcode.com> Signed-off-by: Joah Gerstenberg <joah@squareup.com> # Conflicts: # README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a developer mode display style to the Buzz desktop app (AGNTOPS-354): a prompt-first, keyboard-driven, Amp/Claude-Code-style surface layered on the existing channel structure.
Core flow
@Agentprefix on the wire (plus the mention tag) so the standard UI and other clients see who a prompt is for — but dev mode lifts it out of the message body and renders it as a small “to Agent” line (in the agent's chat color) under the author name. The composer shows its Tab-cycled target the same way: a “to Agent” line at the top of the chat box (no more bordered pill), with “chat” / “working…” for the other states.Channel list: pinned chats, recency order, unread indicators
pin/unpin): pinned chats render in their own pinned section at the top; all other chats sit beneath. Pins are device-local (localStorage) — the channel protocol has no pin field.Command palette
Window chrome & focus
?in an empty composer (or the palette's "keyboard shortcuts" entry) opens a shortcuts pane listing every dev-mode binding.Split-pane side chat
Channel naming
buzz-acp completesubcommand runs a bounded one-shot ACP prompt through the tagged agent's own harness (same command/model/env resolution as agent spawn), bridged by agenerate_agent_completionTauri command. The exchange is a private subprocess — it never appears in any channel.new-sessionplaceholder and renamed asynchronously when the title arrives; a lingering placeholder makes naming failures visible instead of masquerading as generated titles.Color
inline code, @mentions. Mentions matching one of the message's p-tag members render as plain colored text in that author's chat color (no box); multi-word agent names likeamp (local)are matched longest-first. On human-authored messages, a leading mention is treated as direction, not prose — stripped from the body (side-chat header preview included) and shown as the “to Agent” line. Agent-authored messages keep their mentions inline in the body as normal colored text.#channelreferences: typing#in either composer autocompletes known channel names (↑/↓ select, Tab/Enter insert, Esc dismiss); accepted references serialize as plain#channel-nametext (the same format the standard UI links), and known references in the transcript render as clickable links that open that channel without stealing composer focus. Multi-word names match longest-first, same as mentions.fetch_page_titleTauri command (https-only, 4s timeout, 256KB cap) resolves og:/twitter:/<title>titles.Layout containment
min-w-0so nowrap content (truncated link labels, thread-header previews) can't inflate its intrinsic width, and the transcript/side-chat scroll viewports clip residual horizontal overflow. Long unbroken words and link labels wrap inside their pane.dev-mode-overflow.spec.ts, smoke project) sends a long unbroken message, opens the side chat, and asserts nothing extends past the window.Markdown rendering
strikethrough, and[label](url)links (labeled links render via the Slack-style link component using the written label, skipping the title fetch). Emphasis delimiters are strict so snake_case and arithmetic stay plain prose; everything renders as React nodes — never HTML — and plain human chat renders exactly as typed.Channel members & join/leave narration
N memberscount read straight off the channel record — a 1000-member channel never fetches its roster just to render the header. Both refresh live off the same membership invalidation the standard UI uses.→ alice joined,← bob left,removed by …) interleaved chronologically with the prompt cards. Card ↑/↓ navigation skips them.useMemberNameResolver.dev-mode-members.spec.ts, smoke project) plus unit tests for the 40099 payload parser.@user mentions
@in either dev-mode composer autocompletes users: current channel members rank first, then relay-widesearch_usersresults (self excluded). ↑/↓ select, Tab/Enter insert@Display Name, Esc dismisses — same keyboard contract as#channelautocomplete.@Namestill present emits a["p", pubkey]tag (multi-word names match longest-first; editing a mention away drops its tag;user@example.comnever triggers).dev-mode-mentions.spec.ts, smoke project) plus unit tests for mention extraction.Notes
@fontsource-variable/jetbrains-mono, wired into Tailwind'sfont-monostack with the system stack as fallback).⌘⇧Dtoggles); standard UI untouched.pnpm typecheck,pnpm check,pnpm test(3826 pass),cargo test -p buzz-acp(639 pass),pnpm buildall green.Linear: AGNTOPS-354
Parent & sub-channels (agent sessions)
parent-slug--sub-slug, one nesting level — so the relay is completely untouched and existing clients keep working.main, then each sub's suffix, unread dots,+ sub) — horizontally scrollable and built off a single-pass parent/child index, so parents with hundreds of subs stay cheap.+ sub(tab strip or palette "new sub-channel" action) drafts a prompt; Enter creates the child (inheriting the parent's visibility/type), sends the prompt there, posts a spawn announcement in the parent, and records the relationship + report-back contract in both channels' canvases. Children get LLM-generated names via the same naming path as new channels (renaming only the suffix);sanitizeChannelNamenow guarantees generated names can never contain--.buzz channels create --parent <channel>mirrors the same contract — parent resolution by id or name, caller-membership check, slug sanitization, no nested children, inherited type/visibility, spawn announcement + canvas updates. The ACP base prompt documents how agents spawn sub-channels and report completion back to the parent.dev-mode-sub-channels.spec.ts): children hidden from the navigator + exposed as tabs,+ subcreation with parent announcement, palette action.Tabs UX: keyboard cycling & terminology
+ tab(test iddev-mode-new-tab), and the palette action is "new tab in # channel" with a⌘⇧Thint. Internal naming (parent--subslug convention, canvas metadata, CLI--parent) is unchanged for relay compatibility.⌘⇧Tstarts a new-tab draft;⌘[/⌘]cycle backward/forward through a channel's tabs (main + subs), wrapping at the ends.⌘K,⌘N,⌘T,⌘⇧T,⌘[/⌘]) are consolidated into a newuseDevModeShortcutshook.⌘⇧T, and⌘[/⌘]wrap-around cycling.parent--prefix is the parent link); renaming a main cascades to its tabs via the existinguseUpdateChannelMutationplan, with an active channel-list refetch so the family stays intact in the mounted dev-mode UI.Contextual unread indicators that bubble up
topLevelUnreadChannelIdsvs. full unread set).⌘[/⌘], and⌥↑/⌥↓never reroute.selectInlineVisibleCounthelper) — collapsed replies stay unread until the side chat is actually opened. Channel previews from the navigator never clear read state.unreadThreads.ts:selectUnreadThreadRoots/findUnreadFamilyTarget) with unit tests, auseUnreadRoutinghook resolving families via the existing single-passSubChannelIndex, and a new Playwright suite (dev-mode-unread.spec.ts) covering bubbling, routing, and read-state distinctions. Shell focus/refocus logic extracted touseShellFocusGuardsto keepDevModeShellunder the file-size limit (no behavior change).Included from PR #3335
Media attachments
useMediaUpload+buildOutgoingMessagewith NIP-92imetatags plus standalone markdown media lines — so attachments are fully compatible with the standard UI and other clients.imeta-tagged media and standalonelines render through the sharedMarkdownrenderer: images open in the standard lightbox, videos get native controls, and everything stays constrained to pane width. Media-only sends work inside an existing channel (a brand-new channel still needs prompt text for naming).Palette: join channels you haven't joined
get_channels): they surface only while typing (a relay can have hundreds), labeled "not joined · enter to join", ranked below joined channels. Enter joins and opens in one step. Foreignparent--subtabs stay hidden unless you're a member of the parent (membership invariant). The left navigator still shows joined channels only.Developer-mode community rail
Authenticated link labels (Slack, Google) + copyable link URLs
fetch_agent_link_labelTauri command resolves labels through the locally installedsq agent-toolsCLI first: Slack channel messages label asauthor in #channel, DM messages asauthor with partner, bare channel links as#channel, and Google Docs/Drive/Sheets/Slides show their real file title. Everything is best-effort — missingsq, CLI errors, and timeouts fall back to the generic page-title path, and CLI errors never surface in the UI.data-dev-linkmarker, and a shell-level copy handler rewritestext/plainso each link serializes as its href (surrounding text preserved) whiletext/htmlkeeps the pretty labels for rich paste targets. Previously copying a link title could paste as nothing.Per-channel composer drafts
eedit preserves the pre-edit channel draft rather than leaking the abandoned edit buffer.