Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff by jon-bell · Pull Request #128 · ndif-team/workbench · GitHub
Skip to content

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff - #128

Draft
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643
Draft

feat(chat): cross-workbench Chat tool with Logit Lens / Patch Lens handoff#128
jon-bell wants to merge 2 commits into
mainfrom
cursor-workbench-chat-tool-2643

Conversation

@jon-bell

Copy link
Copy Markdown
Contributor

Summary

Adds a Chat tool available across the whole workbench — a lightweight completion playground wired to the same model the rest of the workspace uses, with easy two-way handoff to the interpretability tools.

It fulfils the request:

  • Chatbot interface to the same model, reachable from any tool page.
  • Easy access from Logit Lens / Patch Lens via a "Spin out" action.
  • Spin a prompt out to see up to N tokens — pushes the current tool prompt into the chat and generates a continuation.
  • Capture a prompt back into Logit Lens (or Patch Lens) — a completed chat result can be sent straight into a tool's prompt editor.
  • Unobtrusive when not in use — collapsed to a slim right-edge strip by default on desktop; a bottom-right FAB + drawer on mobile.
  • E2E test coverage.

UX

  • Desktop: a collapsible right rail that shares horizontal space with the active tool. Collapsed → a thin Chat strip with an open button; expanded → header, transcript, and composer. Mounted at the workspace layout level so it appears on every tool page.
  • Mobile: a bottom-right MessageSquare FAB (mirrors the bottom-left charts drawer) opening a right-side slide-over with the same panel.
  • Composer: multi-line prompt, a max-new-tokens field (1–64), and ⌘/Ctrl+Enter to send (plain Enter is a newline), matching the app's other prompt inputs.
  • Each turn shows the prompt + generated continuation, with inline pending/error states and Logit Lens / Patch Lens capture actions.

Handoff model

  • Spin out (SpinOutButton in Logit Lens and Patch Lens): opens the chat, seeds the composer with the tool's prompt, and auto-generates a continuation.
  • Capture back: a completed message can be sent to a tool. If the current route is a compatible tool it injects into that chart's prompt editor; otherwise it creates a new chart and navigates to it.
  • Seed (tool → chat) and injection (chat → tool) are ephemeral channels in the store; the consuming components guard on a nonce so each handoff runs exactly once.

Implementation

  • Reuses the existing /models/start-generate NDIF flow — no backend changes. modelsApi.ts now exports a bare generateCompletion (no toast) for the rail's inline error UI, keeping useGenerate intact.
  • New useChat zustand store (persisted to localStorage, keyed per-workspace) holds rail open-state, composer drafts, and conversation history. Interrupted pending turns are surfaced as errors on rehydrate. This is the interim history home documented in CLAUDE.md (a DB-backed table is the eventual target).
  • New feature-local components under app/workbench/[workspaceId]/components/chat/: WorkbenchChat (layout switch), ChatRail, MobileChatDrawer, ChatPanel, ChatComposer, ChatMessageItem, SpinOutButton, and the useChatController hook (generation + handoff logic shared by desktop/mobile).
  • Minimal edits to layout.tsx, Lens2Controls.tsx, and the Patch Lens page/area to mount the rail and wire spin-out + injection.

Tests

  • tests/chat-handoff.spec.ts (no NDIF): rail is collapsed by default and toggles open; a seeded completed message is captured into the Logit Lens prompt editor (seeded lens2 chart via tests/seed-chat.cjs + seeded chat localStorage, so no model run is needed).
  • tests/chat.spec.ts (real NDIF): compose a prompt and generate a continuation; spin the lens prompt out to chat, generate, and capture the result back.

Verification

bunx tsc --noEmit (no new errors), eslint, knip, prettier --check, and next build all pass for the changed files.

Open in WebOpen in Cursor

cursoragentand others added 2 commits July 2, 2026 22:25
Add a collapsible Chat rail available on every workspace tool page. It talks
to the same model as the rest of the workspace via the existing generate
endpoint, and integrates with the interpretability tools:
- Spin out: push a Logit Lens / Patch Lens prompt into the chat and generate
up to N tokens.
- Capture back: send a completed chat result into the Logit Lens or Patch
Lens prompt editor.
The rail is collapsed to a slim strip by default (bottom-right FAB + drawer on
mobile) so it stays unobtrusive when idle. History is kept per-workspace in a
persisted zustand store; spin-out/injection are ephemeral handoff channels.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
- chat-handoff.spec.ts (no NDIF): rail collapsed-by-default + toggle, and
send-to-Logit-Lens handoff via a seeded chart + seeded chat message.
- chat.spec.ts (real NDIF): compose+generate a continuation, and spin the lens
prompt out to chat then capture it back.
- seed-chat.cjs seeds a deterministic lens2 chart for the handoff spec.
Co-authored-by: Jonathan Bell <jon@jonbell.net>
@vercel

vercelBot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
workbenchReadyReadyPreview, CommentJul 2, 2026 10:26pm

Request Review

@coderabbitai

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7806dcc6-379a-4916-b7e3-7b3e532b0fba

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor-workbench-chat-tool-2643

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Preview deployed

@argos-ci

argos-ciBot commented Jul 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Argos notifications ↗︎

BuildStatusDetailsUpdated (UTC)
default (Inspect)⚠️ Changes detected (Review)2 changed, 4 removed, 7 failuresJul 2, 2026, 10:43 PM

Sign up for freeto 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

@jon-bell@cursoragent