Improve message markdown display and formatting#7
Merged
Conversation
tlongwell-block
added a commit
that referenced
this pull request
Mar 10, 2026
* origin/main: Add desktop Home feed (#12) Add desktop Playwright e2e harness (#11) Update desktop icon and persist window state (#9) feat: add channel creation flow (#8) Improve message markdown display and formatting (#7) feat(desktop): connect chat to relay (#6) docs(readme): clarify desktop setup (#4) feat: add desktop app (#3) # Conflicts: # crates/sprout-test-client/tests/e2e_rest_api.rs
tlongwell-block
added a commit
that referenced
this pull request
Mar 16, 2026
Crossfire round 1: codex 4/10, opus 8/10. All critical issues fixed: Security (critical): - Force channel_id=None for kind:1059 gift wraps — prevents channel-scoped storage that would bypass #p AUTH-gating (codex finding #1) Correctness: - NIP-50 pagination loop — keep fetching Typesense pages until limit met or result set exhausted, capped at MAX_SEARCH_PAGES=5 (codex finding #2) - Push authors/since/until to Typesense filter_by — post-filtering is now a correction step, not the primary filter (codex + opus suggestion) - NIP-10 root tag validation — reject events where client-supplied root diverges from server-resolved ancestry (codex finding #3) Clarity: - Consolidate #p gating into single P_GATED_KINDS check (opus suggestion #7) - filter.clone() → std::slice::from_ref(filter) (opus suggestion #1) - Remove no-op get_events_by_ids test, add debug_assert (opus #3, #5)
tlongwell-block
added a commit
that referenced
this pull request
May 14, 2026
Adds a Settings → Agent Provider panel to the desktop GUI for configuring the `sprout-agent` provider, model, API key, and behavior knobs. Settings are encrypted at rest with NIP-44 self-encryption (the user's own nostr key) and injected into the sprout-agent child's env at spawn time. The panel also gains automatic provider-URL detection based on the API key format the user pastes. ## Backend (`desktop/src-tauri/src/commands/agent_provider_settings/`) - `mod.rs` — IPC types + plaintext `StoredSettings` (Drop zeroizes `api_key`; no Debug derive on input/stored). v2 envelope binds the plaintext to its owner pubkey for rollback / envelope-swap protection. - `storage.rs` — envelope read/write, NIP-44 encrypt/decrypt with `Zeroizing<String>` for plaintext, atomic-rename writes, file-size cap, `normalize_origin` (rejects non-loopback `http://`, userinfo, query, fragment), `validate_input` (provider whitelist, control-char rejection, size caps for key/model/base_url/system_prompt, positive- int knobs). `validate_stored` mirrors the same rules on the decrypted blob at spawn time — fails closed on a rolled-back pre-validation envelope so a redirected `http://api.example.com/v1` cannot escape. - `commands.rs` — `get_*`, `save_*`, `delete_*`, `get_*_env_presence` Tauri commands. The save command trims whitespace + zeroes the input api_key before validation can early-return. - `spawn.rs` — `LoadForSpawn` enum + `EnvPairs` newtype whose Drop zeroizes every value buffer. `apply_to_command` hands each env pair to `Command::env` by reference, zeroizing the local buffer after. Spawn policy: Ok → strip OWNED_AGENT_ENV_VARS + ACP-level vars then inject; None → no-op; IdentityMismatch / Error → fail closed (strip inherited owned vars, inject nothing). - `tests.rs` — round-trip envelope I/O, identity-rotation, save-time validation (oversized prompt, zero timeouts, tiny history bytes, unknown provider, control chars, oversized fields, api-key whitespace trim, owner_pubkey v2), `apply_to_command` × `LoadForSpawn` matrix (Ok/None/IdentityMismatch-fails-closed/Error-fails-closed, openai dialect), `stored_to_env_pairs` for each dialect, R7 `validate_stored` coverage (non-loopback http, control chars in key/model/base_url, userinfo/query in base_url, unknown provider, oversized prompt, empty-key + loopback local accepted). ## Runtime integration (`desktop/src-tauri/src/managed_agents/runtime.rs`) - `build_agent_command` calls `agent_provider_settings::apply_to_command` exactly when the harness is `sprout-agent`. ACP-level vars (SPROUT_AGENT_PROVIDER etc.) are stripped from inherited parent env before injection so a stale shell `ANTHROPIC_API_KEY` never shadows saved settings. `respond-to` gate env (`SPROUT_ACP_RESPOND_TO[_ALLOWLIST]`) threads through with the new `owner_hex: Option<&str>` parameter (origin/main merge). ## Frontend - `lib/detectProvider.ts` — pure key-format detector. Recognizes Anthropic, OpenAI (legacy/proj/svcacct via fixed infix), OpenRouter, Groq, xAI, Cerebras, Together, Perplexity, Fireworks (medium), bare sk- → DeepSeek (low + ambiguity-aware), plus localhost/127.0.0.1 patterns for Ollama / vLLM / llama.cpp. Includes ADMIN_ONLY_PROVIDER_ID sentinel for `sk-ant-admin01-` which we explicitly refuse to save. Key format wins over a prefilled default base URL; an explicit non- default base URL wins back for medium-confidence keys (e.g. Fireworks + api.openai.com host). Fixture strings construct the OpenAI infix via concat so GitHub's secret scanner doesn't regex-match an inline OpenAI-shaped service-account/project key (`detectProvider.test.mjs`, `settings-agent-provider.spec.ts`). - `lib/providerCatalog.ts` — declarative catalog (id, label, dialect, isLocal, default model + base URL, key-shape hint). Drives the picker, the auto-fill on detection, the local-provider placeholder enforcement, and the per-provider model field default. - `lib/agentProviderFormState.ts` — FormState shape + reducers. `applyProviderSwitch` is the single source of truth for what gets reset on a provider change (model when empty or still previous default; baseUrl when new provider has a default OR user hasn't edited it; clears previous default host for null-default providers; drops apiKey on switch TO a local provider). Used by both the manual picker and the auto-detect effect so the policy can't drift. - `lib/agentProviderSettingsApi.ts` + `hooks/useAgentProviderSettings.ts` — typed IPC wrappers + React-Query hooks (load / save / delete / envPresence). - `ui/AgentProviderSettingsCard.tsx` — the panel itself. Empty state with shell-env hint, identity-rotation banner, load-error banner, detected-provider badge, reveal/hide toggle, advanced section, inline provider-change warning, confirm-clear dialog. On save success the plaintext is wiped from form state + reveal toggles off, independent of any React-Query refresh effect (covers the structural- sharing identical-redacted-view edge case). - `ui/AgentProviderAdvancedFields.tsx`, `AgentProviderBanners.tsx`, `AgentProviderClearDialog.tsx` — split components. ## Per-agent dialog (sprout-agent special case) - `agents/ui/CreateAgentDialogSections.tsx` — Model + System prompt inputs are hidden for sprout-agent paths (those are owned globally). A note line points users to Settings → Agent Provider. - `agents/ui/EditAgentDialog.tsx` — passes `selectedProviderId="custom"` to the shared runtime fields so the agent-command input stays editable for existing rows; the system-prompt hide still resolves via `isSproutAgentPath`'s `agentCommand` arm. - `agents/ui/ManagedAgentRow.tsx` — "Model managed by Sprout settings" link is a span with role="button" + stopPropagation (was a nested `<button>` inside the row button — both invalid HTML and double- triggering). - `agents/lib/resolveAcpProviderId.ts` — TS / Rust alignment for inline-args resolution (Rust `known_acp_provider` strips args; TS now matches). ## Tests - Rust: 328 tests passing (R7 added 7; full agent_provider_settings suite at 44/44). - TS / node-test: 55 cases for the form-state reducer + provider detector. - Playwright integration: 12 settings-agent-provider scenarios (empty state + detection + save round-trip, identity-rotation banner, provider-change-warning, key-format-beats-prefilled-baseUrl, load- error banner, clear flow + Escape cancel, rotation banner a11y, local-provider switch with saved key, manual switch reset, detected- provider model reset, post-save key-input clear). ## just ci summary - Rust: 321/321 + 7 new validate_stored tests - Mobile: 336/336 - Desktop / web: format + biome + file-size + ts-check all green - Playwright integration agents + settings-agent-provider: 19/19 green ## Codex review history - Reviews #1, #2, #3, #5, #6 surfaced and fixed: non-loopback HTTP at save AND spawn, identity-mismatch fails closed, local-provider key leak prevention, inline-args resolver alignment, Zeroize on api_key before early-return, no Debug derive, control-char / length caps + trim, owner_pubkey v2 envelope, local-provider switch unblock, EditAgentDialog command field, model reset on detection switch, nested-button fix, `applyProviderSwitch` reducer extraction. - Review #7 (P2-UI + P2-Rust): clear form.apiKey + revealKey on save success; validate decrypted settings at spawn time. - Review #8: 9/10, no blocking findings. Signed-off-by: Tyler Longwell <109685178+tlongwell-block@users.noreply.github.com> Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@sprout-oss.stage.blox.sqprod.co>
wpfleger96
added a commit
that referenced
this pull request
May 22, 2026
…iew findings The original implementation created a second parallel Tauri command (discover_all_acp_providers) alongside the existing one to avoid changing the return type. This produced two commands, two hooks, two query keys, and two raw type converters. Consolidates into a single command returning the full catalog, with a useAvailableAcpProviders hook that type-narrows for callers needing non-null command/binaryPath. Also fixes: pipe deadlock in install command (#1), UTF-8 truncation panic (#2/#4), adds install concurrency guard (#11), exact provider ID match (#15), error display stdout fallback (#5), success banner suppression when already available (#12), misleading re-run text (#13), IIFE refactor in PersonaDialog (#14), hidden internal query lift (#7), configurable e2e mocks (#9), shared raw type exports (#8), and classify_provider unit tests (#10).
wpfleger96
added a commit
that referenced
this pull request
May 22, 2026
…iew findings The original implementation created a second parallel Tauri command (discover_all_acp_providers) alongside the existing one to avoid changing the return type. This produced two commands, two hooks, two query keys, and two raw type converters. Consolidates into a single command returning the full catalog, with a useAvailableAcpProviders hook that type-narrows for callers needing non-null command/binaryPath. Also fixes: pipe deadlock in install command (#1), UTF-8 truncation panic (#2/#4), adds install concurrency guard (#11), exact provider ID match (#15), error display stdout fallback (#5), success banner suppression when already available (#12), misleading re-run text (#13), IIFE refactor in PersonaDialog (#14), hidden internal query lift (#7), configurable e2e mocks (#9), shared raw type exports (#8), and classify_provider unit tests (#10).
wpfleger96
added a commit
that referenced
this pull request
May 22, 2026
…iew findings The original implementation created a second parallel Tauri command (discover_all_acp_providers) alongside the existing one to avoid changing the return type. This produced two commands, two hooks, two query keys, and two raw type converters. Consolidates into a single command returning the full catalog, with a useAvailableAcpProviders hook that type-narrows for callers needing non-null command/binaryPath. Also fixes: pipe deadlock in install command (#1), UTF-8 truncation panic (#2/#4), adds install concurrency guard (#11), exact provider ID match (#15), error display stdout fallback (#5), success banner suppression when already available (#12), misleading re-run text (#13), IIFE refactor in PersonaDialog (#14), hidden internal query lift (#7), configurable e2e mocks (#9), shared raw type exports (#8), and classify_provider unit tests (#10).
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.
Summary
react-markdown, GFM, and line-break supportfeatures/messagesand remove the old static chat dataTesting
lefthookran automatically duringgit pushcargo fmt --all -- --checkpnpm checkpnpm buildcargo check --manifest-path desktop/src-tauri/Cargo.tomlcargo clippy --workspace --all-targets -- -D warnings./scripts/run-tests.sh unit