Codebase-wide bug fixes and cleanup (audit) + Gemini support for pi - #55
Conversation
General fixes surfaced by a full-codebase audit, on files untouched by
the hosted-deployment overlay. No hosted/multi-tenant coupling.
Editor (correctness):
- Fix ySyncPluginKey imported from the wrong package. TiptapEditor and
comment-overlay imported it from y-prosemirror while the Collaboration
extension installs the sync plugin from @tiptap/y-tiptap, so the keys
never matched: agent/remote Yjs transactions were misclassified as user
edits for the idle timer, and the RelativePosition comment-anchoring
tier was dead (comments fell back to first-match indexOf). editor-
extensions now re-exports the key/rel-pos helpers as the single source.
- Fix TiptapEditor store-subscription leak: 11 manual .subscribe() calls
were never torn down and leaked on every tab-switch remount. Converted
to $store auto-subscription; collapsed 5 duplicated mirror-var pairs.
- Delete the unreachable non-round diff mode in diff-overlay (dead since
per-round review; removes a whole-doc word-diff computed every review
change and never used) and the .diff-removed-widget CSS.
- Delete unescapeMarkdown: a tiptap-markdown-era vestige that corrupted
user-typed backslashes (\[, \{) in LaTeX on copy and diff line-matching.
- Remove dead symbols (hasPendingProposal, computeDetachedThreads,
markdownToPlainText, unused imports/exports).
Providers (permission bug):
- openai/cursor/pi silently ignored allowedTools and canUseTool, so plan
mode's mutation-tool removal and the scratch-only Edit/Write guard were
enforced only on claude/codex (pi even exposed tools during warmup). Add
a shared wrapToolsForProvider that filters by allowedTools and gates
each execute through canUseTool; adopt in all non-Claude adapters.
- Dedupe the proposal-event mapping, lazy SDK loader, prompt concat, and
stale-session event into providers/shared.ts.
Types/dedup:
- Delete dead InlineFeedback; wire classifyRoundKind to TINY_EDIT_THRESHOLD
instead of a hardcoded literal; import AgentSettings/Rule/default from
$lib/types instead of re-declaring them in runtime-state.
UI:
- TabBar tablist a11y (tabindex) + drop a no-op drag-over rule.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
The right-hand review sidebar was already gone from the layout (the sole
OutlinePane instance is the left TOC, showReview={false}), leaving ~575
lines of unreachable review markup/script/CSS plus a 15s polling interval
and 6 review-store subscriptions the live TOC paid for on every change.
- Reduce OutlinePane to the TOC (heading observer + scroll-to-heading).
- Drop the dead cross-tab producer in +page.svelte's syncAllTabsState and
the allTabPendingRounds store it fed.
- Delete other now-unused UI stores (showHistory, selectedAction) and the
unused togglePinnedDiffRound helper.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
The OutlinePane review-sidebar mode this PR removes was still documented as a live right-hand pane. Describe the actual layout: single TOC-only OutlinePane, floating AgentDockShell/HistoryPane, in-editor CommentGutter for pending edits, and toast-surfaced rule/hook proposals. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
…pe claude.ts proposal events
Documentation (every claim verified against the current tree):
- Persistence: drop the state.json "JSON mirror" (removed long ago; only
stale comments referenced it); document hooks.json; SQLite is the single
source of truth for runtime state.
- Undo: there is no server-side UndoManager. Client undo is the custom
Y.UndoManager in editor-extensions.ts (fragment + review array +
comments map, trackedOrigins {ySyncPluginKey, USER_ORIGIN}); replace the
stale yjs-agent.ts / AGENT_APPLY_KEY / ydoc-registry.ts descriptions
(none of those exist).
- Layout: ARCHITECTURE.md's three-pane diagram, OutlinePane/HistoryPane
component table, and store list updated to the dock-based layout and the
stores that actually exist (userMd/annotations/activeTabKind are gone).
- Editor update loop: agent-vs-user classification is
getMeta(ySyncPluginKey), with the @tiptap/y-tiptap package caveat.
- Gotchas: replace tiptap-markdown escaping / StarterKit undoRedo / Link
(all removed machinery) with the real ones — ySyncPluginKey package
matching, plain-text serialization, shared origin constants.
- Conventions: multi-provider model selection; $store auto-subscription
allowed (capture unsubscribers if subscribing manually); 500ms flush tick.
Code:
- claude.ts adopts emitProposalEvents from providers/shared.ts, deleting
the last hand-rolled copy of the rule/hook proposal mapping (the other
four adapters already use it).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
pi-ai already speaks to Google's Generative Language API natively (via GEMINI_API_KEY); this wires DocWriter's plumbing around it: - Model catalog: add Gemini 3.1 Pro (google/gemini-3.1-pro-preview, the ids pi-ai 0.80.x's catalog actually resolves) and Gemini 3.5 Flash to the pi fallback lists (server + client mirror), alongside the existing 2.5 entries. - API keys: the panel gains a settable Gemini row (GEMINI_API_KEY), and the pi provider now counts as usable when a Gemini key is present even without a Together key (new altEnvVars mechanism on ProviderKeySpec). - e2e harness: pi credentials accept GEMINI_API_KEY, and when it is the only key available the pi smoke defaults to google/gemini-3.5-flash; the workflow passes a GEMINI_API_KEY secret through. - Bug fix surfaced by testing: a pi model-call failure (e.g. invalid API key) ends the assistant message with stopReason 'error' but still RESOLVES session.prompt(), so renders completed as a silent "made no edits" no-op. pi.ts now captures message_end stopReason errors and throws, so the render endpoint emits a visible error event. Verified live: /api/keys reports pi usable from GEMINI_API_KEY alone, the smoke harness selects the Gemini default, pi-ai resolves both new model ids, and the request reaches Google (which rejected the test key as invalid — the error now renders in the history pane instead of a silent timeout). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Replace the buried Settings > Provider and Settings > Model submenus with two Cursor-style pill buttons in the header, right after the Settings menu: a provider pill and a model pill. Each shows the live selection, opens its own dropdown on click, and closes on outside-click / Escape. The model dropdown gains a type-to-filter box (the pi catalog alone has ~14 models) plus the existing "Custom model..." entry. Provider and Model are removed from the Settings dropdown so the pills are the single control. Front-end only: reuses the selectedProvider / selectedModel stores and setSelectedProvider / setSelectedModel actions; no backend change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
- comment-overlay: write the rel-position backfill with SYSTEM_ORIGIN
instead of USER_ORIGIN. The backfill is machine-generated, but
USER_ORIGIN is in the UndoManager's trackedOrigins, so the first
backfill after loading a legacy/agent-created thread planted a phantom
undo step the user could Cmd+Z into. This path was dead before the
ySyncPluginKey unification reactivated it.
- stores: pick a valid model for the new provider synchronously in
setSelectedProvider, before the async /api/models load. The header
model pill always renders the current model label, so on a provider
switch it briefly showed the previous provider's raw model id until the
fetch resolved. Verified across all five providers: no raw-id flash.
- +page: pass models={providerModels} to ModelPicker instead of the
providerModels.length > 0 ? providerModels : modelOptions fallback.
The fallback was dead (providerModels is never empty) and modelOptions
carries duplicate ids across providers, which would throw a duplicate
key error in the keyed {#each}. The empty-list state already handles
an empty list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
The settings panel (and the client autonomy-change copy) already promise that the agent creates its own comment threads at Medium/High autonomy, but the backend never delivered it: the render prompt told the agent it "cannot open new threads", comment_doc was in no allowlist, and Claude's MCP doc-tools server didn't register it at all. The UI was writing a check the backend couldn't cash. This brings the backend in line with the UI (matching the model on the codex/landing-hosted-split branch, minus its hosted-only request-context plumbing): - mcp-doc-tools.ts: register a commentDocTool on the docwriter-doc MCP server so Claude gains parity with the other providers, backed by a shared createAgentCommentThread helper that writes under AGENT_ORIGIN. Export COMMENT_DOC_TOOL_NAME; point reply_to_comment at comment_doc for new threads. - render/+server.ts: allowlist comment_doc in fullAllowedTools (not plan mode, which stays read-only), and rewrite the autonomy guidance as a three-level model — conservative/Low (no proactive comments), balanced/Medium (may open comment threads, no unsolicited edits), aggressive/High (comments and reviewable edits). Comment routing is threaded through the "when in doubt", assistant-text, reply-vs-edit, mode-override, and read-vs-write sections. comment_doc was already handled for the non-Claude providers via buildToolDefinitions; it just needed allowlisting. No client changes — the panel, hover preview, and change-copy were already built for this. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
- review-diff: delete buildReviewDiffPreview, its ReviewPreviewLine type, the splitLines helper, and the now-orphaned DiffPart / wordDiff imports. The function lost its only caller when diff-overlay dropped its import. - diff-overlay: drop the vestigial DiffState.isPlainText field. The only setDiffState caller always passed true, and INITIAL_STATE's false is unreachable past the `baseline === null` short-circuit, so the `&& isPlainText` guard was always true. Removes a field that implied a markdown-stripping mode that no longer exists. - providers/openai, providers/pi: remove the redundant module-level sdkLoaded boolean. makeLazySdkLoader already memoizes the dynamic import, so re-destructuring the SDK globals on a warm cache is idempotent; the extra flag was a second memo layer. - Fix two stale comments (+page.svelte "showReview sidebar", comment-overlay "tabsBackfilled set") to match the current code. check and build both clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qn9Y2sWV7xtkWDbvbmsmVf
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
Hosted-independent fixes surfaced by a full-codebase audit, plus Gemini model support for the pi provider. These touch files untouched by the hosted-deployment overlay, so they belong on
mainand flow tolandingvia the sync workflow. The hosted-coupled subset (authFetch adoption, multi-tenant correctness) stays on PR #51.npm run checkandnpm run buildboth pass;checkis clean (0 errors, 0 warnings).Editor (correctness bugs)
ySyncPluginKeyimported from the wrong package.TiptapEditorandcomment-overlayimported it fromy-prosemirror, but the Collaboration extension installs its sync plugin from@tiptap/y-tiptap, so the twoPluginKey('y-sync')instances never matched. Consequences: agent/remote Yjs transactions were misclassified as user edits for the idle timer, and the entire RelativePosition comment-anchoring tier was dead at runtime (comments silently fell back to first-matchindexOf).editor-extensionsnow re-exports the key/rel-pos helpers as the single source.TiptapEditor. 11 manual.subscribe()calls were never torn down and leaked on every tab-switch remount, firing overlay updates against destroyed editors. Converted to$storeauto-subscription; collapsed 5 duplicated mirror-var pairs..diff-removed-widgetCSS.unescapeMarkdownremoved. A tiptap-markdown-era vestige that corrupted user-typed backslashes (\[,\{) in LaTeX on copy and in diff line-matching.hasPendingProposal,computeDetachedThreads,markdownToPlainText, unused imports/exports).Providers (permission bug)
allowedTools/canUseToolwere silently ignored by openai/cursor/pi, so plan-mode's mutation-tool removal and the scratch-only Edit/Write guard were enforced only on claude/codex (pi even exposed tools during warmup). A sharedwrapToolsForProvidernow filters byallowedToolsand gates each tool throughcanUseToolin all non-Claude adapters.providers/shared.ts(all five adapters, including claude).Gemini support for pi (new)
Gemini 3.1 Pro(google/gemini-3.1-pro-preview— the id pi-ai 0.80.x's catalog resolves) andGemini 3.5 Flashadded to the pi model lists (server + client mirror).GEMINI_API_KEY), and pi counts as usable with a Gemini key even without a Together key (newaltEnvVarsonProviderKeySpec).GEMINI_API_KEYas credentials and defaults togoogle/gemini-3.5-flashwhen it's the only key; the workflow passes aGEMINI_API_KEYsecret through (stopReason: 'error'but still resolvessession.prompt(), so renders completed as a silent "made no edits" no-op. pi.ts now surfaces it as a visible render error. Verified live end-to-end up to Google's API (which rejected the test key — the error now renders in the history pane).UI
allTabPendingRounds/showHistory/selectedActionstores and the unusedtogglePinnedDiffRoundhelper.Types
InlineFeedback; wiredclassifyRoundKindtoTINY_EDIT_THRESHOLDinstead of a hardcoded literal; importAgentSettings/Rule/default from$lib/typesinstead of re-declaring them.Docs
state.jsondual-write,yjs-agent.ts/AGENT_APPLY_KEY/ydoc-registry.tsreferences, server-side UndoManager, and stale gotchas; documented the real layout (dock + CommentGutter), undo model, and theySyncPluginKeypackage-matching gotcha.checkandbuildcan't catch an anchoring or timing regression. The two behavior-changing editor fixes — theySyncPluginKeyunification (reactivates the RelativePosition anchoring tier) and the subscription-leak refactor (moves overlay triggers to$effect) — should be exercised in the running app (comments landing on the right passage, agent edits, diff overlay) or via the e2e smoke before merging. A green pi-with-Gemini run also still needs a validGEMINI_API_KEY(the test key was rejected by Google as invalid).🤖 Generated with Claude Code