Files
buzz/desktop
Hayt cef78d723d feat(desktop): render permission-request sentinel card in thread timeline
Implements the desktop side of issue #4938: a kind-9 sentinel published by
buzz-acp is parsed and displayed as an interactive card in the thread view.

Parser (permissionRequest.ts):
- Discriminated union PermissionRequestPending | PermissionRequestResolved
  aligned to the frozen schema (event b31c716e): requestNonce, sessionId,
  turnId, optionIds[], labels{}, hasDurableRule, durableRuleNote, outcome,
  chosenOptionId, originalEventId.
- All untrusted display strings capped at 200 chars; optionIds bounded to 10.
- extractPermissionRequest() and stripPermissionRequestSentinel() are
  render-safe (never throw).

Compute helper (computePermissionRequest.ts):
- D1 signer gate: kind-9 must be signed by the channel's known agent pubkey.
- Edit authenticity: resolved state accepted only from kind-40003 signed by
  the original agent (not owner, not attacker).
- selectProseOrPermission() mirrors selectProseOrNudge() from configNudge.

Card (permission-request-card.tsx):
- Pending: renders option buttons from optionIds + labels[optionId].
  Deny-style button heuristic uses label text (opaque optionIds carry no
  semantic). ExpiryCountdown ticks down to expiresAt; expired cards show
  'Timed out'. D5 durable-rule disclosure (durableRuleNote) shown below
  buttons when hasDurableRule is true.
- Resolved: shows outcome label derived from outcome + chosenOptionId.
- Actionable buttons gated: isOwner && state === 'pending' only.
- sendPermissionDecision() is fire-and-forget; button disables on click with
  'Decision sent'; re-enables on relay error so the user can retry.

Integration (MessageRow.tsx, formatTimelineMessages.ts, types.ts):
- editSignerPubkey threaded from formatTimelineMessages through TimelineMessage
  so the edit authenticity gate has the raw event signer without re-querying.
- PermissionRequestCardBlock wraps useIdentityQuery to resolve the current
  viewer, computes isOwner, and renders the card when a trusted sentinel is
  present. React.memo with custom comparator keeps MessageRow re-render budget
  clean.

Auth helper (permissionRequestAuthPubkey.ts):
- getPermissionRequestAgentPubkey() returns the agent pubkey only for
  known-agent-signed kind-9 events, mirroring getConfigNudgeAuthorPubkey.

Tests: 33 named tests covering all 6 frozen fixtures verbatim plus rejection
cases (bad version, unknown state, size bounds, malformed JSON, field
invariants) and the full signer/edit-authenticity gate matrix.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-08-08 13:17:54 -04:00
..
2026-08-03 21:51:17 -04:00
2026-03-09 13:02:11 -07:00
2026-06-11 15:06:24 +00:00
2026-03-09 13:02:11 -07:00

Buzz

Desktop chat shell with:

  • Tauri + React + TypeScript + Vite
  • Tailwind CSS
  • shadcn/ui-ready shared components
  • Biome (lint/format/check)
  • Feature-driven frontend structure

Scripts

  • pnpm dev - run the web frontend
  • pnpm tauri dev - run the desktop app
  • pnpm build - typecheck and build frontend
  • pnpm typecheck - TypeScript checks
  • pnpm lint - Biome lint
  • pnpm format - Biome format (write)
  • pnpm check - Biome check

Structure

  • src/shared - reusable app-wide code (ui, lib, styles)
  • src/features - feature modules (vertical slices)
  • src/app - top-level app composition