feat: P2 — tool approvals, streaming markdown, and tool-call diffs - #6
Merged
Conversation
Builds the P2 slice from the mobile design doc (§6-§8, §10): a
provider-agnostic approve/deny bar, a markdown-rendered streaming
transcript, and Edit/Write diffs — all on the already-published
@codeoid/{protocol,core}@0.1.0 (no package bump; the approval wire
contract already ships there).
- ApprovalBar: binary approve/deny over `session.approve`. Detection
delegates to the shared @codeoid/core `findPendingApproval` kernel via
usePendingApproval (throttled transcript + live session status), so
mobile stays byte-for-byte consistent with the web bar. Provider-
agnostic for free: claude/codex/pi/acp all normalize to this one wire
contract in the daemon, so the bar never branches on provider. The
in-flight machine stays disabled until the tool-state delta lands and
is keyed by approvalId so each approval starts clean. v1 is binary-only
— updatedInput is deferred (the daemon doesn't broadcast patchableKeys
to clients and codex ignores it).
- MessageRow: role dispatch. assistant/thinking render markdown from the
authoritative `content` string (matching web); tool_call -> ToolCard.
- ToolCard: tool-call state machine (phase badge + per-phase body) with a
persistent Edit/Write diff across phases, reconstructed from tool.input
with jsdiff and collapsed to +/-2 context lines (src/lib/diff.ts).
- Transcript list swapped FlatList -> FlashList v2 with chat-style
maintainVisibleContentPosition (startRenderingFromBottom + autoscroll).
- Streaming perf: rAF-coalesced store subscriptions (useTranscript plus a
new useMessageVersion) so a 20-40/s token stream repaints once per
frame; MarkdownText memoized by text, and rows re-render off their own
message version (the store mutates messages in place, so reference
identity can't drive per-row updates).
Deps are all Expo-Go-compatible — this slice must run over Tailscale with
no custom dev build: @shopify/flash-list@2.0.2 + react-native-svg@15.15.4
(both bundled in Expo Go SDK 57), react-native-marked@8.1.1 (pure-JS
markdown; its only native peer is svg), diff@9. Deliberately NOT
react-native-streamdown — its native peers (react-native-enriched-markdown,
react-native-worklets) are not in Expo Go and would force a dev build.
Verified: tsc + eslint clean; expo export (web) bundles 952 modules;
src/lib/diff.ts unit-tested (14 assertions incl. context collapse and line
numbering); a read-only protocol probe against the live daemon (auth ->
list -> attach) observed 525 real messages across all roles and confirmed
every shape matches the renderers. A live approval click-through (needs a
session actively blocked on a tool) pends an on-device/browser session.
Signed-off-by: Yash Datta <saucam@gmail.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
KunalJavelin
approved these changes
Jul 25, 2026
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.
What
P2 for codeoid-mobile: the provider-agnostic approve/deny bar, a markdown streaming transcript, and Edit/Write tool diffs. Mobile-only — no daemon or protocol changes; builds on the already-published
@codeoid/{protocol,core}@0.1.0.This is the "approvals + streaming first" slice. Push notifications are a separate follow-up (they need protocol + daemon + a relay).
Highlights
session.approve. Detection reuses the shared@codeoid/corefindPendingApprovalkernel (usePendingApproval= throttled transcript + live session status), so it stays consistent with the web bar. Provider-agnostic by construction: claude / codex / pi / acp all normalize to the onewaiting_confirmation+approvalIdwire contract in the daemon, so the bar never branches on provider. The in-flight machine stays disabled until the tool-state delta lands and is keyed byapprovalIdso each approval starts clean.FlashList v2(chat-style stick-to-bottom viamaintainVisibleContentPosition),MessageRowrole dispatch,MarkdownText(react-native-marked, memoized by text). rAF-coalesced store subscriptions + a per-rowuseMessageVersionso a 20-40/s token stream repaints once per frame and streamed content never freezes behind memoization (the store mutates messages in place, so ref identity can't drive per-row updates).tool.inputwith jsdiff and collapsed to ±2 context lines (src/lib/diff.ts).Library choice
Deliberately react-native-marked (pure-JS; only native peer is react-native-svg, bundled in Expo Go SDK 57), not the design doc's react-native-streamdown — streamdown's native peers (
react-native-enriched-markdown,react-native-worklets) aren't in Expo Go and would force a custom dev build. Keeping this slice Expo-Go-runnable is the whole point of shipping it first (usable over Tailscale today). FlashList 2.0.2 + react-native-svg 15.15.4 are pinned to the versions Expo Go SDK 57 bundles.Scope notes
updatedInput(edit-then-approve) is deferred: the daemon doesn't broadcast a tool'spatchableKeysto clients, and codex ignoresupdatedInput.parts[]rendering (code/table/tree/image beyond plain text) is a follow-up; assistant text renders from the authoritativecontentstring, matching the web UI.Verification
tsc --noEmit+eslint .clean.expo export --platform webbundles 952 modules.src/lib/diff.tsunit-tested — 14 assertions incl. context collapse + line numbering.🤖 Generated with Claude Code