Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions web/.env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
VITE_POSTHOG_KEY=phc_your_project_key
VITE_POSTHOG_HOST=https://us.i.posthog.com
VITE_TAMBO_API_KEY=your_tambo_api_key
4 changes: 4 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@
"@codemirror/lang-xml": "^6.1.0",
"@codemirror/lang-yaml": "^6.1.2",
"@codemirror/view": "^6.39.15",
"@tambo-ai/client": "^0.0.1",
"@tambo-ai/react": "^1.1.0",
"@tambo-ai/typescript-sdk": "^0.93.1",
"@tanstack/react-query": "^5.90.21",
Comment on lines +56 to +59
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if Tambo packages exist on npm and their versions

echo "=== Checking `@tambo-ai/client` ==="
curl -s https://registry.npmjs.org/@tambo-ai/client 2>/dev/null | jq -r '.name, .["dist-tags"], (.versions | keys | reverse | .[0:5])' 2>/dev/null || echo "Not found or private"

echo -e "\n=== Checking `@tambo-ai/react` ==="
curl -s https://registry.npmjs.org/@tambo-ai/react 2>/dev/null | jq -r '.name, .["dist-tags"], (.versions | keys | reverse | .[0:5])' 2>/dev/null || echo "Not found or private"

echo -e "\n=== Checking `@tambo-ai/typescript-sdk` ==="
curl -s https://registry.npmjs.org/@tambo-ai/typescript-sdk 2>/dev/null | jq -r '.name, .["dist-tags"], (.versions | keys | reverse | .[0:5])' 2>/dev/null || echo "Not found or private"

echo -e "\n=== Checking `@tanstack/react-query` ==="
curl -s https://registry.npmjs.org/@tanstack/react-query 2>/dev/null | jq -r '.name, .["dist-tags"].latest' 2>/dev/null || echo "Not found"

Repository: CodeCraftersLLC/companion

Length of output: 561


Verify stability of @tambo-ai/client before production use.

The @tambo-ai/client package is at version 0.0.1—the only available release on npm. This extremely early version indicates the package is in initial development stages and may be unstable. Additionally, the @tambo-ai/typescript-sdk is at version 0.93.1, which, while more mature, is still pre-1.0 and subject to breaking changes.

Consider:

  1. Verifying with the Tambo AI team that these versions are production-ready.
  2. Pinning to exact versions for all Tambo packages to prevent unexpected breaking changes during dependency updates.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/package.json` around lines 56 - 59, The package.json lists pre-1.0 Tambo
packages (notably "@tambo-ai/client" at ^0.0.1 and "@tambo-ai/typescript-sdk" at
^0.93.1) which may be unstable; confirm with the Tambo AI team that these exact
releases are production-ready, then pin those dependencies to exact versions
(remove ^ and use exact version strings) for "@tambo-ai/client",
"@tambo-ai/react" and "@tambo-ai/typescript-sdk" to avoid automatic minor/patch
upgrades and reduce risk of unexpected breaking changes during installs.

"@uiw/react-codemirror": "^4.25.4",
"croner": "^10.0.1",
"diff": "^8.0.3",
Expand Down
4 changes: 4 additions & 0 deletions web/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ vi.mock("./components/TerminalPage.js", () => ({
TerminalPage: () => <div data-testid="terminal-page">TerminalPage</div>,
}));

vi.mock("./components/RightPanel.js", () => ({
RegentSidebar: () => null,
}));

vi.mock("./components/ProcessPanel.js", () => ({
ProcessPanel: () => <div data-testid="process-panel">ProcessPanel</div>,
}));
Expand Down
22 changes: 5 additions & 17 deletions web/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { TopBar } from "./components/TopBar.js";
import { HomePage } from "./components/HomePage.js";
import { TaskPanel } from "./components/TaskPanel.js";
import { DiffPanel } from "./components/DiffPanel.js";
import { RegentSidebar } from "./components/RightPanel.js";
import { UpdateBanner } from "./components/UpdateBanner.js";
Comment on lines +14 to 15
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

RegentSidebar (and its Tambo dependencies) is imported and rendered unconditionally at the app root. This means everyone pays the bundle/runtime cost for @tambo-ai/* even when Regent is disabled (no API key). Consider code-splitting RegentSidebar behind a lightweight API-key check (or behind the toggle) using React.lazy/dynamic import so the optional feature doesn’t impact initial load for all users.

Copilot uses AI. Check for mistakes.
import { SessionLaunchOverlay } from "./components/SessionLaunchOverlay.js";
import { SessionTerminalDock } from "./components/SessionTerminalDock.js";
Expand All @@ -30,7 +31,6 @@ const AgentsPage = lazy(() => import("./components/AgentsPage.js").then((m) => (
const TerminalPage = lazy(() => import("./components/TerminalPage.js").then((m) => ({ default: m.TerminalPage })));
const ProcessPanel = lazy(() => import("./components/ProcessPanel.js").then((m) => ({ default: m.ProcessPanel })));


function LazyFallback() {
return (
<div className="flex items-center justify-center h-full">
Expand Down Expand Up @@ -284,24 +284,9 @@ export default function App() {
</div>
</div>

{/* Task panel — overlay on mobile, inline on desktop */}
{/* Task panel — overlay on mobile, inline on desktop (session view only) */}
{currentSessionId && isSessionView && (
<>
{!taskPanelOpen && (
<button
type="button"
onClick={() => useStore.getState().setTaskPanelOpen(true)}
className="hidden lg:flex fixed right-0 top-1/2 -translate-y-1/2 z-30 items-center gap-1 rounded-l-lg border border-r-0 border-cc-border bg-cc-card/95 backdrop-blur px-2 py-2 text-[11px] text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer"
title="Open context panel"
>
<svg viewBox="0 0 16 16" fill="currentColor" className="w-3 h-3">
<path d="M3 2.5A1.5 1.5 0 014.5 1h7A1.5 1.5 0 0113 2.5v11a1.5 1.5 0 01-1.5 1.5h-7A1.5 1.5 0 013 13.5v-11zm2 .5v10h6V3H5z" />
</svg>
<span className="[writing-mode:vertical-rl] rotate-180 tracking-wide">Context</span>
</button>
)}

{/* Mobile overlay backdrop */}
{taskPanelOpen && (
<div
className="fixed inset-0 bg-black/30 z-30 lg:hidden"
Expand All @@ -321,6 +306,9 @@ export default function App() {
</div>
</>
)}

{/* Regent sidebar — persistent, always available */}
<RegentSidebar />
<UpdateOverlay active={updateOverlayActive} />
</div>
);
Expand Down
159 changes: 159 additions & 0 deletions web/src/components/RegentPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import { useRef, useEffect, type ReactNode } from "react";
import {
TamboProvider,
useTambo,
useTamboThreadInput,
ComponentRenderer,
} from "@tambo-ai/react";
import { sessionTools } from "../regent/tools/session-tools.js";
import { sessionCardTamboComponent } from "../regent/components/SessionCard.js";
import { taskOverviewTamboComponent } from "../regent/components/TaskOverview.js";

const TAMBO_API_KEY = (import.meta as unknown as Record<string, Record<string, string>>).env?.VITE_TAMBO_API_KEY;

export function hasTamboApiKey(): boolean {
return !!TAMBO_API_KEY;
}
Comment on lines +1 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. regentpanel missing .test.tsx 📘 Rule violation ⛯ Reliability

web/src/components/RegentPanel.tsx is a new component but there is no corresponding
RegentPanel.test.tsx providing render, axe, and interaction coverage. This violates the baseline
frontend component test requirement and risks regressions in the Regent chat/provider UI.
Agent Prompt
## Issue description
`web/src/components/RegentPanel.tsx` is a new frontend component but it does not have a corresponding `RegentPanel.test.tsx` that includes (1) a render test, (2) an axe accessibility scan assertion using `toHaveNoViolations()`, and (3) interaction/state behavior tests.

## Issue Context
Compliance requires baseline test coverage for each new/modified component under `web/src/components/`. This PR introduces the Regent chat/provider UI, including API-key gating and a composer.

## Fix Focus Areas
- web/src/components/RegentPanel.tsx[1-159]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +12 to +16
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

3. Tambo key exposed client 🐞 Bug ⛨ Security

Regent reads VITE_TAMBO_API_KEY from the Vite client environment and passes it to TamboProvider,
which means the key is shipped to every browser. If the key is privileged, it can be extracted and
abused (billing/tenant access).
Agent Prompt
### Issue description
`VITE_TAMBO_API_KEY` is consumed in the browser and passed to `TamboProvider`, which ships the key to all clients. If the key is privileged, it can be extracted from the JS bundle/devtools and reused.

### Issue Context
This app already has an authenticated backend API layer (`/api` with Bearer auth). Prefer using that backend to hold third-party secrets and/or mint scoped, short-lived tokens.

### Fix Focus Areas
- web/src/components/RegentPanel.tsx[12-16]
- web/src/components/RegentPanel.tsx[139-145]
- web/.env.example[1-3]
- web/src/api.ts[5-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +12 to +16
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

TAMBO_API_KEY is read via a broad cast on import.meta and cached at module load. This bypasses the repo’s ImportMetaEnv typing and makes tests harder to control with vi.stubEnv(). Prefer using import.meta.env.VITE_TAMBO_API_KEY directly, and add VITE_TAMBO_API_KEY to src/vite-env.d.ts (like analytics.ts does for PostHog).

Copilot uses AI. Check for mistakes.

export function RegentChat() {
const { messages, isStreaming, currentThreadId } = useTambo();
const { value, setValue, submit, isPending } = useTamboThreadInput();
const feedRef = useRef<HTMLDivElement>(null);

useEffect(() => {
if (feedRef.current) {
feedRef.current.scrollTop = feedRef.current.scrollHeight;
}
}, [messages, isStreaming]);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!value.trim() || isPending) return;
await submit();
};

return (
<div className="flex flex-col h-full">
{/* Message feed */}
<div ref={feedRef} className="flex-1 overflow-y-auto px-3 py-2 space-y-3">
{messages.length === 0 && (
<div className="flex flex-col items-center justify-center h-full text-center">
<div className="text-sm text-cc-muted mb-2">
Ask your Regent about your agent sessions
</div>
<div className="text-[11px] text-cc-muted/60 space-y-1">
<div>"What are all my sessions doing?"</div>
<div>"Which sessions need permission approvals?"</div>
<div>"Summarize the progress across all agents"</div>
</div>
</div>
)}

{messages.map((msg) => (
<div
key={msg.id}
className={`text-sm ${
msg.role === "user"
? "text-cc-fg bg-cc-hover rounded-lg px-3 py-2 ml-8"
: "text-cc-fg"
}`}
>
{msg.content.map((content, i) => {
if (content.type === "text") {
return (
<div
key={`${msg.id}-${i}`}
className="whitespace-pre-wrap text-[13px] leading-relaxed"
>
{content.text}
</div>
);
}
if (content.type === "component") {
return (
<div key={`${msg.id}-${i}`} className="my-2">
<ComponentRenderer
content={content}
threadId={currentThreadId ?? ""}
messageId={msg.id}
/>
Comment on lines +75 to +79
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

@tambo-ai/react ComponentRenderer threadId prop documentation

💡 Result:

In @tambo-ai/react (React SDK 1.0+), ComponentRenderer expects a threadId prop that is the ID of the thread that the message (and its component content block) belongs to—you pass it alongside the content block and the messageId when rendering a "component" content block. [1]

You can typically source that ID from useTambo()’s currentThreadId (or from your thread object, e.g. thread.id). [2]

Example (from the migration docs):

<ComponentRenderer
  content={block}
  threadId={threadId}
  messageId={message.id}
  fallback={<div>Unknown component: {block.name}</div>}
/>
``` [1]

**Sources:** [1] [2]

---

🏁 Script executed:

```shell
# Search for ComponentRenderer imports and usage in the codebase
rg "ComponentRenderer" --type tsx --type ts -B 2 -A 5

Repository: CodeCraftersLLC/companion

Length of output: 96


🏁 Script executed:

# Find the file and examine the context around the flagged lines
cat -n web/src/components/RegentPanel.tsx | sed -n '60,90p'

Repository: CodeCraftersLLC/companion

Length of output: 1336


🏁 Script executed:

# Check for type definitions or documentation for ComponentRenderer
fd -e d.ts -e ts | xargs rg "ComponentRenderer" -l

Repository: CodeCraftersLLC/companion

Length of output: 53


Remove empty string fallback for threadId—ComponentRenderer requires a valid thread ID.

The @tambo-ai/react ComponentRenderer expects threadId to be "the ID of the thread that the message belongs to." Passing an empty string when currentThreadId is null does not meet this requirement and may cause runtime errors or unexpected behavior. Either ensure currentThreadId is always available at this point, or skip rendering the component entirely when the thread ID is unavailable.

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

In `@web/src/components/RegentPanel.tsx` around lines 75 - 79, The
ComponentRenderer is being passed an empty-string fallback for threadId which
violates its requirement for a valid thread ID; in RegentPanel replace the
current rendering logic so that ComponentRenderer (used in the JSX block with
props content, threadId, messageId) is only rendered when currentThreadId is
non-null/defined (i.e., guard the render with a check for currentThreadId) or
ensure currentThreadId is resolved earlier; update the JSX around
ComponentRenderer to skip rendering when currentThreadId is falsy rather than
passing "".

</div>
);
}
return null;
})}
</div>
))}

{isStreaming && (
<div className="flex items-center gap-1.5 text-[11px] text-cc-muted">
<span className="w-1.5 h-1.5 rounded-full bg-cc-accent animate-pulse" />
Thinking...
</div>
)}
</div>

{/* Composer */}
<form onSubmit={handleSubmit} className="shrink-0 border-t border-cc-border p-2">
<div className="flex items-center gap-2">
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
placeholder="Ask the Regent..."
className="flex-1 bg-cc-bg border border-cc-border rounded-lg px-3 py-2 text-sm text-cc-fg placeholder:text-cc-muted/50 focus:outline-none focus:border-cc-accent"
disabled={isPending}
/>
<button
type="submit"
disabled={isPending || !value.trim()}
className="shrink-0 px-3 py-2 rounded-lg bg-cc-accent text-white text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed hover:opacity-90 transition-opacity cursor-pointer"
>
Send
</button>
</div>
</form>
</div>
);
}

interface RegentProviderProps {
children: ReactNode;
}

export function RegentProvider({ children }: RegentProviderProps) {
if (!TAMBO_API_KEY) {
return (
<div className="flex flex-col items-center justify-center h-full p-6 text-center">
<svg viewBox="0 0 16 16" fill="currentColor" className="w-8 h-8 text-cc-muted mb-3">
<path d="M8 1.5a6.5 6.5 0 100 13 6.5 6.5 0 000-13zM7.25 5a.75.75 0 011.5 0v3a.75.75 0 01-1.5 0V5zm.75 6.5a1 1 0 110-2 1 1 0 010 2z" />
</svg>
<div className="text-sm text-cc-muted mb-1">Regent requires a Tambo API key</div>
<div className="text-[11px] text-cc-muted/60">
Set <code className="px-1 py-0.5 rounded bg-cc-hover text-cc-fg">VITE_TAMBO_API_KEY</code> in your environment to enable Regent.
</div>
</div>
);
}

return (
<TamboProvider
apiKey={TAMBO_API_KEY}
userKey="companion-user"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The userKey for TamboProvider is hardcoded to "companion-user". If this application is used by multiple users, they will all share the same Tambo user profile, including chat threads and history. This is a potential data privacy and security issue.

The userKey should be a unique identifier for each authenticated user. You could use a user ID from your authentication system, for example.

<TamboProvider
  apiKey={TAMBO_API_KEY}
  userKey={getUniqueUserId()} // e.g., from auth context or store
  tools={sessionTools}
  components={[sessionCardTamboComponent, taskOverviewTamboComponent]}
>

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot Mar 3, 2026

Choose a reason for hiding this comment

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

P1: Hardcoded userKey makes all users share the same Tambo identity, which can leak/mix thread data across users.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web/src/components/RegentPanel.tsx, line 142:

<comment>Hardcoded `userKey` makes all users share the same Tambo identity, which can leak/mix thread data across users.</comment>

<file context>
@@ -0,0 +1,159 @@
+  return (
+    <TamboProvider
+      apiKey={TAMBO_API_KEY}
+      userKey="companion-user"
+      tools={sessionTools}
+      components={[sessionCardTamboComponent, taskOverviewTamboComponent]}
</file context>
Fix with Cubic

tools={sessionTools}
components={[sessionCardTamboComponent, taskOverviewTamboComponent]}
Comment on lines +140 to +144
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Hard-coded tambo userkey 🐞 Bug ⛨ Security

TamboProvider is initialized with a constant userKey ("companion-user"), so different authenticated
users/devices can share the same Tambo identity and potentially see each other’s threads/history.
This is a privacy/security risk for an app with auth tokens.
Agent Prompt
### Issue description
`userKey` is hard-coded to a constant value, which collapses all users into one shared Tambo identity and can mix threads/history across users.

### Issue Context
The UI uses an auth token (`companion_auth_token`) and tracks authentication state in the store, so multiple distinct principals are expected.

### Fix Focus Areas
- web/src/components/RegentPanel.tsx[139-145]
- web/src/store.ts[33-44]
- web/src/api.ts[5-13]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

>
Comment on lines +140 to +145
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

userKey is hard-coded to "companion-user". If Tambo uses userKey to scope threads/history, this will mix thread identity across all users/browsers sharing the same API key (and can lead to unexpected data cross-talk). Generate a stable per-browser/user key (e.g., persisted UUID in localStorage, or derived from an authenticated user id) instead of a constant.

Copilot uses AI. Check for mistakes.
{children}
</TamboProvider>
);
}

export function RegentPanel() {
return (
<RegentProvider>
<RegentChat />
</RegentProvider>
);
}

export default RegentPanel;
149 changes: 149 additions & 0 deletions web/src/components/RightPanel.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
// @vitest-environment jsdom
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import { describe, it, expect, vi, beforeEach } from "vitest";

const mockSwitchThread = vi.fn();
const mockStartNewThread = vi.fn();

vi.mock("@tambo-ai/react", () => ({
TamboProvider: ({ children }: { children: React.ReactNode }) => <div data-testid="tambo-provider">{children}</div>,
useTambo: () => ({
messages: [],
isStreaming: false,
currentThreadId: "thread-1",
switchThread: mockSwitchThread,
startNewThread: mockStartNewThread,
client: {},
thread: undefined,
streamingState: { status: "idle" },
isWaiting: false,
isIdle: true,
registerComponent: vi.fn(),
registerTool: vi.fn(),
registerTools: vi.fn(),
componentList: new Map(),
toolRegistry: new Map(),
initThread: vi.fn(),
dispatch: vi.fn(),
cancelRun: vi.fn(),
authState: { status: "identified" },
isIdentified: true,
updateThreadName: vi.fn(),
}),
useTamboThreadInput: () => ({
value: "",
setValue: vi.fn(),
submit: vi.fn(),
isPending: false,
}),
useTamboThreadList: () => ({
data: {
threads: [
{ id: "thread-1", name: "Thread One", runStatus: "idle", createdAt: "2025-01-01", updatedAt: "2025-01-01" },
{ id: "thread-2", name: "Thread Two", runStatus: "idle", createdAt: "2025-01-01", updatedAt: "2025-01-01" },
],
hasMore: false,
},
isLoading: false,
isError: false,
}),
ComponentRenderer: () => null,
}));

const workerStub = vi.hoisted(() => {
return vi.fn().mockImplementation(() => ({
postMessage: vi.fn(),
terminate: vi.fn(),
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
onmessage: null,
onerror: null,
}));
});

vi.stubGlobal("Worker", workerStub);

vi.mock("../regent/tools/session-tools.js", () => ({
sessionTools: [],
}));

vi.mock("../regent/components/SessionCard.js", () => ({
sessionCardTamboComponent: {
name: "SessionCard",
description: "test",
component: () => null,
propsSchema: { type: "object" as const, properties: {}, required: [] },
},
SessionCard: () => null,
}));

vi.mock("../regent/components/TaskOverview.js", () => ({
taskOverviewTamboComponent: {
name: "TaskOverview",
description: "test",
component: () => null,
propsSchema: { type: "object" as const, properties: {}, required: [] },
},
TaskOverview: () => null,
}));

import { RegentSidebar } from "./RightPanel.js";
import { useStore } from "../store.js";

beforeEach(() => {
vi.clearAllMocks();
useStore.setState({
regentPanelOpen: true,
rightPanelActiveTab: "",
});
Comment on lines +91 to +99
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

This test relies on hasTamboApiKey() being true, but RegentPanel reads VITE_TAMBO_API_KEY at module import time and there’s no stub/mock here. On machines/CI without that env var, RegentSidebar will return null and these assertions will fail. Consider mocking ./RegentPanel.js in this test (like TopBar.test does), or refactor RegentPanel to read import.meta.env at runtime so you can vi.stubEnv() + dynamic import reliably.

Copilot uses AI. Check for mistakes.
});

describe("RegentSidebar", () => {
it("renders Regent thread tabs from Tambo thread list", () => {
render(<RegentSidebar />);
expect(screen.getByTitle("Thread One")).toBeInTheDocument();
expect(screen.getByTitle("Thread Two")).toBeInTheDocument();
Comment on lines +103 to +106
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Decouple RightPanel tests from Tambo env configuration

These tests assume RegentSidebar renders, but the component short-circuits to null when hasTamboApiKey() is false; since this file does not mock that guard (unlike TopBar.test.tsx), the assertions become environment-dependent and will fail in CI configurations without VITE_TAMBO_API_KEY (I checked .github/workflows/ci.yml, which only injects PostHog vars for test/build jobs).

Useful? React with 👍 / 👎.

});

it("shows new thread button", () => {
render(<RegentSidebar />);
expect(screen.getByTitle("New Regent thread")).toBeInTheDocument();
});

it("renders RegentChat when panel is open", () => {
render(<RegentSidebar />);
expect(screen.getByPlaceholderText("Ask the Regent...")).toBeInTheDocument();
});

it("switching to a thread tab calls switchThread", () => {
render(<RegentSidebar />);
fireEvent.click(screen.getByTitle("Thread Two"));
expect(mockSwitchThread).toHaveBeenCalledWith("thread-2");
});

it("collapses panel when closed", () => {
useStore.setState({ regentPanelOpen: false });
const { container } = render(<RegentSidebar />);
const panelWrapper = container.querySelector(".translate-x-full");
expect(panelWrapper).toBeInTheDocument();
});

it("clicking new thread button calls startNewThread", () => {
render(<RegentSidebar />);
fireEvent.click(screen.getByTitle("New Regent thread"));
expect(mockStartNewThread).toHaveBeenCalled();
});

it("shows diamond header icon in vertical tab bar", () => {
render(<RegentSidebar />);
const tabBar = screen.getByTitle("New Regent thread").parentElement;
expect(tabBar).toBeInTheDocument();
});

it("shows numbered tabs for each thread", () => {
render(<RegentSidebar />);
expect(screen.getByText("1")).toBeInTheDocument();
expect(screen.getByText("2")).toBeInTheDocument();
});
});
Comment on lines +1 to +149
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. rightpanel.test.tsx missing axe 📘 Rule violation ⛯ Reliability

web/src/components/RightPanel.test.tsx includes render and interaction checks but does not run an
axe accessibility scan with toHaveNoViolations(). This violates the required accessibility
baseline for new/modified components.
Agent Prompt
## Issue description
`web/src/components/RightPanel.test.tsx` lacks the required axe accessibility test (`expect(results).toHaveNoViolations()`).

## Issue Context
Other component tests in the repo use `vitest-axe` (dynamic import) to enforce a baseline accessibility check. Rule 2 requires this for each new/modified component test.

## Fix Focus Areas
- web/src/components/RightPanel.test.tsx[1-149]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Loading