Commit Graph
1414 Commits
Author SHA1 Message Date
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 17c3674468 fix(dictation): stream transcripts on-the-fly and suppress accent picker
Three fixes for the push-to-talk dictation experience:

1. **Streaming partial transcripts**: The STT engine now flushes a partial
   transcript every ~2 seconds of continuous speech (DICTATION_PARTIAL_FLUSH_SAMPLES).
   Previously, text only appeared after a silence gap or when the user released
   the key. Now text streams into the composer while you're still speaking.

2. **Final flush on shutdown**: When the STT worker exits (key released or
   stop_dictation called), it flushes any remaining speech buffer before
   shutting down. Previously, speech accumulated since the last silence gap
   was silently discarded.

3. **Suppress macOS accent picker**: Holding ⌘D triggered the macOS
   press-and-hold accent character popup (showing ð, đ, etc.). Fixed by
   setting ApplePressAndHoldEnabled=false for the app's bundle ID at startup.

Also fixes the event ordering: the 'dictation-state: stopped' event is now
emitted by the forwarder task after all pending transcripts have been
delivered, ensuring the frontend receives the final text before the stopped
signal.
2026-07-11 16:18:41 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 1bcfcd4171 feat(dictation): ⌘D push-to-talk, stop button icon when recording
- ⌘D is now push-to-talk: hold to record, release to stop.
  AppShell dispatches keydown/keyup events; useComposerDictation
  starts on keydown and stops on keyup.
- Button click still toggles (click mic to start, click stop to end).
- Mic icon swaps to a filled stop square while recording.
- Shortcut description updated to reflect hold-to-talk behavior.
2026-07-11 16:18:40 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 ba958389e7 refactor(dictation): drop OpenAI Realtime path, add ⌘D shortcut
- Remove useRealtimeDictation, realtimeAudio, realtimeBufferWorklet,
  and transcribeSession API — dictation is now local-only (Parakeet).
- useDictation simplified to use only useLocalDictation.
- Add ⌘D (Ctrl+D on Windows) global shortcut to toggle dictation.
- DictationButton tooltip shows 'Voice Dictation ⌘D' with kbd styling
  matching the search bar's ⌘K pattern.
- Shortcut registered in keyboard-shortcuts.ts settings list.
- AppShell dispatches custom event; useComposerDictation listens.
2026-07-11 16:18:40 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 d3fd07b10d refactor(dictation): extract SttEngine, add local Parakeet dictation pipeline
Phase 1: Extract reusable SttEngine from huddle/stt.rs into stt_engine.rs.
The core STT logic (resample 48→16 kHz, earshot VAD, sherpa-onnx Parakeet
inference) is now a standalone component configurable via SttEngineConfig.
huddle/stt.rs becomes a thin wrapper that passes huddle-specific flags
(TTS barge-in, PTT gating). drain_until_shutdown moves to stt_engine and
is re-exported by huddle/mod.rs for backward compat.

Phase 2: Add Tauri dictation commands (start_dictation, stop_dictation,
push_dictation_audio, get_dictation_status) that create a standalone
SttEngine instance with dictation-tuned settings (longer silence threshold,
no TTS/PTT flags). Transcribed text is emitted to the frontend via
'dictation-transcript' Tauri events.

Phase 3: Add useLocalDictation hook that captures mic audio via AudioWorklet
and sends raw PCM to the native STT engine. useDictation now routes to
local STT when available (offline, no API key), falling back to cloud
(OpenAI Realtime via relay) when the model isn't downloaded.

Key wins:
- Works fully offline — no BUZZ_OPENAI_API_KEY needed
- Self-hosters get dictation for free
- Lower latency (no network round-trip)
- No relay billing concern
- Cloud fallback preserved for higher accuracy
2026-07-11 16:18:40 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 1dcfbf25a2 fix(dictation): handle transcription failed events
OpenAI emits conversation.item.input_audio_transcription.failed when an
item cannot be transcribed (e.g. audio_unintelligible). Previously this
event was dropped by the type filter, leaving isTranscribing stuck true
with no user feedback after a preceding buffer commit.

Now the failed event is treated as terminal: clears the transcribing
indicator and surfaces the error message via toast.
2026-07-11 16:18:39 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 ed9cb0c29f fix(dictation): single-request connect, clear transcribing on completion
1. Merge session + SDP into single POST /transcribe/connect

   The two-step flow (POST /transcribe/session → POST /transcribe/sdp) stored
   the OpenAI client secret in an in-memory DashMap, which breaks in HA
   deployments where the two requests may land on different relay replicas.

   Replaced with a single POST /transcribe/connect that accepts the SDP offer,
   mints the OpenAI session, proxies the SDP exchange, and returns the SDP
   answer + model — all in one request. No server-side session state is needed
   between requests, so this works correctly across any number of replicas.
   Removed the transcribe_sessions DashMap from AppState entirely.

2. Clear isTranscribing even when final text is unchanged

   When a TRANSCRIPT_COMPLETED_EVENT produces no text change (final matches
   accumulated deltas), the early-return in handleRealtimeEvent skipped the
   setIsTranscribing(false) call. This left the 'Transcribing…' indicator
   stuck indefinitely. Fixed by updating the transcribing flag before the
   text-change early return.
2026-07-11 16:18:39 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 f320ac0092 fix(dictation): proxy SDP server-side, allow send during transcribing
1. Proxy SDP exchange through the relay (transcribe.rs)

   The relay no longer returns the raw OpenAI client secret to the desktop
   client. Instead, POST /transcribe/session returns an opaque session ID,
   and the new POST /transcribe/sdp endpoint accepts the client's SDP offer,
   looks up the cached secret, forwards it to OpenAI, and returns the SDP
   answer. This prevents a compromised client from reusing the bearer token
   to open non-transcription Realtime sessions under the operator account.

2. Allow send during the transcribing grace window (MessageComposer.tsx)

   Previously, pressing Enter/Send while isTranscribing was true (during the
   3s grace window after user-stop) blocked the send entirely. Now the send
   proceeds with whatever content is already in the composer (transcript
   deltas have been applied incrementally) and cancels the dictation run so
   late events don't refill the cleared composer.
2026-07-11 16:18:39 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 25fd7ca78d fix(dictation): correct expires_after schema, grace window for all models
Two fixes:

1. (transcribe.rs) expires_after must be an object with anchor + seconds
   fields per OpenAI's client-secret schema, not a bare integer. The bare
   number would cause OpenAI to reject the request with a 400, making
   dictation fail with a 502 from the relay.

2. (useRealtimeDictation.ts) The user-stop grace window (3s delayed
   teardown with run kept valid) now applies to ALL models, not just
   manual-commit ones. For server-VAD models, the final VAD completion
   event may still be in-flight when the user clicks the mic to stop —
   without the grace window, that event was dropped and the tail of the
   dictated message disappeared.
2026-07-11 16:18:38 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 bf8ca927ee fix(transcribe): set short expires_after on client secrets
Add expires_after: 60 (seconds) to the OpenAI Realtime client-secret
request payload. This limits the reuse window of minted secrets — a
client must establish its WebRTC connection within 60s, and cannot reuse
the secret to open additional sessions after that. Without this, the
default 600s TTL allowed a compromised or malicious client to bypass the
per-pubkey rate limiter by reusing a single minted secret for many
concurrent sessions.
2026-07-11 16:18:38 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 4e1f6493ee fix(dictation): preserve final transcript on user stop, cancel on send
Distinguish user-initiated stop (mic button) from send/navigation cleanup:

- stopRecording(): user stop — keeps the run valid during the 3s grace
  window so the final commit's transcript is delivered to the composer.
- cancelRecording(): send/edit-save/navigation — immediately invalidates
  the run so late transcripts cannot refill the cleared composer.

MessageComposer's stopDictationRef now uses cancelRecording (called on
send and edit-save). useComposerDictation's draftKey/disabled effects
also use cancelRecording. The user-facing toggleRecording/stopRecording
path preserves the final transcript for short recordings.
2026-07-11 16:18:38 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 cd6a707fc0 fix(dictation): invalidate run before delayed teardown, remove unwrap
Address two review comments:

1. (useRealtimeDictation.ts) Increment activeRunIdRef immediately in the
   manual-commit cleanup path so late transcript events from the 3s grace
   window are rejected by handleRealtimeEvent. Previously, the run stayed
   valid during the timeout, allowing transcripts to write back into the
   composer after a send, edit-save, or navigation.

2. (transcribe.rs) Replace as_object_mut().unwrap() with a branch that
   builds the correct JSON literal directly. Avoids introducing an unwrap
   in a production path per AGENTS.md rules.
2026-07-11 16:18:37 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 625037e35d fix(dictation): commit buffered audio for manual-VAD models
When BUZZ_TRANSCRIPTION_MODEL is a realtime-whisper variant, the relay
now omits server_vad from the session config. Without server VAD, OpenAI
buffers audio indefinitely until a manual input_audio_buffer.commit is
sent. This commit adds the client-side counterpart:

1. Export commitAudioBuffer() and requiresManualCommit() from
   realtimeAudio.ts.
2. On session creation, track whether the model requires manual commit.
3. After flushing the pre-connection buffer, commit immediately and start
   a 2s periodic commit interval so streaming transcripts flow during
   recording.
4. On stop, send a final commit before teardown and keep the data channel
   open briefly (3s) to receive the last transcript response.
5. Clear the commit interval on cleanup.

Without this, realtime-whisper sessions would stream/buffer audio but
never produce transcripts because no commit was ever sent.
2026-07-11 16:18:37 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 6be052e0c2 fix(transcribe): gate status on membership, make VAD model-aware
Address two review comments:

1. /transcribe/status now returns configured: false for non-members on open
   relays, preventing the mic button from appearing for users who would get
   a 403 on session creation. Uses the same require_relay_member check.

2. The OpenAI Realtime session payload now omits turn_detection for
   realtime-whisper models (which require manual audio commit per OpenAI
   guidance). Other models continue to use server_vad.

Adds unit tests for the model-aware payload builder.
2026-07-11 16:18:37 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 955f6871db fix: require NIP-98 for transcribe endpoints, allow stop during startup
1. P1 — Force NIP-98 signed auth for /transcribe/* endpoints regardless of
   BUZZ_REQUIRE_AUTH_TOKEN. The X-Pubkey dev fallback is spoofable, so a
   billable endpoint must always require cryptographic proof of identity
   before the membership check trusts the pubkey.

2. P2 — DictationButton now allows the stop action whenever isRecording is
   true, even during startup (mic live but SDP exchange in progress) or
   when the composer is disabled. Only blocks the button when idle and
   disabled, or when startup hasn't captured the mic yet.
2026-07-11 16:18:37 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 577b7bb434 fix: address remaining review comments on dictation PR
1. P1 — Require relay membership for billable /transcribe/session even on
   open relays. Added require_relay_member() that always checks actual
   membership (with NIP-OA fallback) regardless of the
   BUZZ_REQUIRE_RELAY_MEMBERSHIP setting. Prevents arbitrary NIP-98 signers
   from minting metered OpenAI sessions on the operator's bill.

2. P2 — Keep stop control usable while recording. DictationButton now
   allows the stop action even when the composer is disabled — only
   *starting* a new recording is blocked. Additionally, useComposerDictation
   auto-stops the active session when the composer becomes disabled
   mid-recording (e.g. channel becomes read-only).

3. P2 — .expect() already removed in prior commit (openai_client() returns
   Result and propagates errors). No additional change needed.

4. P2 — Stop dictation before edit saves already addressed in prior commit
   (stopDictationRef.current() at line 540). No additional change needed.
2026-07-11 16:18:36 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 1815e02931 fix(dictation): stop dictation before edit-save, remove expect() in openai_client
- Stop active dictation run before the edit branch clears/saves, matching
  the normal-send path. Prevents late transcript events from writing back
  into the restored or fresh draft after an edit save.
- Replace .expect() in openai_client() with proper error propagation via
  Result, avoiding a panic if TLS backend initialization ever fails.

Addresses review feedback on PR #1511.
2026-07-11 16:18:36 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 d493378835 fix(dictation): rate-limit session minting, disable auto-submit by default
Addresses Wes's review feedback on PR #1511:

1. Per-pubkey rate limit on POST /transcribe/session (5/min, configurable
   via BUZZ_TRANSCRIBE_SESSIONS_PER_MINUTE). Each session opens a metered
   OpenAI Realtime connection on the operator's bill.

2. Auto-submit disabled by default (DEFAULT_AUTO_SUBMIT_PHRASE = '').
   The infrastructure for configurable phrases remains in place and can
   be wired to a user setting later.

Also:
- Move BUZZ_TRANSCRIPTION_MODEL into Config (consistent with other knobs)
- Use a shared reqwest::Client via OnceLock (connection pooling)
- Stop dictation on manual send (stopDictationRef wiring)
2026-07-11 16:18:35 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 062838ac8e fix(dictation): ignore stale transcript events after stop/send
When the user manually sends (Enter/click) while recording, the submit
flow already calls stopRecording(). However, queued data channel messages
could still fire handleRealtimeEvent after the peer connection teardown
began, writing stale transcript text back into the now-empty composer.

Fix: pass the run ID captured at startRecording into the data channel
message handler closure. handleRealtimeEvent now checks activeRunIdRef
against the captured run ID and drops events from stale runs. This
guarantees that once stopRecording() increments the run counter, no
further transcript events from that session can mutate composer state.
2026-07-11 16:18:08 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 ae643bc302 fix(dictation): preserve transcript item order from committed events
Handle input_audio_buffer.committed events to register items in the
correct utterance order using previous_item_id before any transcript
events arrive. This ensures that when completions for different turns
arrive out of order (or when only completions are sent without deltas),
the composer reconstructs multi-utterance dictation in the correct
sequence rather than event-arrival order.

Added tests for committed-order preservation, out-of-order completions
with pre-registered order, and completion-only flows.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:08 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 ed463c73bf fix(relay): wrap transcription config in session object
The OpenAI client_secrets endpoint expects the body as
{ session: { type, audio: { input: { transcription, turn_detection } } } }
not as top-level fields. Also moves turn_detection under audio.input per
the Realtime transcription guide.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:08 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 586d0de244 fix: address review comments — nested audio schema, item separators, safe auto-submit clear
- Relay: restructure OpenAI client-secrets payload to use the current
  typed transcription schema (audio.input.transcription) instead of the
  deprecated top-level input_audio_transcription field.
- realtimeAudio: insert space separators between transcript items when
  neither the preceding nor following text has whitespace, preventing
  multi-utterance runs from merging into unreadable text.
- useDictation: remove premature setText('') after auto-submit — the
  send flow handles clearing on success, so dictated text survives if a
  mention dialog opens or the send is blocked.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:07 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 cd669cbf3e fix(dictation): stop recording on channel/thread switch
When the composer's draftKey changes (channel or thread switch), stop any
active dictation session so transcript events from a stale WebRTC connection
don't leak into the wrong draft.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:07 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 caa6bbfea8 fix(dictation): typed transcription session, sync editor before merge, block sends during upload
- Use OpenAI typed transcription session format (type: "transcription")
  instead of legacy realtime fields that would fail or produce no transcripts
- Sync editor content via syncContentRef before merging dictation text so
  manually typed prefixes are preserved when dictation starts
- Read send-blocked state from refs at transcript time so uploads prevent
  auto-submit from clearing the composer

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:07 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 71580439bb fix(dictation): use client_secrets API, track transcripts by item_id
- Switch relay from /v1/realtime/sessions to /v1/realtime/client_secrets
  with the wrapped { session: { ... } } request shape per OpenAI's current
  WebRTC guide. The old endpoint returns non-2xx, breaking dictation.

- Redesign TranscriptSegmentState to track per-item segments keyed by
  item_id. Completed events for different turns can arrive out of order;
  reconciling by item_id preserves utterance ordering and prevents text
  reordering or partial-turn drops during fast consecutive speech.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:06 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 53f96841c9 fix(dictation): address review — nonce, editor state, segment tracking
- Add nonce tag to NIP-98 auth events to prevent replay rejection when
  multiple components call /transcribe/status in the same second.

- Wire dictation text into both the Tiptap editor and contentRef via
  setComposerContent + setEditorContentRef, so dictated text actually
  appears in the composer and is serialized on submit.

- Call submitMessageRef.current() synchronously in onSend instead of via
  queueMicrotask, ensuring the editor content is consumed before the
  subsequent setText('') clears it.

- Replace naive append-based transcript merging with segment-aware state
  tracking (TranscriptSegmentState). Delta events accumulate into
  pendingDelta; completed events replace accumulated deltas with the
  finalized text, preventing duplication.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:06 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 bde70242b8 fix(relay): add NIP-98 auth + relay membership to transcribe endpoints
Both /transcribe/status and /transcribe/session now require NIP-98
authentication and relay membership (with NIP-OA fallback), matching
the security posture of /events, /query, and /count.

Promotes verify_bridge_auth, check_nip98_replay, and nip98_expected_url
to pub(crate) so the transcribe module can reuse them without duplication.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:06 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 88b0f2627d fix(relay): add doc comments to transcribe response structs
New public API needs doc comments — clippy runs with -D missing-docs, so
TranscribeStatus and TranscribeSession were failing the Rust Lint gate.

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:05 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 19463350e2 style: make dictation button rounded-full to match send button
Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:05 +01:00
klopez4212andnpub13fn4ahfnvaa2qwylvegdgeajqs0mph6v4qsw4jcqnw4mjh3hzh2quuucm5 f7ee8ff980 feat: add real-time voice dictation to composer
Adds dictation support using OpenAI's Realtime API over WebRTC:

Relay:
- New /transcribe/status and /transcribe/session endpoints
- BUZZ_OPENAI_API_KEY env var gates the feature (hidden when absent)
- Proxies ephemeral client-secret minting from OpenAI

Desktop:
- New features/dictation module with:
  - AudioWorklet for 24kHz PCM capture + buffering
  - WebRTC peer connection to OpenAI Realtime API
  - Real-time transcript merging into composer
  - Auto-submit on trigger phrase ('submit')
  - Mic button in composer toolbar (red pulse when recording)
- Integrated into MessageComposer via useComposerDictation hook

Signed-off-by: klopez4212 <klopez4212@gmail.com>
2026-07-11 16:18:04 +01:00
51234bd89f fix(desktop): preserve archived observer history (#1752)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 23:46:42 -04:00
09be502159 fix(dev): install Lefthook hooks into shared .git/hooks dir (#1751)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 22:06:32 -04:00
ec37625522 fix(desktop): surface codex config-parse failures and -32603 internal errors clearly (#1745)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 21:25:52 -04:00
e8df6ba694 fix(desktop): fix workspace rail badge disappearance and persist mark-as-unread (#1747)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 21:15:53 -04:00
9895b4d390 chore(desktop): remove container-only npm-preflight E2E harness (#1749)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 21:05:17 -04:00
58aee8f913 fix(desktop): preflight npm prefix writability in doctor installs (#1732)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 19:25:58 -04:00
d80ed3180b chore(release): release Buzz Mobile version 0.3.32 (#1739)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
mobile-v0.3.32
2026-07-10 15:32:07 -07:00
eb0417aa88 fix(release): install AppImage bundling system deps for Linux (#1737)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 18:30:01 -04:00
ea1c75b0d2 fix(workspaces): exclude muted channels and unfollowed threads from rail unread badge (#1738)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 18:22:11 -04:00
26966aacec feat(desktop): add explicit Edit avatar CTA in agent instance edit dialog (#1736)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 17:58:26 -04:00
67a2d047eb fix(observer): order system prompt below current-session divider on restart (#1734)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 17:44:12 -04:00
215f521880 chore(ios): restrict app to iPhone (#1735)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
2026-07-10 21:25:32 +00:00
bbf80aa73f fix(release): run Linux release job under bash not dash (#1733)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 17:00:25 -04:00
Will PflegerandGitHub 0d9be2fde1 chore(release): release Buzz Relay version 0.2.0 (#1728) relay-v0.2.0 2026-07-10 16:43:23 -04:00
Will PflegerandGitHub 13dded7195 chore(release): release Buzz Desktop version 0.4.0 (#1730) v0.4.0 2026-07-10 20:20:51 +00:00
bdb3e69899 chore(release): release Buzz Mobile version 0.3.31 (#1724)
Signed-off-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1shglkdhngx3hrnhf4gf8vhpqdrmeludctechdvpwd3988zzs7ncq2cmtxu <85d1fb36f341a371cee9aa12765c2068f79ff1b85e7176b02e6c4a738850f4f0@sprout-oss.stage.blox.sqprod.co>
mobile-v0.3.31
2026-07-10 16:11:38 -04:00
d9e4edbbf5 feat(desktop): default observer archive on for dev-nest builds (#1726)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 16:06:25 -04:00
Will PflegerandGitHub a7eca421a5 fix(release): filter changelogs to lane-specific paths in _release-pr (#1729) 2026-07-10 15:55:01 -04:00
2f2ad409a7 feat(desktop): thread baked build env into global agent config UI (#1722)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: npub1mn7jgtj4w2pd0g0zeuhxsa6jy6p0rewxz4kujt98my82ahfmp72sxjexk7 <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@sprout-oss.stage.blox.sqprod.co>
2026-07-10 15:45:42 -04:00
Will PflegerandGitHub febbb06909 fix(release): truncate release PR body to stay under GitHub's 65536-char limit (#1727) 2026-07-10 15:41:23 -04:00
2e529aab75 feat: relay invite links (mint + claim + landing page + deep link) (#1668)
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: npub1k766u8wq088nkr9vy94uqlh9kmw5956g593a7xk7cks7frhgctmsr00p27 <b7b5ae1dc079cf3b0cac216bc07ee5b6dd42d348a163df1adec5a1e48ee8c2f7@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: npub1k766u8wq088nkr9vy94uqlh9kmw5956g593a7xk7cks7frhgctmsr00p27 <b7b5ae1dc079cf3b0cac216bc07ee5b6dd42d348a163df1adec5a1e48ee8c2f7@sprout-oss.stage.blox.sqprod.co>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
2026-07-10 12:06:35 -07:00