Skip to content

Dev env setup, opencode SDK v2 migration, model picker, and main-flow cleanups - #58

Open
tanishqkancharla wants to merge 15 commits into
mainfrom
cursor/env-setup
Open

tanishqkancharla wants to merge 15 commits into
mainfrom
cursor/env-setup

Conversation

@tanishqkancharla

@tanishqkancharla tanishqkancharla commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Environment setup, an opencode SDK v2 migration, a model picker, removal of the repo-level opencode config, main-flow "knot" cleanups, and a fix for a stuck busy/"steering" state.

Environment setup

  • .cursor/environment.json + idempotent .cursor/install.sh: pnpm install + build, OpenCode CLI install and PATH wiring, best-effort VS Code install.
  • Restores opencode OAuth credentials from an OPENCODE_AUTH_JSON Cursor secret into ~/.local/share/opencode/auth.json.

Remove repo-level opencode config

  • Deleted .opencode/ and opencode.json; the extension now uses the user's global/default opencode config.

Migrate to opencode SDK v2

  • Bumped @opencode-ai/sdk 1.2.14 → 1.18.30; adapted session.messages() to the v2 { info, parts }[] shape; aligned FileDiffSchema with the v2 snapshot-diff shape.

Model picker

  • ModelSwitcher dropdown from config.providers(), grouped by provider; selected model sent with each prompt and persisted per-session with a global default. Opens below/right-aligned when the input is at the top.

Main-flow cleanups

  • Derive message text from parts (removed the Message.text cache + 3 hand-maintained resync blocks; added ensureMessage).
  • Shared deriveFileChanges*/deriveContextInfo helpers.
  • sendPrompt options object + shared dispatchPrompt for submit/queue/edit.
  • Replaced inFlightMessage + onSessionIdle registry with a pendingSessionId signal draining off store.thinking.
  • Unified agent/model selection into createSessionPreference.
  • Session-switch bootstrap only refetches session-scoped data.

Fix: stuck busy / "steering" state

  • The busy indicator could stay stuck after a reply already arrived if the session.idle SSE event was missed (timing/reconnects). Added a self-healing fallback in the event reducer that clears thinking when a session's newest message is a completed assistant message (guarded so a queued/steered follow-up still shows busy).
  • Diagnosed and verified by fault-injecting a missed session.idle: reproduced the stuck state, confirmed the fallback clears it, and confirmed normal two-message flows still complete and return to idle.

Testing

  • pnpm exec vitest run → 313 tests passing; pnpm build clean; changed files typecheck clean.
  • Extensive manual E2E via CDP-driven reproduction (sequential, steering ×12, queued follow-ups, tool prompt) plus a final happy-path walkthrough — all return to idle with no stuck busy state.

thinking-stuck-fix-happy-path.mp4

OpenCode panel: multiple replies rendered and input returned to idle

Notes

  • Add the OPENCODE_AUTH_JSON secret (contents of ~/.local/share/opencode/auth.json) to persist credentials across runs.
  • A few pre-existing type issues remain in dev-only files (uikit.tsx, one SelectionAttachment narrowing) and are unrelated to these changes.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

…nstall.sh)

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
@claude

claude Bot commented Sep 11, 2026 •

Copy link
Copy Markdown

Claude finished @tanishqkancharla's task in 2m 4s —— View job


PR Review

📋 Detailed Review

Spec Analysis

This PR bundles five distinct improvements:

  1. Environment setup: Cursor dev environment with idempotent install script
  2. Remove repo-level opencode config: Delete .opencode/ and opencode.json (uses user's global config)
  3. SDK v2 migration: Adapt to session.messages() returning { info, parts }[] instead of Message[]
  4. Model picker: Dropdown grouped by provider, persisted per-session with global default
  5. Main-flow cleanups: Derive text from parts, unified dispatchPrompt, createSessionPreference utility, session-switch optimization
  6. Stuck busy fix: Self-healing fallback when session.idle SSE event is missed

Changed Files

Environment Setup

  • .cursor/environment.json: Cursor environment config pointing to install script
  • .cursor/install.sh: Idempotent setup (pnpm, build, OpenCode CLI, VS Code, credentials)
  • .gitignore: Added entry for local files

Removed Config (5000+ lines)

  • .opencode/*: Deleted all agent/skill/command/plugin definitions
  • opencode.json: Deleted repo-level config

SDK v2 Migration

  • package.json: Bumped @opencode-ai/sdk 1.2.14 → 1.18.30
  • src/OpenCodeService.ts:152-154: Updated return type to { info, parts }[]
  • src/OpenCodeViewProvider.ts:259-262: Adapted message mapping
  • src/shared/messages.ts:59-66: Updated FileDiffSchema for v2 shape

Model Picker

  • src/webview/components/ModelSwitcher.tsx: New component (113 lines)
  • src/webview/components/InputBar.tsx:18-21,185-192: Added model switcher integration
  • src/webview/App.css:1103-1219: Model switcher styles
  • src/OpenCodeViewProvider.ts:20,279-280,307-312: Model persistence

Main-Flow Cleanups

  • src/webview/App.tsx:51-71: New createSessionPreference utility
  • src/webview/App.tsx:560-612: New unified dispatchPrompt helper
  • src/webview/state/derive.ts: New shared derivation helpers (64 lines)
  • src/webview/state/bootstrap.ts:48-49,117-149: Session-scoped bootstrap optimization
  • src/webview/state/eventHandlers.ts:83-100: New ensureMessage helper
  • src/webview/components/MessageItem.tsx:27-28: Text derived from parts via extractTextFromParts
  • src/webview/components/MessageList.tsx:119,219: Use extractTextFromParts
  • src/webview/components/TiptapEditor.tsx:127,163: Fixed double newlines

Stuck Busy Fix

  • src/webview/state/eventHandlers.ts:166-172: Self-healing fallback

New PatchToolCall

  • src/webview/components/parts/PatchToolCall.tsx: Renders apply_patch tool (127 lines)
  • src/webview/components/parts/ToolCall.tsx:5,36-38: Routing for patch tools

Test/Mock Updates

  • src/webview/state/MockSyncProvider.tsx:12-22,45-52: Support mock messages with text or parts

Simplicity Assessment

  • createSessionPreference (App.tsx:51-71): Clean abstraction for the repeated agent/model pattern. The 20 lines eliminate duplication and make the pattern reusable.

  • dispatchPrompt (App.tsx:560-612): Consolidates error handling and state management from submit/queue/edit paths. Reduces three similar blocks to one.

  • Text derivation from parts: Removing Message.text and deriving at render time eliminates three hand-maintained resync blocks mentioned in the PR. The extractTextFromParts utility is 10 lines and used in 3 places.

  • Session bootstrap optimization (bootstrap.ts:117-149): The includeGlobal flag avoids redundant fetches on session switch. Clear and well-documented.

  • PatchToolCall (127 lines): Follows the same pattern as other tool components. Necessary for the new tool type.

Correctness Assessment

  • Self-healing busy fix (eventHandlers.ts:167-172): The guard latest[latest.length - 1].id === info.id correctly ensures we only clear thinking when the completed message is the newest, so queued/steered follow-ups still show busy. Well-designed fallback.

  • TiptapEditor fix (line 127): Setting blockSeparator: "\n" is the correct fix for Tiptap's default "\n\n". Applied consistently at both extraction points.

  • SDK v2 migration: The response shape change is handled correctly. Both OpenCodeService.ts and OpenCodeViewProvider.ts extract info.id and info.role from the new structure.

  • Model persistence: Uses the same pattern as agent persistence (globalState.update). The LAST_MODEL_KEY is properly defined and retrieved in init data.

Summary

Well-structured PR that delivers environment setup, SDK migration, model picker, and several quality-of-life cleanups. The main-flow refactors (createSessionPreference, dispatchPrompt, text derivation from parts) reduce duplication without over-abstracting. The stuck busy state fix is a robust fallback that handles SSE edge cases. The 5000+ line deletion is cleanup of unused repo-level config.

Required Actions

None.

Suggestions

None.

--- |

cursoragent and others added 4 commits September 11, 2026 06:31
Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
- Bump @opencode-ai/sdk 1.2.14 -> 1.18.30 to match the opencode server/CLI
- Adapt session.messages() to the v2 { info, parts }[] response shape
- Align FileDiffSchema with the v2 snapshot diff shape (patch/status)

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
- New ModelSwitcher dropdown lists configured providers/models via config.providers()
- Selected model is sent with each prompt (session.prompt model param)
- Selection persists per-session with a global default, stored via OPENCODE lastUsedModel
- Falls back to the first available model when no selection is persisted

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
@cursor cursor Bot changed the title chore: add Cursor dev environment setup Dev environment setup, opencode SDK v2 migration, and model picker Sep 11, 2026
cursoragent and others added 6 commits September 11, 2026 20:26
… is at top

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
- Message no longer stores a text field; text is derived from store.part at
  render time via extractTextFromParts
- eventHandlers: replace 3 copies of 'ensure message + resync text' with a
  single ensureMessage() helper; drop resolveMessageText
- MessageItem/MessageList derive text from parts; bootstrap stops precomputing
- MockSyncProvider synthesizes a text part from authoring-only mock text

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
… helpers

Removes 3 copies of the summary/diff aggregation and 2 copies of the
token->context-usage math (plus the duplicated 200000 magic number).

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
- Add dispatchPrompt() shared by submit / queue-drain / edit (removes 3 copies
  of setThinking + error parsing + logging)
- sendPrompt now takes an options object instead of 6 positional args
- Replace inFlightMessage object + onSessionIdle callback registry with a single
  pendingSessionId signal that drains the queue via store.thinking reactivity
- Remove sessionIdleCallbacks from the sync store and event reducer

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
…rence

Both pickers now share one default + per-session-override helper instead of two
mirrored signal sets and accessor pairs.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
Full bootstrap (agents + session list + status) runs on connect/reconnect or
when globals are unloaded; session switches now only refetch messages, session
detail, and permissions instead of re-pulling near-static workspace data.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
@cursor cursor Bot changed the title Dev environment setup, opencode SDK v2 migration, and model picker Dev env setup, opencode SDK v2 migration, model picker, and main-flow cleanups Sep 11, 2026
@tanishqkancharla
tanishqkancharla marked this pull request as ready for review September 11, 2026 21:46
cursoragent and others added 4 commits September 11, 2026 23:58
The busy/'steering' state could get stuck after a reply had already arrived
if the session.idle SSE event was missed (timing/reconnects). Add a
self-healing fallback that clears thinking when the session's newest message
is a completed assistant message, so the input returns to idle even without
idle. Guarded to the latest message so a queued/steered follow-up still
shows busy. Verified by fault-injecting a missed session.idle.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
Tiptap getText() defaults to a '\n\n' block separator, so each Enter (new
paragraph) produced a doubled newline in the submitted text. Use a single
'\n' block separator.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
Edit/write and bash tool calls forced defaultOpen=true while every other tool
defaulted closed, making the open/closed state look arbitrary. Drop the
override so all tool results start collapsed and only open when toggled.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>
apply_patch fell through to GenericToolCall, which showed the raw tool result
('Success. Updated the following files...') in the header. Add a dedicated
PatchToolCall that reads state.metadata.files and shows 'Patched <path>' for a
single file or 'Patched N files' for multiple, with diff stats, and the diff
collapsed by default.

Co-authored-by: Tanishq Kancharla <tanishqkancharla@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 69d23de. Configure here.

if (info.role === "assistant" && info.time?.completed) {
const latest = store.message[sessionId];
if (latest && latest.length > 0 && latest[latest.length - 1].id === info.id) {
setStore("thinking", sessionId, false);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Self-heal clears busy during steering

Medium Severity

The new thinking fallback treats a completed latest assistant as session-idle, but a steered follow-up is sent over HTTP before that user message exists in the store. If the prior assistant completes in that window, thinking flips false and the pendingSessionId effect can drain the queue, hiding the stop/busy state while work is still in flight.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 69d23de. Configure here.

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