Skip to content

Couple of small UI/UX improvements#13

Merged
achimala merged 14 commits into
achimala:mainfrom
dhvcc:feat/ux-improvements
Mar 1, 2026
Merged

Couple of small UI/UX improvements#13
achimala merged 14 commits into
achimala:mainfrom
dhvcc:feat/ux-improvements

Conversation

@dhvcc
Copy link
Copy Markdown
Contributor

@dhvcc dhvcc commented Feb 28, 2026

  • Token usage + account rate limits pipeline: protocol schema, codex API client, server endpoint (/api/account/rate-limits), web API parsing, and header usage badges.
  • Sidebar state/features: persisted collapse map, persisted manual order, persisted project colors, drag-to-reorder groups, color picker UI.
  • Per-agent model/mode fetching: agentId-aware model/collab-mode endpoints and client calls.
  • OpenCode adapter got real model/provider/agent listing support (instead of mostly disabled model/mode capability).
  • PWA wiring updates (vite, main.tsx, pwa.d.ts, deps).

Preview

Screenshot 2026-03-01 at 12 32 26 AM Screenshot 2026-03-01 at 12 33 14 AM

Mobile preview

image

Summary by CodeRabbit

  • New Features

    • Rate limit monitoring and account usage visibility
    • Collaboration modes and model selection per thread
    • Per-project/group custom colors with color picker
    • Group drag-and-drop reordering in sidebar
    • Token usage tracking and display badges
    • Progressive Web App (PWA) support with offline access and auto-update
  • Improvements

    • Enhanced sidebar preferences persistence (colors, order, collapse state)
    • Mobile safe-area inset handling

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18ffe39 and 6e0386f.

📒 Files selected for processing (7)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/unified/adapter.ts
  • apps/web/src/App.tsx
  • apps/web/src/lib/code-language.ts
  • apps/web/test/app.test.tsx
  • packages/codex-api/src/app-server-client.ts
  • packages/unified-surface/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/codex-api/src/app-server-client.ts
  • apps/server/src/agents/adapters/codex-agent.ts

📝 Walkthrough

Walkthrough

This PR introduces rate-limit visibility across the platform, enhances OpenCode agent capabilities with model/collaboration mode selection, refactors sidebar preferences to centralized storage utilities, integrates PWA support with offline capabilities, improves CodexAgent IPC frame buffering per thread, and updates supporting schemas and type definitions.

Changes

Cohort / File(s) Summary
Rate Limits API Layer
packages/codex-protocol/src/app-server.ts, packages/codex-api/src/app-server-client.ts, apps/server/src/http-schemas.ts, apps/server/src/index.ts, apps/web/src/lib/api.ts
Added AppServerGetAccountRateLimitsResponse schema and parsing, new readAccountRateLimits() method in AppServerClient, new GET /api/account/rate-limits HTTP endpoint with error handling and adapter resolution via capability registry.
Rate Limits Agent Adapter
apps/server/src/agents/types.ts, apps/server/src/agents/adapters/codex-agent.ts
Added canReadRateLimits capability flag and optional readRateLimits() method to agent interfaces; implemented readRateLimits() in CodexAgentAdapter via appClient with per-thread IPC buffering refactor.
CodexAgent IPC Buffering
apps/server/src/agents/adapters/codex-agent.ts
Replaced per-conversation frame buffering with per-thread buffering (streamEventsByThreadId map, capped at 400 frames); broadened thread ID extraction to handle both request and broadcast frames from conversationId, threadId, or turnId.
OpenCode Agent Capabilities
apps/server/src/agents/adapters/opencode-agent.ts
Enabled canListModels, canListCollaborationModes, canSetCollaborationMode capabilities; added per-thread mode tracking (threadModeById map); implemented listModels(), listCollaborationModes(), and setCollaborationMode() methods with model parsing and input modality mapping; integrated selected mode into message dispatch.
OpenCode Service Enhancements
packages/opencode-api/src/service.ts
Added listProviders() and listAgents() methods to OpenCodeMonitorService; introduced OpenCodeProviderModel and OpenCodeProviderSummary types; extended OpenCodeSendMessageInput with optional agent and model fields for propagation through message/session creation.
Sidebar Preferences Refactor
apps/web/src/lib/sidebar-prefs.ts, apps/web/src/App.tsx
Introduced new sidebar-prefs module with localStorage read/write helpers for sidebar order, collapse state, and project colors (GroupColor type and groupColors palette); refactored App.tsx to use centralized utilities instead of inline persistence; removed inline localStorage logic.
Rate Limits UI Integration
apps/web/src/App.tsx, apps/web/src/lib/api.ts
Added rate limits fetching in loadCoreData, introduced parseTokenUsageInfo() and getLatestTokenUsageFromStreamEvents() helpers, imported token usage schemas (snake_case/camelCase), rendered usage badges in header with thread/provider gating, exported read/write helpers from sidebar-prefs.
Sidebar UI & UX Enhancements
apps/web/src/App.tsx
Added per-group color support with color picker (Palette dropdown) and drag-and-drop reordering; added color accent bar and drag handle to group headers; new UsageRing component for circular token usage indicator; adjusted input area padding for safe-area insets.
PWA Support
apps/web/package.json, apps/web/src/main.tsx, apps/web/src/pwa.d.ts, apps/web/vite.config.ts
Added vite-plugin-pwa dependency; configured PWA plugin with manifest (name, start_url, theme_color), Workbox asset caching, and autoUpdate registerType; added registerSW({ immediate: true }) in main.tsx; created pwa.d.ts with registerSW function type declaration.
UI & Schema Updates
apps/web/src/components/ui/tooltip.tsx, apps/web/src/lib/code-language.ts, apps/web/src/components/ChatTimeline.tsx, packages/unified-surface/src/index.ts, apps/server/src/unified/adapter.ts
Tooltip styling refactor (popover colors, border, shadow); EXTENSION_LANGUAGE_MAP made readonly with added c/cc/hpp mappings; ChatTimeline safe-area-inset adjustments; UnifiedThread schema adds optional latestTokenUsageInfo field; mapThread conditionally includes latestTokenUsageInfo in output.
Test Coverage
apps/web/test/app.test.tsx
Added UI interaction test for project group switch; added new test validating manual sidebar order preservation over automatic recency sorting using localStorage seeding and DOM position assertions.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Poem

🐰 A rabbit hops through limits now,
Each thread buffered, each mode set—how!
Colors bloom, sidebars align,
PWA stores when offline's fine, 📦✨
Better burrows for better rides!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title "Couple of small UI/UX improvements" is overly vague and does not accurately reflect the substantial scope of changes. The PR includes major features like rate-limit integration, PWA setup, sidebar persistence/reordering, per-agent model selection, and OpenCode adapter expansion—far exceeding "a couple of small" improvements. Update the title to accurately convey the major features added, such as: "Add rate-limits integration, PWA setup, sidebar state persistence, and agent model selection features" or similar to properly scope expectations.
Docstring Coverage ⚠️ Warning Docstring coverage is 2.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/src/agents/adapters/codex-agent.ts (1)

161-175: ⚠️ Potential issue | 🟠 Major

Token-usage broadcasts are now mixed into live-state reduction input.

These events are stored in streamEventsByThreadId, but readLiveState() reduces that store as stream-state events. This can trigger false validation warnings and noisy pruning on normal token updates.

Suggested fix
   for (const event of rawEvents) {
+    if (event.type !== "broadcast" || event.method !== "thread-stream-state-changed") {
+      continue;
+    }
     try {
       events.push(parseThreadStreamStateChangedBroadcast(event));
       validRawEvents.push(event);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/agents/adapters/codex-agent.ts` around lines 161 - 175, The
reducer is mistakenly treating "thread/tokenUsage/updated" as a stream-state
event; update the handling in the codex-agent branch (the block around the
frame.method checks and the conversationId extraction) so token-usage broadcasts
are ignored for live-state reduction: only process "thread-stream-state-changed"
into streamEventsByThreadId (skip/return early for "thread/tokenUsage/updated"),
and ensure the conversationId is derived from params only for the stream-state
branch (i.e., do not map token updates into the streamEventsByThreadId store
used by readLiveState()). This keeps readLiveState() reductions free of
token-usage noise.
🧹 Nitpick comments (2)
apps/server/src/agents/adapters/opencode-agent.ts (1)

88-99: Avoid silent per-directory failures in session listing.

The empty catch drops directory-specific errors without visibility, which makes partial data loss hard to diagnose in production.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/agents/adapters/opencode-agent.ts` around lines 88 - 99, The
current per-directory loop around this.service.listSessions silently swallows
errors; update the catch to capture the error (e.g., catch (err)) and log a
clear, contextual message including the directory and the error details so
failures are visible when listing sessions for each directory (refer to the for
(const directory of directories) loop, this.service.listSessions, the sessions
map and this.threadDirectoryById); keep the continue behavior after logging so
other directories are still processed.
apps/web/test/app.test.tsx (1)

390-396: Make the rate-limit mock configurable per test.

This branch hardcodes /api/account/rate-limits to an error path for all tests, which blocks success-path coverage (including usage badge rendering). A fixture toggle here would let tests exercise both 200 and 400 behaviors.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/test/app.test.tsx` around lines 390 - 396, The current mock that
returns a 400 for pathname === "/api/account/rate-limits" is hardcoded and
prevents tests from exercising the success path; change the mock to consult a
test-configurable flag (e.g., a global or exported fixture like
__mockRateLimitsResponse or setMockRateLimitResponse) instead of returning a
fixed error Response, defaulting to the existing 400 behavior when the flag is
unset, and allow tests to toggle it to a 200 JSON payload so tests can cover
both error and success (badge) rendering paths; update the mock branch that
creates the Response for "/api/account/rate-limits" to branch based on that
configurable value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/http-schemas.ts`:
- Around line 79-84: The parseQuery function currently uses the forbidden
unknown type for its second parameter and should instead use Zod's precise
input/output types; update the signature of parseQuery(schema: Schema, value:
unknown): z.infer<Schema> to use z.input<Schema> for the value parameter and
z.output<Schema> for the return type, keeping the function body as
schema.parse(value) and ensuring the function name parseQuery and the generic
Schema extends z.ZodTypeAny remain unchanged.

In `@apps/server/src/index.ts`:
- Around line 744-747: The route is letting parseQuery(AgentScopedQuerySchema,
{...}) throw and bubble up as a 500; wrap the parseQuery call in a try/catch (or
use a validation guard) in the handlers that call parseQuery (the `/api/models`
handler around parseQuery + resolveCapabilityAdapter("canListModels",
query.agentId) and likewise the `/api/collaboration-modes` handler) and return a
400 response when validation fails instead of letting the error propagate;
ensure you only call resolveCapabilityAdapter with a validated query.agentId and
send a clear 400 error payload when parseQuery throws/returns validation errors.

In `@apps/web/package.json`:
- Line 37: Move "vite-plugin-pwa" out of the runtime dependencies into
devDependencies in apps/web/package.json: remove the "vite-plugin-pwa" entry
from the top-level "dependencies" object and add the same version entry under
"devDependencies" so the plugin remains available for build time used by
apps/web/vite.config.ts (which generates virtual:pwa-register) but is not
shipped to production; after editing, reinstall/update the lockfile
(npm/yarn/pnpm) to ensure the lockfile reflects the change.

In `@apps/web/src/App.tsx`:
- Around line 967-969: The current update only sets rateLimits when a successful
read returns (if (nrl) setRateLimits(nrl)), which leaves stale values after
failures; change the logic in the update path(s) that call setRateLimits (the
block with if (nrl) setRateLimits(nrl) and the analogous block at 1368-1370) so
that when the read fails or nrl is falsy you explicitly clear the state (call
setRateLimits(null) or setRateLimits(undefined) / an empty object) instead of
doing nothing, ensuring quota badges don't show stale data.
- Around line 1834-1845: The color swatch buttons are unlabeled and need
accessible names so screen readers know what they do; update the button in the
swatch map (the element that calls setProjectColors with [group.key] and color
as GroupColor) to include a clear accessible label (e.g., an aria-label like
"Set {group.name} color to {color}" or add visually-hidden text inside the
button) that references the group (group.key or group.name) and the color value
so assistive tech users understand the action.

In `@apps/web/src/lib/sidebar-prefs.ts`:
- Around line 18-25: The readStorage function currently swallows JSON parse and
Zod validation errors and returns defaults; change it to fail hard and surface
clear errors instead: remove the broad silent catch and, when JSON.parse or
schema.parse throws, throw a new Error (or rethrow) that includes the storage
key (STORAGE_PREFIX + '.' + key) and the underlying error message so callers
know which stored value is corrupted; keep using schema.parse for validation but
do not fall back to defaultValue—fail fast in readStorage (function name:
readStorage, symbol: schema.parse, STORAGE_PREFIX) so persisted-data mismatches
are reported.

In `@packages/opencode-api/src/service.ts`:
- Around line 132-147: listAgents (and similar methods listSessions,
listProjectDirectories, listProviders) currently cast API responses with "as
Agent[]" without runtime validation; add a Zod schema for the expected Agent
shape, import z from 'zod', validate the response (use schema.parse or
schema.safeParse) against result.data returned by client.app.agents, and throw
or surface a clear error if validation fails; update each method to return the
parsed value instead of the raw assertion and reuse/declare shared Zod schemas
for other list* methods matching their expected types.
- Around line 119-129: The response is being blindly cast to a TypeScript type
(payload) instead of validated at runtime; add a Zod schema (e.g.,
ListProvidersResponseSchema) that matches { all?: array of
OpenCodeProviderSummary, default?: record(string,string), connected?: array of
string } and use schema.parse() or schema.safeParse(result.data) inside
listProviders (or the function returning payload) to validate result.data,
throwing or logging a clear error on failure; then map the validated value to
the returned shape (all -> all, default -> defaults, connected -> connected).

---

Outside diff comments:
In `@apps/server/src/agents/adapters/codex-agent.ts`:
- Around line 161-175: The reducer is mistakenly treating
"thread/tokenUsage/updated" as a stream-state event; update the handling in the
codex-agent branch (the block around the frame.method checks and the
conversationId extraction) so token-usage broadcasts are ignored for live-state
reduction: only process "thread-stream-state-changed" into
streamEventsByThreadId (skip/return early for "thread/tokenUsage/updated"), and
ensure the conversationId is derived from params only for the stream-state
branch (i.e., do not map token updates into the streamEventsByThreadId store
used by readLiveState()). This keeps readLiveState() reductions free of
token-usage noise.

---

Nitpick comments:
In `@apps/server/src/agents/adapters/opencode-agent.ts`:
- Around line 88-99: The current per-directory loop around
this.service.listSessions silently swallows errors; update the catch to capture
the error (e.g., catch (err)) and log a clear, contextual message including the
directory and the error details so failures are visible when listing sessions
for each directory (refer to the for (const directory of directories) loop,
this.service.listSessions, the sessions map and this.threadDirectoryById); keep
the continue behavior after logging so other directories are still processed.

In `@apps/web/test/app.test.tsx`:
- Around line 390-396: The current mock that returns a 400 for pathname ===
"/api/account/rate-limits" is hardcoded and prevents tests from exercising the
success path; change the mock to consult a test-configurable flag (e.g., a
global or exported fixture like __mockRateLimitsResponse or
setMockRateLimitResponse) instead of returning a fixed error Response,
defaulting to the existing 400 behavior when the flag is unset, and allow tests
to toggle it to a 200 JSON payload so tests can cover both error and success
(badge) rendering paths; update the mock branch that creates the Response for
"/api/account/rate-limits" to branch based on that configurable value.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb81b58 and 00bdd30.

⛔ Files ignored due to path filters (3)
  • bun.lock is excluded by !**/*.lock
  • packages/codex-protocol/src/generated/app-server/GetAccountRateLimitsResponseSchema.ts is excluded by !**/generated/**
  • packages/codex-protocol/src/generated/app-server/index.ts is excluded by !**/generated/**
📒 Files selected for processing (17)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/agents/adapters/opencode-agent.ts
  • apps/server/src/agents/types.ts
  • apps/server/src/http-schemas.ts
  • apps/server/src/index.ts
  • apps/web/package.json
  • apps/web/src/App.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/lib/api.ts
  • apps/web/src/lib/sidebar-prefs.ts
  • apps/web/src/main.tsx
  • apps/web/src/pwa.d.ts
  • apps/web/test/app.test.tsx
  • apps/web/vite.config.ts
  • packages/codex-api/src/app-server-client.ts
  • packages/codex-protocol/src/app-server.ts
  • packages/opencode-api/src/service.ts

Comment thread apps/server/src/http-schemas.ts
Comment thread apps/server/src/index.ts Outdated
Comment thread apps/web/package.json Outdated
Comment thread apps/web/src/App.tsx Outdated
Comment thread apps/web/src/App.tsx
Comment thread apps/web/src/lib/sidebar-prefs.ts
Comment thread packages/opencode-api/src/service.ts
Comment thread packages/opencode-api/src/service.ts
@achimala
Copy link
Copy Markdown
Owner

achimala commented Mar 1, 2026

Very cool! Does it work on mobile as well?

I also made some sidebar UX improvements so I think we conflict a bit, would you mind merging the latest?

@dhvcc
Copy link
Copy Markdown
Contributor Author

dhvcc commented Mar 1, 2026

Very cool! Does it work on mobile as well?

I also made some sidebar UX improvements so I think we conflict a bit, would you mind merging the latest?

Hi 👋
I'll merge the upstream and attach the new mobile screenshots

@dhvcc
Copy link
Copy Markdown
Contributor Author

dhvcc commented Mar 1, 2026

@achimala merged the new code into this branch, added mobile preview screenshot

Also removed some of my model selection logic customization since this feature doesn't work for me at all at the moment

main doesn't even build for me at the moment also with [plugin:vite:import-analysis] Failed to resolve import "@/lib/command-action-ui" from "src/components/CommandBlock.tsx". Does the file exist?

If you could help with the testing of this branch - would be great. I don't know all the internals and frankly I don't want to change them, I want this PR to be purely the UI/UX updates. If I broke something on the backend - would be bad

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
apps/server/src/http-schemas.ts (1)

28-33: ⚠️ Potential issue | 🟠 Major

Replace forbidden unknown in parseQuery with strict Zod input/output types.

Line 30 uses unknown, which violates the repo rule and weakens the type boundary for parsed query input.

Proposed fix
 export function parseQuery<Schema extends z.ZodTypeAny>(
   schema: Schema,
-  value: unknown
-): z.infer<Schema> {
+  value: z.input<Schema>
+): z.output<Schema> {
   return schema.parse(value);
 }
#!/bin/bash
# Verify parseQuery signature no longer uses forbidden unknown and uses strict Zod boundaries.
rg -nP 'export function parseQuery|value:\s*unknown|z\.infer<Schema>|z\.input<Schema>|z\.output<Schema>' apps/server/src/http-schemas.ts

As per coding guidelines: “You CANNOT use as any or unknown in this codebase, they are FORBIDDEN.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/http-schemas.ts` around lines 28 - 33, The parseQuery
function currently accepts value: unknown which violates the forbidden-unknown
rule; update the signature to use strict Zod input/output types by changing the
parameter type to z.input<Schema> (and the return type to z.output<Schema> or
keep z.infer<Schema> if preferred) so the function becomes parseQuery<Schema
extends z.ZodTypeAny>(schema: Schema, value: z.input<Schema>): z.output<Schema>;
keep the body as schema.parse(value) and ensure imports/types still resolve for
z.input/z.output usage.
🧹 Nitpick comments (2)
apps/web/src/lib/command-action-ui.ts (1)

27-33: Minor redundancy in displayPath usage.

The path variable is already trimmed on line 37 (action.path?.trim() ?? ""), and line 55 checks !path before calling displayPath(path). Inside displayPath, the path is trimmed again and checked for emptiness—both operations are redundant at that point. You could simplify by using path directly.

♻️ Optional simplification
 function displayPath(path: string): string {
-  const cleaned = path.trim();
-  if (!cleaned) {
-    return "(path)";
-  }
-  return cleaned;
+  return path || "(path)";
 }

Or inline in the listFiles branch:

   if (actionType === "listFiles") {
     if (!path) {
       return { iconKey: "listFiles", text: "list files" };
     }
     return {
       iconKey: "listFiles",
-      text: truncate(displayPath(path)),
-      tooltip: displayPath(path)
+      text: truncate(path),
+      tooltip: path
     };
   }

Also applies to: 54-63

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/lib/command-action-ui.ts` around lines 27 - 33, displayPath
currently re-trims and re-checks emptiness even though callers already pass a
trimmed non-empty path (see action.path?.trim() and the preceding !path guard);
update displayPath (and call sites in the listFiles branch) to assume the input
is already trimmed and non-empty by removing the extra trim()/emptiness logic
from the displayPath function and simply return the passed string, and adjust
any callers that relied on its fallback "(path)" to handle empty-path formatting
instead.
apps/web/src/App.tsx (1)

2030-2034: Consider adding a comment to clarify the codex-specific loading logic.

The conditional combines multiple concerns (debug tab, codex provider, stream events flag). A brief comment would help future maintainers understand why stream events are always loaded for codex threads.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/App.tsx` around lines 2030 - 2034, Add a brief inline comment
above the shouldLoadStreamEvents conditional explaining the codex-specific
behavior: that stream events are always loaded for threads where threadAgentId
=== "codex" (even if includeStreamEvents is false) to support live
codex-provider updates, and that the rest of the condition gates loading to the
debug tab and read-permissions (canReadStreamEvents) and to the currently
selected thread (selectedThreadIdRef/current threadId). Place this comment right
above the declaration of shouldLoadStreamEvents and reference the variables used
(shouldLoadStreamEvents, canReadStreamEvents, activeTabRef, threadAgentId,
selectedThreadIdRef, includeStreamEvents) so future maintainers understand why
codex threads bypass includeStreamEvents.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/agents/adapters/opencode-agent.ts`:
- Around line 183-186: The code currently accepts and stores raw model strings
in setCollaborationMode and lets parseModelSelection return null later in
sendMessage, silently downgrading invalid values; change setCollaborationMode to
validate the incoming model string immediately (use parseModelSelection or a
dedicated validator) and throw a clear error if it returns null/invalid, and
only store a validated model into threadModeById; update any callers/tests that
expect silent nulls accordingly so invalid model formats fail fast instead of
being converted during sendMessage.

In `@apps/server/src/index.ts`:
- Around line 968-971: The response currently uses jsonResponse(res, 400, { ok:
false, error: "No agent supports rate limit reading" }) which misclassifies a
backend capability/state issue as a bad client request; update the status code
to a server-capability code (e.g., jsonResponse(res, 503, ...) or 501) so
clients and monitoring see a server-side failure. Locate the call to
jsonResponse in index.ts where the error string "No agent supports rate limit
reading" is returned and replace the 400 status with 503 (or 501) while keeping
the payload unchanged.

In `@apps/web/vite.config.ts`:
- Around line 13-20: The Web manifest configured in manifest (vite.config.ts) is
missing an icons array which prevents PWA installability; add an icons field to
the manifest with entries for 192x192 and 512x512 PNGs (e.g. { src:
"/icons/icon-192.png", sizes: "192x192", type: "image/png" } and { src:
"/icons/icon-512.png", sizes: "512x512", type: "image/png" }), ensure those PNG
files exist in the app’s public assets (e.g. apps/web/public/icons/) and that
Vite is serving the public directory (confirm publicDir or static asset config
so /icons/* is reachable) before committing the manifest change.

In `@packages/codex-protocol/src/app-server.ts`:
- Around line 28-29: The local schema wrapper
AppServerGetAccountRateLimitsResponseBaseSchema currently calls .passthrough()
on GeneratedGetAccountRateLimitsResponseSchema which is redundant and relaxes
validation; remove the .passthrough() call so
AppServerGetAccountRateLimitsResponseBaseSchema simply references
GeneratedGetAccountRateLimitsResponseSchema directly, and audit other local
wrappers in this file that wrap generated schemas (e.g., other
AppServer*...BaseSchema declarations) to remove any additional .passthrough()
uses to enforce strict validation.

---

Duplicate comments:
In `@apps/server/src/http-schemas.ts`:
- Around line 28-33: The parseQuery function currently accepts value: unknown
which violates the forbidden-unknown rule; update the signature to use strict
Zod input/output types by changing the parameter type to z.input<Schema> (and
the return type to z.output<Schema> or keep z.infer<Schema> if preferred) so the
function becomes parseQuery<Schema extends z.ZodTypeAny>(schema: Schema, value:
z.input<Schema>): z.output<Schema>; keep the body as schema.parse(value) and
ensure imports/types still resolve for z.input/z.output usage.

---

Nitpick comments:
In `@apps/web/src/App.tsx`:
- Around line 2030-2034: Add a brief inline comment above the
shouldLoadStreamEvents conditional explaining the codex-specific behavior: that
stream events are always loaded for threads where threadAgentId === "codex"
(even if includeStreamEvents is false) to support live codex-provider updates,
and that the rest of the condition gates loading to the debug tab and
read-permissions (canReadStreamEvents) and to the currently selected thread
(selectedThreadIdRef/current threadId). Place this comment right above the
declaration of shouldLoadStreamEvents and reference the variables used
(shouldLoadStreamEvents, canReadStreamEvents, activeTabRef, threadAgentId,
selectedThreadIdRef, includeStreamEvents) so future maintainers understand why
codex threads bypass includeStreamEvents.

In `@apps/web/src/lib/command-action-ui.ts`:
- Around line 27-33: displayPath currently re-trims and re-checks emptiness even
though callers already pass a trimmed non-empty path (see action.path?.trim()
and the preceding !path guard); update displayPath (and call sites in the
listFiles branch) to assume the input is already trimmed and non-empty by
removing the extra trim()/emptiness logic from the displayPath function and
simply return the passed string, and adjust any callers that relied on its
fallback "(path)" to handle empty-path formatting instead.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00bdd30 and 587170e.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • packages/codex-protocol/src/generated/app-server/index.ts is excluded by !**/generated/**
📒 Files selected for processing (17)
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/agents/adapters/opencode-agent.ts
  • apps/server/src/agents/types.ts
  • apps/server/src/http-schemas.ts
  • apps/server/src/index.ts
  • apps/server/src/unified/adapter.ts
  • apps/web/package.json
  • apps/web/src/App.tsx
  • apps/web/src/components/ChatTimeline.tsx
  • apps/web/src/components/ui/tooltip.tsx
  • apps/web/src/lib/api.ts
  • apps/web/src/lib/code-language.ts
  • apps/web/src/lib/command-action-ui.ts
  • apps/web/src/main.tsx
  • apps/web/vite.config.ts
  • packages/codex-api/src/app-server-client.ts
  • packages/codex-protocol/src/app-server.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/web/src/main.tsx
  • apps/web/src/lib/api.ts
  • packages/codex-api/src/app-server-client.ts
  • apps/server/src/agents/adapters/codex-agent.ts
  • apps/server/src/agents/types.ts

Comment thread apps/server/src/agents/adapters/opencode-agent.ts
Comment thread apps/server/src/index.ts
Comment thread apps/web/vite.config.ts
Comment thread packages/codex-protocol/src/app-server.ts
@achimala
Copy link
Copy Markdown
Owner

achimala commented Mar 1, 2026

@achimala merged the new code into this branch, added mobile preview screenshot

Also removed some of my model selection logic customization since this feature doesn't work for me at all at the moment

main doesn't even build for me at the moment also with [plugin:vite:import-analysis] Failed to resolve import "@/lib/command-action-ui" from "src/components/CommandBlock.tsx". Does the file exist?

If you could help with the testing of this branch - would be great. I don't know all the internals and frankly I don't want to change them, I want this PR to be purely the UI/UX updates. If I broke something on the backend - would be bad

Sorry about that, the breakage should be fixed now. Some files got dropped from the merge yesterday :(

Yep I'm testing out the branch now

@achimala
Copy link
Copy Markdown
Owner

achimala commented Mar 1, 2026

I tested it out and fixed a couple new merge conflicts, and improved the mobile layout a little to avoid the new stuff taking up so much space. This works great, thanks for the contribution!

@achimala achimala merged commit 1c99cbf into achimala:main Mar 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants