Skip to content

Vercel Sandbox Integration#640

Open
ngoiyaeric wants to merge 5 commits into
mainfrom
feature/vercel-sandbox-integration-4831652204283404284
Open

Vercel Sandbox Integration#640
ngoiyaeric wants to merge 5 commits into
mainfrom
feature/vercel-sandbox-integration-4831652204283404284

Conversation

@ngoiyaeric

@ngoiyaeric ngoiyaeric commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

This PR integrates Vercel Sandbox into QCX, enabling the AI to execute JavaScript and TypeScript code in isolated microVMs.

Key features:

  • Sandbox Tool: A new tool that allows the AI to run code, install npm dependencies, and host web servers.
  • Log Streaming: Real-time streaming of stdout and stderr from the sandbox to the chat UI.
  • Live Previews: Automatically detects web servers and provides a public preview URL rendered in an iframe.
  • UI Components: Terminal-like log viewer and security-conscious iframe container for previews.
  • Agent Integration: Updated the researcher's system prompt to use the sandbox for data transformations and visualization.

Infrastructure:

  • Added @vercel/sandbox package.
  • Defined Zod schemas and TypeScript types for sandbox operations.
  • Added Vercel credentials to .env.example.
  • Updated app/actions.tsx to handle sandbox tool results in history.

PR created automatically by Jules for task 4831652204283404284 started by @ngoiyaeric

Summary by CodeRabbit

  • New Features
    • Added an AI-driven code execution sandbox with real-time stdout/stderr logs.
    • Added live preview for server-style code execution (with refresh/open-in-new-tab).
    • Added execution status reporting, including exit codes and error display.
    • Added “Intelligence Executive Summary” to generated reports using AI-derived context.
  • Configuration & Docs
    • Updated the environment example with required values for sandbox authentication.
    • Added a cloud deployment guide for the full stack.
  • Bug Fixes
    • Improved startup behavior when database URL is missing (warns instead of failing).
  • Tests
    • Added a Playwright spec to verify report rendering.

- Add @vercel/sandbox dependency and types
- Implement `sandbox` tool with support for JS/TS and live previews
- Create UI components for streaming logs and iframe previews
- Update researcher agent with code execution capabilities
- Handle tool result reconstruction in message history

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
qcx Ready Ready Preview, Comment Jun 19, 2026 12:23pm

@CLAassistant

CLAassistant commented Jun 1, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ ngoiyaeric
❌ google-labs-jules[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Introduces code execution via Vercel's microVM sandbox, enhances PDF report generation with AI-generated summaries, expands database schema with geospatial and user collaboration tables, and containerizes the full stack with Docker Compose for local development and cloud deployment with comprehensive deployment documentation and security guidance.

Changes

Sandbox Code Execution

Layer / File(s) Summary
Type contracts and validation schema
lib/schema/sandbox.ts, lib/types/index.ts
Zod schema requires code, language (JS/TS enum), filename, and optional dependencies array. SandboxResult type defines output with logs array, exit code, optional preview URL, and optional error string.
Sandbox execution engine
lib/agents/tools/sandbox.tsx
Executes JavaScript/TypeScript in isolated Vercel microVM: writes source file, installs npm dependencies conditionally, detects server-like code via regex, runs detached for servers (obtaining preview domain on port 3000) or awaited for scripts. Streams stdout/stderr asynchronously and ensures sandbox cleanup on success or error.
Agent routing and tool registration
lib/agents/researcher.tsx, lib/agents/tools/index.tsx
System prompt adds "Code Execution and Data Transformation" section guiding code/data queries to sandbox tool. Tool conditionally registered only when VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID present.
Log viewer, preview, and orchestration UI
components/sandbox-logs.tsx, components/sandbox-preview.tsx, components/sandbox-section.tsx, components/section.tsx
SandboxLogs renders scrollable colored output with stderr/stdout differentiation and auto-scroll. SandboxPreview displays iframe with refresh/reload controls. SandboxSection orchestrates both with error display and exit-code rendering. Section adds Terminal icon for Sandbox titles.
Action layer sandbox result handling
app/actions.tsx
getUIStateFromAIState handles sandbox_result tool messages: streams logs via useStreamableValue, renders SandboxSection with preview URL, exit code, and error, and returns UI element with computed isCollapsed state.
Environment and package configuration
.env.local.example, package.json
Adds VERCEL_TOKEN, VERCEL_TEAM_ID, VERCEL_PROJECT_ID environment placeholders with setup instructions. Registers @vercel/sandbox ^2.0.2 production dependency.

Report Generation with AI Summaries

Layer / File(s) Summary
AI-driven title and summary generation
lib/actions/chat.ts
New generateReportContext server action filters chat messages to user/assistant responses, builds prompt, calls model with fixed geospatial intelligence instruction requesting JSON {title, summary}, and returns deterministic fallback on parse error.
Report download button with action invocation
components/download-report-button.tsx
Imports useActions to invoke generate_report_context with chat messages, captures map canvas as PNG data URL (replacing JPEG), and wires generated title/summary into report template via ReportTemplate props.
Report template with AI summary rendering
components/report-template.tsx
ReportTemplate accepts optional aiSummary prop and conditionally renders "Intelligence Executive Summary" section as Markdown via ReactMarkdown/remark-gfm. Reworks message filtering logic and updates resolution_search_result layout from 2-column grid to vertically stacked centered flex.
PDF export image quality and format
lib/utils/report-generator.ts
Increases html2canvas scale for sharper rendering and switches intermediate image format from JPEG to PNG for lossless PDF embedding.

Container Infrastructure and Database Schema

Layer / File(s) Summary
Database schema expansion
drizzle/migrations/0001_sync_schema_full.sql, drizzle/migrations/meta/...
Comprehensive migration enables postgis/vector extensions, creates five new tables (calendar_notes, chat_participants, locations, system_prompts, visualizations) with geospatial/collaboration fields. Alters existing tables to add vector embeddings, sharing links, user metadata. Updates Drizzle snapshots with full schema metadata across 8 tables.
Docker Compose orchestration
docker-compose.yaml
Adds dedicated db (Postgres) and qdrant services with healthchecks and persistent volumes. Production qcx service uses runner target, inlines computed DATABASE_URL/QDRANT_URL, depends on healthy db/qdrant. Dev service wired for hot reload with database connectivity. Top-level volumes manage postgres_data and qdrant_data persistence.
Container startup and runtime
Dockerfile, docker-entrypoint.sh, .dockerignore
Adds docker-entrypoint.sh that conditionally runs migrations when EXECUTE_MIGRATIONS=true, then execs main process. Dockerfile runner stage copies migration assets (drizzle/, lib/db/migrate.ts), copies executable entrypoint.sh, and switches startup to ENTRYPOINT. Removes drizzle/ from .dockerignore.
Database initialization and version upgrades
lib/db/index.ts, package.json
Replaces hard error on missing DATABASE_URL with production-only warning and localhost fallback. Removes cross-env wrapper from db:migrate script. Upgrades drizzle-orm to ^0.45.2 and @playwright/test to ^1.60.0.
Cloud deployment guide and security
CLOUD_DEPLOYMENT.md
Comprehensive guide covering Docker Compose local setup, Render managed Postgres/web deployment with SQL extension commands, Google Cloud Run/Cloud SQL deployment, vector database options (pgvector vs Qdrant), and production security checklist for passwords/SSL/NODE_ENV/secrets.
Playwright test for report
tests/verify_report.spec.ts
New test navigates to /, sends chat message via test IDs, and waits for bot response with placeholder comments for future report-download assertions.

Sequence Diagram

sequenceDiagram
  participant Agent
  participant SandboxTool
  participant Vercel as Vercel Sandbox
  participant UIStream
  Agent->>SandboxTool: execute(code, language, filename, dependencies)
  SandboxTool->>Vercel: write file, install dependencies
  alt detect server code
    SandboxTool->>Vercel: start tsx server (detached)
    SandboxTool->>Vercel: obtain domain for port 3000
    SandboxTool->>UIStream: stream preview URL
  else script
    SandboxTool->>Vercel: run tsx/node awaited
  end
  Vercel->>SandboxTool: stdout/stderr output
  SandboxTool->>UIStream: stream logs asynchronously
  SandboxTool->>Agent: return sandbox_result with logs, exitCode, previewUrl
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • QueueLab/QCX#638: Both PRs modify report generation components (components/download-report-button.tsx, components/report-template.tsx, lib/utils/report-generator.ts) with overlapping changes to PDF export and template rendering.
  • QueueLab/QCX#665: Both PRs modify Docker setup and startup flow including Dockerfile runner stage, docker-entrypoint.sh migrations, docker-compose.yaml services, and lib/db/index.ts database initialization.
  • QueueLab/QCX#668: Both PRs modify docker-compose.yaml vector database wiring and drizzle/migrations/ schema files for database infrastructure expansion.

Suggested labels

Review effort 4/5, database schema, docker infrastructure, AI features

Suggested reviewers

  • CharlieHelps

Poem

🐰 A sandbox whispers secrets in the VM,
Reports bloom bright with summaries at the hem,
Docker containers dance with postgres grace,
Geospatial queries find their place! 🗺️✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Vercel Sandbox Integration' directly and clearly summarizes the main feature added across the changeset—a comprehensive sandbox tool integration enabling code execution in isolated microVMs.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/vercel-sandbox-integration-4831652204283404284

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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 and usage tips.

@qodo-code-review

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Integrate Vercel Sandbox for code execution and live previews

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Integrates Vercel Sandbox for isolated JavaScript/TypeScript code execution
• Adds real-time log streaming and live preview capabilities for web servers
• Creates UI components for terminal-like log viewer and iframe previews
• Updates researcher agent to support code execution and data transformation
• Adds sandbox tool configuration and environment variables
Diagram
flowchart LR
  A["Researcher Agent"] -->|uses sandbox tool| B["Sandbox Tool"]
  B -->|executes code| C["Vercel Sandbox MicroVM"]
  C -->|streams logs| D["SandboxSection Component"]
  D -->|displays| E["SandboxLogs"]
  C -->|detects server| F["SandboxPreview Component"]
  F -->|renders| G["Live Preview iframe"]

Loading

Grey Divider

File Changes

1. lib/schema/sandbox.ts ⚙️ Configuration changes +30/-0

Define Zod schema for sandbox parameters

lib/schema/sandbox.ts


2. lib/types/index.ts ✨ Enhancement +8/-0

Add SandboxResult type to AIMessage

lib/types/index.ts


3. .env.local.example ⚙️ Configuration changes +6/-0

Add Vercel Sandbox environment variables

.env.local.example


View more (9)
4. app/actions.tsx ✨ Enhancement +17/-0

Handle sandbox tool results in UI state

app/actions.tsx


5. components/sandbox-logs.tsx ✨ Enhancement +52/-0

Create terminal-like log viewer component

components/sandbox-logs.tsx


6. components/sandbox-preview.tsx ✨ Enhancement +51/-0

Create iframe preview component with controls

components/sandbox-preview.tsx


7. components/sandbox-section.tsx ✨ Enhancement +51/-0

Create main sandbox section container component

components/sandbox-section.tsx


8. components/section.tsx ✨ Enhancement +6/-1

Add Terminal icon for Sandbox section

components/section.tsx


9. lib/agents/researcher.tsx 📝 Documentation +11/-4

Update researcher prompt with sandbox tool guidance

lib/agents/researcher.tsx


10. lib/agents/tools/index.tsx ✨ Enhancement +8/-0

Register sandbox tool with environment checks

lib/agents/tools/index.tsx


11. lib/agents/tools/sandbox.tsx ✨ Enhancement +127/-0

Implement sandbox tool with execution and streaming

lib/agents/tools/sandbox.tsx


12. package.json Dependencies +1/-0

Add @vercel/sandbox dependency

package.json


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0)

Grey Divider


Action required

1. Npm install injection 🐞 Bug ⛨ Security
Description
sandboxTool builds an npm install ... command by string-concatenating dependencies, allowing
command injection in the microVM (extra commands beyond npm). This breaks the tool’s security
boundary and can be used to run arbitrary unintended commands during the install step.
Code

lib/agents/tools/sandbox.tsx[R37-43]

Evidence
The tool passes a template string to runCommand with unescaped, user-controlled dependencies,
which is the classic command-injection pattern.

lib/agents/tools/sandbox.tsx[37-43]
lib/schema/sandbox.ts[8-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`sandboxTool` constructs `npm install` as a single shell string using `dependencies.join(' ')`, which enables command injection if any dependency string includes shell metacharacters.

### Issue Context
The tool parameter schema allows arbitrary strings in `dependencies`, and the tool executes them inside the sandbox via `runCommand`.

### Fix Focus Areas
- Replace string command with structured args (no shell parsing).
- Validate/whitelist dependency names (e.g., npm package name grammar) and reject anything else.

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[37-44]
- lib/schema/sandbox.ts[8-20]

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


2. Sandbox never stopped 🐞 Bug ☼ Reliability
Description
When isServer is true, the tool detaches the process and returns without ever calling
sandbox.stop(), leaving microVMs running and consuming resources. The /http|express|.../
heuristic is broad enough to misclassify non-server code, increasing the frequency of this leak.
Code

lib/agents/tools/sandbox.tsx[R46-97]

Evidence
The code only calls sandbox.stop() in the non-server branch, while the server branch detaches and
returns immediately; isServer is determined by a broad regex over the code string.

lib/agents/tools/sandbox.tsx[46-97]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In the server/detached execution path, the sandbox is never stopped, so microVMs can remain alive after the tool returns.

### Issue Context
`sandbox.stop()` is only called in the non-server branch. Server mode is triggered by a regex against the source code, which can produce false positives.

### Fix Focus Areas
- Ensure `sandbox.stop()` is invoked for server runs (e.g., via a `finally` block) or implement a controlled lifecycle/TTL shutdown strategy.
- Avoid heuristic server detection: add an explicit `mode`/`isServer` parameter or detect actual listening port readiness.
- Avoid unhandled background async failures (use `void streamLogs().catch(...)`).

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[46-97]

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


3. TypeScript runner missing 🐞 Bug ≡ Correctness
Description
TypeScript execution uses cmd = 'tsx' but the tool never installs tsx, so a TypeScript run will
fail unless the caller knows to include tsx in dependencies. This makes the advertised
TypeScript support unreliable by default.
Code

lib/agents/tools/sandbox.tsx[R46-53]

Evidence
The code picks tsx when language is typescript, but the install step only installs
user-specified dependencies and does not add tsx automatically, so tsx may be missing at
runtime.

lib/agents/tools/sandbox.tsx[37-53]
lib/schema/sandbox.ts[8-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The tool selects `tsx` as the command for TypeScript, but does not guarantee `tsx` exists in the sandbox environment.

### Issue Context
The tool only runs `npm install` when `dependencies` are provided, and it does not add any implicit dependencies based on the selected `language`.

### Fix Focus Areas
- If `language === 'typescript'`, install `tsx` (and typically `typescript`/`@types/node`) automatically before execution.
- Or run through `npx`/a pinned package runner with explicit args.

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[37-53]
- lib/schema/sandbox.ts[8-20]

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



Remediation recommended

4. Server logs not saved 🐞 Bug ☼ Reliability
Description
For server executions, streamLogs() runs asynchronously but the tool returns immediately, so the
persisted tool message stores only the initial snapshot of logs at return time. After a
refresh/rehydration, getUIStateFromAIState will render incomplete/empty logs even if the user saw
more logs streamed live.
Code

lib/agents/tools/sandbox.tsx[R68-97]

Evidence
The sandbox tool returns before the async log-streaming finishes, but the app persists tool results
immediately via JSON.stringify, freezing whatever the logs array contained at return time.

lib/agents/tools/sandbox.tsx[68-97]
app/actions.tsx[478-493]
app/actions.tsx[788-859]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In server mode, logs are streamed after the tool returns, but the app persists tool output immediately; this loses later log entries in saved chat state.

### Issue Context
`app/actions.tsx` saves tool outputs by `JSON.stringify(output.result)` when the tool returns. In server mode the returned object includes `logs` before streaming completes.

### Fix Focus Areas
- Delay returning until you have captured a meaningful initial log set (or until server readiness), then return that snapshot.
- Alternatively, write logs to a durable store keyed by run ID and persist only the run ID in AI state; hydrate logs from storage on reload.

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[68-97]
- app/actions.tsx[478-493]

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


Grey Divider

Qodo Logo

Comment on lines +37 to +43
if (dependencies && dependencies.length > 0) {
const installCommand = await sandbox.runCommand(`npm install ${dependencies.join(' ')}`)
for await (const log of installCommand.logs()) {
const logEntry = { type: log.stream as 'stdout' | 'stderr', content: log.data }
logs.push(logEntry)
streamableLogs.update([...logs])
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

1. Npm install injection 🐞 Bug ⛨ Security

sandboxTool builds an npm install ... command by string-concatenating dependencies, allowing
command injection in the microVM (extra commands beyond npm). This breaks the tool’s security
boundary and can be used to run arbitrary unintended commands during the install step.
Agent Prompt
### Issue description
`sandboxTool` constructs `npm install` as a single shell string using `dependencies.join(' ')`, which enables command injection if any dependency string includes shell metacharacters.

### Issue Context
The tool parameter schema allows arbitrary strings in `dependencies`, and the tool executes them inside the sandbox via `runCommand`.

### Fix Focus Areas
- Replace string command with structured args (no shell parsing).
- Validate/whitelist dependency names (e.g., npm package name grammar) and reject anything else.

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[37-44]
- lib/schema/sandbox.ts[8-20]

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

Comment on lines +46 to +97
const isServer = /http|express|serve|listen|app\.get|app\.post|createServer/.test(code)
const cmd = language === 'typescript' ? 'tsx' : 'node'

const command = await sandbox.runCommand({
cmd,
args: [filename],
detached: isServer
})

let previewUrl: string | undefined
if (isServer) {
previewUrl = await sandbox.domain(3000)
// Update UI with preview URL
uiStream.update(
<SandboxSection
key={nanoid()}
logs={streamableLogs.value}
previewUrl={previewUrl}
/>
)
}

const streamLogs = async () => {
for await (const log of command.logs()) {
const logEntry = { type: log.stream as 'stdout' | 'stderr', content: log.data }
logs.push(logEntry)
streamableLogs.update([...logs])
}
streamableLogs.done(logs)
}

if (isServer) {
// For servers, stream logs in the background and return immediately
streamLogs()
} else {
// For regular scripts, wait for execution to complete
await streamLogs()
const result = await (command as any).wait()
await sandbox.stop()
return {
logs,
exitCode: result.exitCode,
type: 'sandbox_result'
}
}

return {
logs,
exitCode: 0,
previewUrl,
type: 'sandbox_result'
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

2. Sandbox never stopped 🐞 Bug ☼ Reliability

When isServer is true, the tool detaches the process and returns without ever calling
sandbox.stop(), leaving microVMs running and consuming resources. The /http|express|.../
heuristic is broad enough to misclassify non-server code, increasing the frequency of this leak.
Agent Prompt
### Issue description
In the server/detached execution path, the sandbox is never stopped, so microVMs can remain alive after the tool returns.

### Issue Context
`sandbox.stop()` is only called in the non-server branch. Server mode is triggered by a regex against the source code, which can produce false positives.

### Fix Focus Areas
- Ensure `sandbox.stop()` is invoked for server runs (e.g., via a `finally` block) or implement a controlled lifecycle/TTL shutdown strategy.
- Avoid heuristic server detection: add an explicit `mode`/`isServer` parameter or detect actual listening port readiness.
- Avoid unhandled background async failures (use `void streamLogs().catch(...)`).

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[46-97]

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

Comment on lines +46 to +53
const isServer = /http|express|serve|listen|app\.get|app\.post|createServer/.test(code)
const cmd = language === 'typescript' ? 'tsx' : 'node'

const command = await sandbox.runCommand({
cmd,
args: [filename],
detached: isServer
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Action required

3. Typescript runner missing 🐞 Bug ≡ Correctness

TypeScript execution uses cmd = 'tsx' but the tool never installs tsx, so a TypeScript run will
fail unless the caller knows to include tsx in dependencies. This makes the advertised
TypeScript support unreliable by default.
Agent Prompt
### Issue description
The tool selects `tsx` as the command for TypeScript, but does not guarantee `tsx` exists in the sandbox environment.

### Issue Context
The tool only runs `npm install` when `dependencies` are provided, and it does not add any implicit dependencies based on the selected `language`.

### Fix Focus Areas
- If `language === 'typescript'`, install `tsx` (and typically `typescript`/`@types/node`) automatically before execution.
- Or run through `npx`/a pinned package runner with explicit args.

### Fix Focus Areas (code locations)
- lib/agents/tools/sandbox.tsx[37-53]
- lib/schema/sandbox.ts[8-20]

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

@ngoiyaeric

Copy link
Copy Markdown
Collaborator Author

Can you synchronize this branch to the main branch.

@google-labs-jules

Copy link
Copy Markdown
Contributor

Can you synchronize this branch to the main branch.

I have merged the latest changes from the main branch into this feature branch. I also resolved a conflict in bun.lock. I'm now fixing a build error that appeared after the merge in the map component to ensure the build passes before submitting.

- Merged latest changes from main branch
- Resolved bun.lock conflict
- Fixed type error in Map3D component center prop
- Verified build passes

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/sandbox-preview.tsx`:
- Around line 25-31: The preview controls and iframe lack accessible names; add
explicit accessible labels to the icon-only controls and the iframe: give the
reload Button (onClick={reload}) an aria-label="Reload preview" (or wrap the
icon with a visually-hidden <span> for screen readers), give the external link
anchor or the Button with asChild an aria-label="Open preview in new tab" (or
include hidden text), and add a descriptive title and/or aria-label to the
iframe (e.g., title="Live preview of user sandbox" and aria-label="Sandbox
preview iframe") so assistive tech can identify each control and the frame;
update the elements referenced (the Button with onClick={reload}, the Button
asChild wrapping <a href={url} ...>, and the iframe element) accordingly.

In `@lib/agents/researcher.tsx`:
- Around line 50-55: Update the sandbox tool description (the "sandbox" entry in
lib/agents/researcher.tsx) to explicitly document network-access security
implications: state that user code can make arbitrary HTTP requests
(SSRF/exfiltration risk), potential to reach internal/localhost services or
bypass IP allowlists, and recommend mitigations such as running sandboxes in an
isolated network namespace, applying egress filtering, and clearly defining
allowed/forbidden endpoints; include a brief guidance sentence for operators on
when to enable the feature and where to configure these controls.

In `@lib/agents/tools/sandbox.tsx`:
- Around line 26-30: The Sandbox.create call currently hardcodes ports [3000]
and timeout 60000; update this to accept configurable values instead: add
optional parameters (e.g., ports and timeout) to the function or config schema
that lead to Sandbox.create({ runtime: 'node24', ports: providedPorts || [3000],
timeout: providedTimeout || 60000 }), and ensure callers can pass multiple ports
(array) and a numeric timeout; reference the Sandbox.create invocation and the
sandbox variable so you change the invocation site and propagate the new
optional parameters through the calling code and schema.
- Line 46: The current fragile detection using the isServer regex in sandbox.tsx
can miss frameworks and hit false-positives; add an optional isServer: boolean
to sandboxSchema and prefer that explicit flag in the code that computes
isServer (use the schema value when present), and as a fallback expand detection
patterns to include Fastify/Koa/Hapi/Socket.io/ws and strip comments/strings
before regex testing (or use a simple AST/token scan) so function/method name
references like isServer and the server detection logic in sandbox.tsx reliably
reflect intent.
- Around line 77-97: When isServer is true, avoid leaking and misreporting by:
(1) running streamLogs() with error handling (call streamLogs().catch(err => {
/* log and stop sandbox */ })) so errors aren’t swallowed; (2) ensure
sandbox.stop() is called on any streamLogs error and register the sandbox with a
lifecycle manager (e.g., add to an activeSandboxes registry with a stop() API
and/or TTL) so server-mode sandboxes can be cleaned up; (3) don’t return a live
reference to logs—return a shallow copy (e.g., [...logs]) to avoid race
conditions; and (4) don’t return exitCode: 0 immediately — either wait a short
startup period or return exitCode: undefined (or a "running" state) so callers
know the server may still fail; update the code paths around isServer,
streamLogs, sandbox.stop, logs and previewUrl accordingly.
- Line 13: Change the execute function signature to use the concrete
SandboxParameters type instead of any: import the SandboxParameters type from
the schema and update execute: async (params: SandboxParameters) => { ... }
(then destructure const { code, language, filename, dependencies } = params).
Update any other references to the execute parameter shape to use
SandboxParameters so the function and callers get full type safety.

In `@lib/schema/sandbox.ts`:
- Around line 13-15: The filename schema (the z.string() chain for filename in
lib/schema/sandbox.ts) currently accepts arbitrary strings; update it to
validate that values contain no path separators or navigational components by
adding a refinement or regex check (reject '/' and '\' and any '..' segments) —
e.g., use z.string().refine(...) or z.string().regex(...) to enforce that the
input equals its basename and does not include ".." or path separators, and
provide a clear error message for invalid filenames.
- Around line 16-19: The dependencies schema currently allows any string
(dependencies: z.array(z.string()).optional()), so add validation that each
entry matches npm package naming rules: update the array element from z.string()
to either z.string().regex(/.../) with a regex implementing npm name constraints
or z.string().refine(v => validateNpmPackageName(v), { message: 'invalid npm
package name' }) and include a clear error message; keep the .optional() and
.describe(...) and reference the symbol name "dependencies" in
lib/schema/sandbox.ts when making this change.
- Line 30: PartialSandboxParameters is incorrectly defined using
DeepPartial<typeof sandboxSchema>; DeepPartial expects a TypeScript type, not a
Zod schema. Replace DeepPartial<typeof sandboxSchema> with
DeepPartial<SandboxParameters> (the TS type corresponding to sandboxSchema) and
ensure SandboxParameters is imported or defined in the file so
PartialSandboxParameters becomes DeepPartial<SandboxParameters>.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dac3a1ff-a3d3-4ca1-85e6-64ac57eeff1e

📥 Commits

Reviewing files that changed from the base of the PR and between e6045ca and d41ece3.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • .env.local.example
  • app/actions.tsx
  • components/sandbox-logs.tsx
  • components/sandbox-preview.tsx
  • components/sandbox-section.tsx
  • components/section.tsx
  • lib/agents/researcher.tsx
  • lib/agents/tools/index.tsx
  • lib/agents/tools/sandbox.tsx
  • lib/schema/sandbox.ts
  • lib/types/index.ts
  • package.json
📜 Review details
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-06-07T16:19:00.111Z
Learnt from: ngoiyaeric
Repo: QueueLab/QCX PR: 0
File: :0-0
Timestamp: 2026-06-07T16:19:00.111Z
Learning: In the QCX repository, `package.json` must keep all `ai-sdk/*` provider packages at the `^1.x` series (e.g., `ai-sdk/openai@^1.3.22`, `ai-sdk/anthropic@^1.1.6`, `ai-sdk/google@^1.0.27`, `ai-sdk/xai@^1.2.18`, `ai-sdk/amazon-bedrock@^2.2.10`) to remain compatible with the pinned `ai4.3.19` core. Upgrading providers to `^3.x` or `^4.x` causes `AI_UnsupportedModelVersionError` because those versions implement model spec "v2" which AI SDK 4 does not support.

Applied to files:

  • package.json
📚 Learning: 2026-02-08T16:50:33.791Z
Learnt from: ngoiyaeric
Repo: QueueLab/QCX PR: 0
File: :0-0
Timestamp: 2026-02-08T16:50:33.791Z
Learning: The PR branch implements RAG (Retrieval-Augmented Generation) via `lib/actions/rag.ts` which uses Supabase RPC functions `generate_embedding()` and `hybrid_search()` to retrieve relevant context from past conversations. However, there's a bug on line 433 of `app/actions.tsx` where `currentSystemPrompt` is passed to the researcher instead of `augmentedSystemPrompt`, preventing the retrieved context from being used.

Applied to files:

  • lib/agents/researcher.tsx
🪛 dotenv-linter (4.0.0)
.env.local.example

[warning] 36-36: [UnorderedKey] The VERCEL_PROJECT_ID key should go before the VERCEL_TEAM_ID key

(UnorderedKey)

🔇 Additional comments (12)
components/sandbox-logs.tsx (1)

16-50: LGTM!

components/sandbox-section.tsx (1)

21-49: LGTM!

components/section.tsx (1)

11-12: LGTM!

Also applies to: 23-23, 54-56

app/actions.tsx (1)

30-30: LGTM!

Also applies to: 901-916

.env.local.example (1)

32-37: LGTM!

package.json (1)

55-55: Check @vercel/sandbox security advisories and lockfile pin

  • GitHub’s NPM ecosystem security-advisory lookup for @vercel/sandbox returns no findings.
  • package.json uses ^2.0.2, which already allows newer 2.x versions; the “is 2.0.2 current?” concern only applies to what the lockfile actually resolves—confirm the pinned version in bun.lock and bump if you want the latest.
lib/types/index.ts (1)

77-85: LGTM!

lib/agents/tools/sandbox.tsx (2)

37-44: Potential npm install timeout for large dependency trees.

Large or complex dependency installations (e.g., tensorflow, puppeteer) can take significantly longer than the 60-second sandbox timeout, especially on cold starts. This could lead to timeout errors before installation completes.

Consider whether the 60-second timeout is sufficient for realistic dependency installations. If this becomes a problem in production, you may need to:

  • Increase the timeout for dependency installation scenarios
  • Pre-install common dependencies in the sandbox image
  • Document limitations on which packages can be installed

83-83: Avoid (command as any).wait() unless @vercel/sandbox@2.0.2’s Command type includes wait().

The concern is still valid: the cast bypasses type safety. However, the requested local verification can’t be completed here because node_modules/@vercel/sandbox/dist/index.d.ts is missing in this environment, so the presence/absence of the wait() typing can’t be confirmed from the SDK types.

lib/agents/researcher.tsx (1)

77-80: LGTM!

lib/agents/tools/index.tsx (2)

43-46: LGTM!


42-42: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Empty string environment variables pass the truthiness check.

The condition process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID will be truthy even if these variables are set to empty strings (e.g., VERCEL_TOKEN=""). This could lead to runtime errors when the sandbox tries to authenticate.

🔧 Proposed fix
-  if (process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID) {
+  if (process.env.VERCEL_TOKEN?.trim() && process.env.VERCEL_TEAM_ID?.trim() && process.env.VERCEL_PROJECT_ID?.trim()) {
     tools.sandbox = sandboxTool({
       uiStream,
       fullResponse
     })
   }
			> Likely an incorrect or invalid review comment.

Comment on lines +25 to +31
<Button variant="ghost" size="icon" className="h-7 w-7" onClick={reload}>
<RefreshCw className="h-3.5 w-3.5" />
</Button>
<Button variant="ghost" size="icon" className="h-7 w-7" asChild>
<a href={url} target="_blank" rel="noopener noreferrer">
<ExternalLink className="h-3.5 w-3.5" />
</a>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add accessible labels to preview controls and iframe.

Line 25 and Line 28 render icon-only actions without accessible names, and Line 41 renders an unlabeled iframe. This makes the preview controls/frame hard to use with assistive tech.

Suggested fix
-          <Button variant="ghost" size="icon" className="h-7 w-7" onClick={reload}>
+          <Button
+            variant="ghost"
+            size="icon"
+            className="h-7 w-7"
+            onClick={reload}
+            aria-label="Reload sandbox preview"
+          >
             <RefreshCw className="h-3.5 w-3.5" />
           </Button>
           <Button variant="ghost" size="icon" className="h-7 w-7" asChild>
-            <a href={url} target="_blank" rel="noopener noreferrer">
+            <a
+              href={url}
+              target="_blank"
+              rel="noopener noreferrer"
+              aria-label="Open sandbox preview in a new tab"
+            >
               <ExternalLink className="h-3.5 w-3.5" />
             </a>
           </Button>
@@
         <iframe
           key={key}
           src={url}
+          title="Sandbox live preview"
           className="w-full h-full border-none"
           sandbox="allow-scripts allow-same-origin allow-forms"
           onLoad={() => setIsLoading(false)}
         />

Also applies to: 41-47

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@components/sandbox-preview.tsx` around lines 25 - 31, The preview controls
and iframe lack accessible names; add explicit accessible labels to the
icon-only controls and the iframe: give the reload Button (onClick={reload}) an
aria-label="Reload preview" (or wrap the icon with a visually-hidden <span> for
screen readers), give the external link anchor or the Button with asChild an
aria-label="Open preview in new tab" (or include hidden text), and add a
descriptive title and/or aria-label to the iframe (e.g., title="Live preview of
user sandbox" and aria-label="Sandbox preview iframe") so assistive tech can
identify each control and the frame; update the elements referenced (the Button
with onClick={reload}, the Button asChild wrapping <a href={url} ...>, and the
iframe element) accordingly.

Comment thread lib/agents/researcher.tsx
Comment on lines +50 to +55
#### **3. Code Execution and Data Transformation**
- **Tool**: \`sandbox\`
- **When to use**:
Use this to execute JavaScript or TypeScript code snippets, perform complex data transformations, generate dynamic HTML reports, or visualize data. The sandbox provides an isolated environment with network access and can host web servers for live previews.
- **Features**: Supports installing npm dependencies and provides a public preview URL if a web server (e.g., Express) is detected.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider documenting network access security implications.

The prompt states the sandbox provides "network access," which enables powerful data-fetching use cases but also introduces SSRF (Server-Side Request Forgery) risk. While this is inherent to the feature's design, consider documenting the security implications:

  • User code can make arbitrary HTTP requests to internal/external services
  • Could be used to scan internal networks or exfiltrate data
  • Could bypass IP allowlists or access localhost services

If the sandbox runs in a controlled environment (e.g., isolated network namespace, egress filtering), mention this in documentation. Otherwise, ensure users understand the security boundaries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/researcher.tsx` around lines 50 - 55, Update the sandbox tool
description (the "sandbox" entry in lib/agents/researcher.tsx) to explicitly
document network-access security implications: state that user code can make
arbitrary HTTP requests (SSRF/exfiltration risk), potential to reach
internal/localhost services or bypass IP allowlists, and recommend mitigations
such as running sandboxes in an isolated network namespace, applying egress
filtering, and clearly defining allowed/forbidden endpoints; include a brief
guidance sentence for operators on when to enable the feature and where to
configure these controls.

return {
description: 'Execute code in an isolated microVM environment. Use this to run JavaScript or TypeScript code, perform data transformations, or generate dynamic HTML content.',
parameters: sandboxSchema,
execute: async ({ code, language, filename, dependencies }: any) => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Replace any with proper TypeScript types.

The execute function parameters should be typed using SandboxParameters from the schema to ensure type safety and better IDE support.

♻️ Proposed fix
+import { SandboxParameters } from '`@/lib/schema/sandbox`'

-    execute: async ({ code, language, filename, dependencies }: any) => {
+    execute: async ({ code, language, filename, dependencies }: SandboxParameters) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
execute: async ({ code, language, filename, dependencies }: any) => {
import { SandboxParameters } from '`@/lib/schema/sandbox`'
execute: async ({ code, language, filename, dependencies }: SandboxParameters) => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/tools/sandbox.tsx` at line 13, Change the execute function
signature to use the concrete SandboxParameters type instead of any: import the
SandboxParameters type from the schema and update execute: async (params:
SandboxParameters) => { ... } (then destructure const { code, language,
filename, dependencies } = params). Update any other references to the execute
parameter shape to use SandboxParameters so the function and callers get full
type safety.

Comment on lines +26 to +30
sandbox = await Sandbox.create({
runtime: 'node24',
ports: [3000],
timeout: 60000,
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Hardcoded port and timeout may not suit all use cases.

The sandbox is configured with a hardcoded port 3000 and timeout of 60000ms (60 seconds). Consider:

  1. Port: Some applications may need multiple ports or a different port (e.g., websockets on 8080, API on 3001).
  2. Timeout: CPU-intensive data transformations or large npm installs might exceed 60 seconds.

Consider making these configurable via the schema or exposing them as optional parameters in a future iteration if users request it.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/tools/sandbox.tsx` around lines 26 - 30, The Sandbox.create call
currently hardcodes ports [3000] and timeout 60000; update this to accept
configurable values instead: add optional parameters (e.g., ports and timeout)
to the function or config schema that lead to Sandbox.create({ runtime:
'node24', ports: providedPorts || [3000], timeout: providedTimeout || 60000 }),
and ensure callers can pass multiple ports (array) and a numeric timeout;
reference the Sandbox.create invocation and the sandbox variable so you change
the invocation site and propagate the new optional parameters through the
calling code and schema.

}
}

const isServer = /http|express|serve|listen|app\.get|app\.post|createServer/.test(code)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Server detection regex is fragile and incomplete.

The regex only detects Express/HTTP patterns but misses other popular frameworks (Fastify, Koa, Hapi, Socket.io, ws, etc.). Additionally, it can false-positive on comments or string literals containing these keywords. Consider a more robust detection strategy or make the server mode explicit via a schema parameter.

💡 Suggested improvements

Option 1: Add more patterns

-const isServer = /http|express|serve|listen|app\.get|app\.post|createServer/.test(code)
+const isServer = /http|express|fastify|koa|hapi|socket\.io|ws|serve|listen|app\.(get|post|put|delete|patch)|createServer|createHTTPServer/.test(code)

Option 2: Add explicit parameter to schema (better)
Add an optional isServer: boolean parameter to sandboxSchema to let the agent explicitly specify server mode rather than relying on heuristics.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/tools/sandbox.tsx` at line 46, The current fragile detection using
the isServer regex in sandbox.tsx can miss frameworks and hit false-positives;
add an optional isServer: boolean to sandboxSchema and prefer that explicit flag
in the code that computes isServer (use the schema value when present), and as a
fallback expand detection patterns to include Fastify/Koa/Hapi/Socket.io/ws and
strip comments/strings before regex testing (or use a simple AST/token scan) so
function/method name references like isServer and the server detection logic in
sandbox.tsx reliably reflect intent.

Comment on lines +77 to +97
if (isServer) {
// For servers, stream logs in the background and return immediately
streamLogs()
} else {
// For regular scripts, wait for execution to complete
await streamLogs()
const result = await (command as any).wait()
await sandbox.stop()
return {
logs,
exitCode: result.exitCode,
type: 'sandbox_result'
}
}

return {
logs,
exitCode: 0,
previewUrl,
type: 'sandbox_result'
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Critical: Server mode has multiple resource and correctness issues.

When isServer is true, this code has several problems:

  1. Resource leak: The sandbox is never stopped for server mode, causing resource exhaustion over time.
  2. Unhandled errors: streamLogs() runs in the background without .catch(), so errors during log streaming are silently lost.
  3. Misleading exit code: Returns exitCode: 0 immediately, even if the server crashes during startup.
  4. Race condition: The background streamLogs() modifies the logs array while the function returns a reference to it.
🔧 Recommended fixes

Fix 1: Add error handling to background streaming

         if (isServer) {
           // For servers, stream logs in the background and return immediately
-          streamLogs()
+          streamLogs().catch(err => {
+            console.error('Background log streaming failed:', err)
+            // Consider appending error to UI stream
+          })
         } else {

Fix 2: Return a copy of logs to avoid race conditions

         return {
-          logs,
+          logs: [...logs],
           exitCode: 0,
           previewUrl,
           type: 'sandbox_result'
         }

Fix 3: Consider lifecycle management for servers

Server-mode sandboxes are never stopped, leading to resource leaks. You need a strategy for cleanup:

  • Track active sandboxes and provide a shutdown API
  • Set a maximum lifetime for server sandboxes
  • Stop the sandbox when the user navigates away or closes the chat
  • Document that sandbox resources must be manually managed

Fix 4: More accurate exit code

Consider waiting briefly (e.g., 2-3 seconds) for server startup before returning, or use an exitCode: undefined to indicate "still running" rather than falsely claiming success with 0.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/agents/tools/sandbox.tsx` around lines 77 - 97, When isServer is true,
avoid leaking and misreporting by: (1) running streamLogs() with error handling
(call streamLogs().catch(err => { /* log and stop sandbox */ })) so errors
aren’t swallowed; (2) ensure sandbox.stop() is called on any streamLogs error
and register the sandbox with a lifecycle manager (e.g., add to an
activeSandboxes registry with a stop() API and/or TTL) so server-mode sandboxes
can be cleaned up; (3) don’t return a live reference to logs—return a shallow
copy (e.g., [...logs]) to avoid race conditions; and (4) don’t return exitCode:
0 immediately — either wait a short startup period or return exitCode: undefined
(or a "running" state) so callers know the server may still fail; update the
code paths around isServer, streamLogs, sandbox.stop, logs and previewUrl
accordingly.

Comment thread lib/schema/sandbox.ts
Comment on lines +13 to +15
filename: z
.string()
.describe('The name of the file to create and execute (e.g., app.js or index.ts)'),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add path traversal validation to filename.

The filename parameter accepts any string without validation. A malicious or crafted input like "../../../etc/passwd" or "../../node_modules/malicious.js" could potentially write files outside the intended sandbox directory. Add validation to ensure the filename contains no path separators or navigational components.

🛡️ Proposed fix
   filename: z
     .string()
+    .refine((val) => !val.includes('/') && !val.includes('\\') && !val.includes('..'), {
+      message: 'Filename must not contain path separators or parent directory references'
+    })
     .describe('The name of the file to create and execute (e.g., app.js or index.ts)')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
filename: z
.string()
.describe('The name of the file to create and execute (e.g., app.js or index.ts)'),
filename: z
.string()
.refine((val) => !val.includes('/') && !val.includes('\\') && !val.includes('..'), {
message: 'Filename must not contain path separators or parent directory references'
})
.describe('The name of the file to create and execute (e.g., app.js or index.ts)'),
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/sandbox.ts` around lines 13 - 15, The filename schema (the
z.string() chain for filename in lib/schema/sandbox.ts) currently accepts
arbitrary strings; update it to validate that values contain no path separators
or navigational components by adding a refinement or regex check (reject '/' and
'\' and any '..' segments) — e.g., use z.string().refine(...) or
z.string().regex(...) to enforce that the input equals its basename and does not
include ".." or path separators, and provide a clear error message for invalid
filenames.

Comment thread lib/schema/sandbox.ts
Comment on lines +16 to +19
dependencies: z
.array(z.string())
.optional()
.describe('An optional list of npm packages to install before execution')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add validation for npm package names in dependencies.

The dependencies array accepts arbitrary strings without validation. While npm will reject obviously malicious package names, adding basic validation (e.g., matching npm's naming rules) provides defense in depth and clearer error messages.

🛡️ Proposed validation
   dependencies: z
     .array(z.string())
+    .array(z.string().regex(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/, 'Invalid npm package name'))
     .optional()
     .describe('An optional list of npm packages to install before execution')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
dependencies: z
.array(z.string())
.optional()
.describe('An optional list of npm packages to install before execution')
dependencies: z
.array(z.string()
.regex(/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/, 'Invalid npm package name')
)
.optional()
.describe('An optional list of npm packages to install before execution')
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/sandbox.ts` around lines 16 - 19, The dependencies schema
currently allows any string (dependencies: z.array(z.string()).optional()), so
add validation that each entry matches npm package naming rules: update the
array element from z.string() to either z.string().regex(/.../) with a regex
implementing npm name constraints or z.string().refine(v =>
validateNpmPackageName(v), { message: 'invalid npm package name' }) and include
a clear error message; keep the .optional() and .describe(...) and reference the
symbol name "dependencies" in lib/schema/sandbox.ts when making this change.

Comment thread lib/schema/sandbox.ts
/**
* Partial type for the sandbox schema, useful for streaming tool calls.
*/
export type PartialSandboxParameters = DeepPartial<typeof sandboxSchema>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Incorrect TypeScript usage of DeepPartial.

DeepPartial<typeof sandboxSchema> is incorrect. DeepPartial expects a type, not a Zod schema object. This should be DeepPartial<SandboxParameters>.

🔧 Proposed fix
 /**
  * Partial type for the sandbox schema, useful for streaming tool calls.
  */
-export type PartialSandboxParameters = DeepPartial<typeof sandboxSchema>
+export type PartialSandboxParameters = DeepPartial<SandboxParameters>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export type PartialSandboxParameters = DeepPartial<typeof sandboxSchema>
export type PartialSandboxParameters = DeepPartial<SandboxParameters>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/schema/sandbox.ts` at line 30, PartialSandboxParameters is incorrectly
defined using DeepPartial<typeof sandboxSchema>; DeepPartial expects a
TypeScript type, not a Zod schema. Replace DeepPartial<typeof sandboxSchema>
with DeepPartial<SandboxParameters> (the TS type corresponding to sandboxSchema)
and ensure SandboxParameters is imported or defined in the file so
PartialSandboxParameters becomes DeepPartial<SandboxParameters>.

…xample

- Added comments explaining where to find VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID
- Re-verified build passes

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
@ngoiyaeric

Copy link
Copy Markdown
Collaborator Author

synchronize latest changes from the main branch

@google-labs-jules

Copy link
Copy Markdown
Contributor

synchronize latest changes from the main branch

I have synchronized the latest changes from the main branch and verified that the build still passes.

- Synchronized feature branch with main
- Resolved conflicts and verified build consistency

Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 15

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.env.local.example:
- Around line 35-39: The VERCEL_PROJECT_ID environment variable assignment is
missing from the configuration file. After the comment describing
VERCEL_PROJECT_ID on line 39, add the actual environment variable assignment
following the same pattern as VERCEL_TOKEN and VERCEL_TEAM_ID above it. The
assignment should include the variable name VERCEL_PROJECT_ID followed by an
equals sign and a placeholder value like your_vercel_project_id to match the
format and documentation of the other two Vercel environment variables in the
file.
- Around line 35-38: Reorder the environment variables in the file to maintain
alphabetical order as required by dotenv-linter. Move the VERCEL_TEAM_ID
variable block (including its comment lines) to appear before the VERCEL_TOKEN
variable block. This ensures that VERCEL_TEAM_ID comes before VERCEL_TOKEN
alphabetically, satisfying the linting rule for conventional environment
variable ordering.

In `@CLOUD_DEPLOYMENT.md`:
- Line 53: The Qdrant Docker image specification uses the `latest` tag which can
change unexpectedly and cause production deployments to break. Replace the image
reference `qdrant/qdrant:latest` with a specific pinned version like
`qdrant/qdrant:v1.13.0` to ensure consistent and reproducible deployments.
- Around line 68-69: The gcloud run deploy qcx command is incomplete for
production deployment. Update the command to include all required flags: add
--region to specify the Cloud Run region, add --allow-unauthenticated if the
service should be public, and add --set-env-vars to pass critical environment
variables like DATABASE_URL, QDRANT_URL, NODE_ENV, and API keys. Additionally,
add a new section after the Cloud Run deployment command that explains the
connection pooling requirement for Cloud Run's ephemeral nature, recommending
the use of Cloud SQL Proxy or Supavisor to handle database connections, since
direct DATABASE_URL connections will exhaust connection limits in a serverless
environment.
- Around line 14-23: The Docker Compose deployment section in
CLOUD_DEPLOYMENT.md is missing critical production considerations for data
persistence. Add a new step 4 after the "Run the stack" command that addresses
volume persistence by ensuring postgres_data and qdrant_data volumes are
properly mounted on the host, provides guidance on implementing regular backups
of these volumes, and recommends considering external managed databases (RDS,
Cloud SQL, Neon) as alternatives for production workloads to ensure data
durability and reliability.

In `@components/download-report-button.tsx`:
- Around line 56-60: The messages being appended to formData contain potentially
large base64 image data URLs that can exceed server action payload limits.
Before calling formData.append with the stringified aiState.messages, sanitize
the messages array by filtering or transforming it to remove or strip out image
data URLs. Apply this sanitization to the messages before JSON.stringify is
called so the payload sent to the generate_report_context server action remains
within size limits.

In `@docker-compose.yaml`:
- Around line 10-12: The docker-compose.yaml file contains weak default
credentials for PostgreSQL that will be used if environment variables are not
set. Remove the insecure fallback values from the POSTGRES_PASSWORD environment
variable (and similar password/credential fields at lines 46-47 and 73-74) by
deleting the weak default values like qcxpassword after the :- operator. These
sensitive credentials should be explicitly required from environment variables
rather than falling back to hardcoded weak defaults, ensuring credentials must
be intentionally provided in production deployments.
- Around line 47-53: The qcx service declares a dependency on the qdrant service
via the QDRANT_URL environment variable but does not list qdrant in the
depends_on section. Add qdrant as a dependency in the depends_on block alongside
db, using the condition service_healthy to ensure qdrant is ready before qcx
attempts to start and connect to it.

In `@docker-entrypoint.sh`:
- Around line 5-9: The migration failure handling in the EXECUTE_MIGRATIONS
block masks errors and allows the application to continue starting even when
migrations fail, which can cause runtime and data issues. Remove the || echo
"Migration failed, but continuing..." clause from the bun run db:migrate command
so that if the migration command fails, the script will exit immediately and
prevent the application from starting with an incompatible database schema.

In `@drizzle/migrations/0001_sync_schema_full.sql`:
- Around line 61-62: The two ALTER TABLE statements adding shareable_link_id and
updated_at columns to the chats table use volatile defaults (gen_random_uuid()
and now()) which trigger full table rewrites and exclusive locks on PostgreSQL.
Replace these two statements with a phased migration: first add both columns as
nullable without any defaults, then add separate UPDATE statements to backfill
the columns with the desired values in batches (for example, UPDATE chats SET
shareable_link_id = gen_random_uuid() WHERE shareable_link_id IS NULL LIMIT 1000
in a loop), and finally add ALTER TABLE statements to set the NOT NULL
constraints and defaults on the updated_at column (the shareable_link_id can
remain nullable or be made NOT NULL after backfilling depending on
requirements). This eliminates the exclusive lock by avoiding volatile defaults
during the initial ADD COLUMN operation.
- Around line 69-78: Add CREATE INDEX statements for all foreign key columns to
improve query performance and prevent full table scans during constraint
operations and joins. After the foreign key constraint additions for
calendar_notes, chat_participants, locations, system_prompts, visualizations,
and messages tables, create indexes on the user_id, chat_id, and location_id
columns following a consistent naming convention such as
tablename_columnname_idx. Ensure each index statement uses CREATE INDEX IF NOT
EXISTS to avoid conflicts during re-runs of the migration.
- Line 78: The foreign key constraint "messages_location_id_locations_id_fk"
being added to the "messages" table will cause a full table scan during
deployment, potentially causing lock contention on large tables. Split this into
two steps: first, add the constraint with NOT VALID to skip the initial
validation scan, then in a separate subsequent migration statement, validate the
constraint using ALTER TABLE "messages" VALIDATE CONSTRAINT
"messages_location_id_locations_id_fk". This approach distributes the locking
impact across two separate operations.

In `@lib/db/index.ts`:
- Around line 10-15: The poolConfig initialization silently falls back to a
localhost connection string when DATABASE_URL is missing, which undermines the
production safety check. Remove the fallback from the connectionString property
assignment and instead use environment-dependent logic: in production
environments, throw an error or exit the process if DATABASE_URL is not defined,
while in development environments you can keep a fallback to localhost. This
ensures the application fails fast in production when critical configuration is
missing rather than attempting to connect to an unintended target.

In `@lib/utils/report-generator.ts`:
- Line 34: The report generator is using a fixed scale of 3 with PNG format
across the entire document (at lines 34, 52, 73, and 81), which causes excessive
memory consumption and canvas overflow for long reports. Instead of hardcoding
scale: 3 everywhere, implement adaptive scaling based on report content length
or page count, use a lower scale (e.g., 2) as default with PNG only for short
reports, and conditionally switch to JPEG format for longer documents to reduce
memory footprint. Additionally, add memory or dimension checks to prevent canvas
overflow and browser stalls when rendering large reports.

In `@tests/verify_report.spec.ts`:
- Around line 3-17: The test `verify report template rendering` is incomplete
and does not actually verify the report download behavior. After waiting for the
bot message with the waitForSelector for data-testid="bot-message", you need to
locate and interact with the download report button (likely DownloadReportButton
control), trigger the report generation, and add assertions to verify that the
report/PDF flow succeeds (such as checking for a download event, verifying a
dialog appears, or confirming the response contains expected report content).
This will ensure the report export path is properly tested and reduce flakiness
by avoiding dependency on live chat responses without validation of the actual
export functionality.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 88ba97b2-0c0e-4a53-b9e3-10a8fddcc2b3

📥 Commits

Reviewing files that changed from the base of the PR and between d41ece3 and dccf04b.

⛔ Files ignored due to path filters (3)
  • Dockerfile.db is excluded by !**/*.db
  • bun.lock is excluded by !**/*.lock
  • server.log is excluded by !**/*.log
📒 Files selected for processing (19)
  • .dockerignore
  • .env.local.example
  • CLOUD_DEPLOYMENT.md
  • Dockerfile
  • app/actions.tsx
  • components/download-report-button.tsx
  • components/map/map-3d.tsx
  • components/report-template.tsx
  • docker-compose.yaml
  • docker-entrypoint.sh
  • drizzle/migrations/0001_sync_schema_full.sql
  • drizzle/migrations/meta/0000_snapshot.json
  • drizzle/migrations/meta/0001_snapshot.json
  • drizzle/migrations/meta/_journal.json
  • lib/actions/chat.ts
  • lib/db/index.ts
  • lib/utils/report-generator.ts
  • package.json
  • tests/verify_report.spec.ts
💤 Files with no reviewable changes (1)
  • .dockerignore
📜 Review details
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
.env.local.example

[warning] 38-38: [UnorderedKey] The VERCEL_TEAM_ID key should go before the VERCEL_TOKEN key

(UnorderedKey)

🪛 markdownlint-cli2 (0.22.1)
CLOUD_DEPLOYMENT.md

[warning] 21-21: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 31-31: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 35-35: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 40-40: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 50-50: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 60-60: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 62-62: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 65-65: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 67-67: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 71-71: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 74-74: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🪛 SQLFluff (4.2.2)
drizzle/migrations/0001_sync_schema_full.sql

[error] 69-69: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 70-70: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 71-71: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 72-72: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 73-73: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 74-74: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 75-75: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 76-76: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 77-77: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 78-78: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)

🔇 Additional comments (12)
lib/actions/chat.ts (1)

22-84: LGTM!

app/actions.tsx (1)

17-17: LGTM!

Also applies to: 54-66

components/report-template.tsx (1)

16-17: LGTM!

Also applies to: 19-25, 55-59, 109-123, 196-213

package.json (1)

12-12: No action needed—Bun availability is confirmed across all CI/deployment paths.

The db:migrate script is properly configured for a Bun-only runtime. Bun is the declared package manager (v1.3.5 in package.json), all Docker images use the official oven/bun base, and docker-entrypoint.sh invokes migrations via bun run db:migrate. The cloud build toolchain includes Bun as a build tool.

Note: The EXECUTE_MIGRATIONS environment variable gate remains active in lib/db/migrate.ts (line 36) and docker-entrypoint.sh (line 5), and is still set in docker-compose.yaml (line 48). This is not a concern for the db:migrate script itself but may warrant documentation if this gate is being deprecated in favor of explicit script invocation.

components/map/map-3d.tsx (1)

88-88: Handle LatLng instances correctly when passed as center.

Line 88 assumes center.lat and center.lng are properties, but the type signature allows google.maps.LatLng objects where .lat() and .lng() are methods. If a LatLng instance is passed, the current code assigns function references instead of coordinate values. Additionally, the code should validate that lng exists (not just lat) and preserve any caller-provided altitude value rather than always overwriting it to 0.

💡 Suggested fix
-          center={center && typeof center === 'object' && 'lat' in center ? { lat: center.lat, lng: center.lng, altitude: 0 } : center}
+          center={
+            center && typeof center === 'object' && 'lat' in center && 'lng' in center
+              ? {
+                  lat: typeof (center as any).lat === 'function' ? (center as any).lat() : (center as any).lat,
+                  lng: typeof (center as any).lng === 'function' ? (center as any).lng() : (center as any).lng,
+                  altitude:
+                    typeof (center as any).altitude === 'number' ? (center as any).altitude : 0
+                }
+              : center
+          }
drizzle/migrations/meta/0000_snapshot.json (1)

2-177: LGTM!

drizzle/migrations/meta/0001_snapshot.json (1)

1-684: LGTM!

drizzle/migrations/meta/_journal.json (1)

11-18: LGTM!

Dockerfile (1)

58-60: No action needed; migration dependencies are complete.

Line 59's lib/db/migrate.ts contains no local imports—it only depends on external packages (drizzle-orm, pg, dotenv) and references ./drizzle/migrations, which is copied on line 58. All required files for the migration script are present in the runner image.

			> Likely an incorrect or invalid review comment.
CLOUD_DEPLOYMENT.md (3)

47-48: ⚡ Quick win

API keys list is vague; enumerate required credentials.

Line 47–48 mentions "other necessary API keys like Google, xAI, etc." without specifying what the actual application requires. Add a concrete list of required environment variables for completeness.

📋 Suggested improvements

Replace:

*   (Add other necessary API keys like Google, xAI, etc.)

With a concrete checklist (adjust based on your actual app):

*   `ANTHROPIC_API_KEY`: Anthropic API key (for AI features)
*   `NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN`: Mapbox public token (required, for map rendering)
*   `GOOGLE_API_KEY`: Google API key (if using Google geolocation or other services)
*   `XAI_API_KEY`: xAI API key (if using xAI models)
*   Any other service-specific keys your app requires

21-21: 💤 Low value

Fix markdown formatting: add blank lines around code fences.

Markdown linter (markdownlint-cli2) flags missing blank lines around fenced code blocks (MD031). Add a blank line before and after each triple-backtick block.

For example, line 21 should have a blank line before it:

3.  **Run the stack**:

    ```bash
    docker-compose up -d --build
    ```

Apply the same fix to lines 35, 62, 65, 67, 74.

Also applies to: 35-35, 62-62, 65-65, 67-67, 74-74


31-31: 💤 Low value

Fix markdown formatting: add blank lines around headings.

Markdown linter flags missing blank lines after headings (MD022). Add a blank line after each heading (###) and before the next content block.

For example, line 31 (heading ### PostgreSQL (Managed)) should have a blank line after it:

### PostgreSQL (Managed)

1.  Create a **New PostgreSQL** instance on Render.

Apply the same fix to lines 40, 50, 60, 71.

Also applies to: 40-40, 50-50, 60-60, 71-71

Comment thread .env.local.example
Comment on lines +35 to +38
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial | 💤 Low value

Reorder environment variables to alphabetical order.

The static analysis tool (dotenv-linter) reports that VERCEL_TEAM_ID (line 38) should appear before VERCEL_TOKEN (line 36) to maintain alphabetical/conventional ordering.

🔧 Proposed reordering
-# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
-VERCEL_TOKEN=your_vercel_token
-# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
-VERCEL_TEAM_ID=your_vercel_team_id
+# VERCEL_PROJECT_ID: Found in Vercel Dashboard > [Project] > Settings > General (starts with 'prj_')
+VERCEL_PROJECT_ID=your_vercel_project_id
+# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
+VERCEL_TEAM_ID=your_vercel_team_id
+# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
+VERCEL_TOKEN=your_vercel_token
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 38-38: [UnorderedKey] The VERCEL_TEAM_ID key should go before the VERCEL_TOKEN key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.local.example around lines 35 - 38, Reorder the environment variables
in the file to maintain alphabetical order as required by dotenv-linter. Move
the VERCEL_TEAM_ID variable block (including its comment lines) to appear before
the VERCEL_TOKEN variable block. This ensures that VERCEL_TEAM_ID comes before
VERCEL_TOKEN alphabetically, satisfying the linting rule for conventional
environment variable ordering.

Source: Linters/SAST tools

Comment thread .env.local.example
Comment on lines +35 to +39
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id
# VERCEL_PROJECT_ID: Found in Vercel Dashboard > [Project] > Settings > General (starts with 'prj_')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Missing VERCEL_PROJECT_ID environment variable assignment.

Line 39 shows the comment for VERCEL_PROJECT_ID but the actual environment variable assignment is missing. The upstream code in lib/agents/tools/index.tsx:40 conditionally registers the sandbox tool only when all three variables are set (VERCEL_TOKEN, VERCEL_TEAM_ID, and VERCEL_PROJECT_ID).

✅ Proposed fix to complete the configuration
 # VERCEL_PROJECT_ID: Found in Vercel Dashboard > [Project] > Settings > General (starts with 'prj_')
+VERCEL_PROJECT_ID=your_vercel_project_id
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id
# VERCEL_PROJECT_ID: Found in Vercel Dashboard > [Project] > Settings > General (starts with 'prj_')
# VERCEL_TOKEN: Create a Personal Access Token in Vercel Dashboard > Settings > Tokens
VERCEL_TOKEN=your_vercel_token
# VERCEL_TEAM_ID: Found in Vercel Dashboard > Team Settings > General (starts with 'team_')
VERCEL_TEAM_ID=your_vercel_team_id
# VERCEL_PROJECT_ID: Found in Vercel Dashboard > [Project] > Settings > General (starts with 'prj_')
VERCEL_PROJECT_ID=your_vercel_project_id
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 38-38: [UnorderedKey] The VERCEL_TEAM_ID key should go before the VERCEL_TOKEN key

(UnorderedKey)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.local.example around lines 35 - 39, The VERCEL_PROJECT_ID environment
variable assignment is missing from the configuration file. After the comment
describing VERCEL_PROJECT_ID on line 39, add the actual environment variable
assignment following the same pattern as VERCEL_TOKEN and VERCEL_TEAM_ID above
it. The assignment should include the variable name VERCEL_PROJECT_ID followed
by an equals sign and a placeholder value like your_vercel_project_id to match
the format and documentation of the other two Vercel environment variables in
the file.

Comment thread CLOUD_DEPLOYMENT.md
Comment on lines +14 to +23
## 1. Deploying with Docker Compose (VPS / EC2 / Compute Engine)

The easiest way to deploy the entire stack is using `docker-compose`.

1. **Clone the repository** on your server.
2. **Create a `.env` file** based on the environment variables in `docker-compose.yaml`.
3. **Run the stack**:
```bash
docker-compose up -d --build
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add production persistence guidance for docker-compose.

The Docker Compose section omits important production considerations. Add notes on volume persistence and backup strategy:

💾 Suggested additions

Add after line 22:

4.  **Verify persistence** (important for production):
    - Ensure `postgres_data` and `qdrant_data` volumes are properly mounted on the host.
    - Implement regular backups of these volumes.
    - Consider external managed databases (RDS, Cloud SQL, Neon) for production workloads.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 21-21: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLOUD_DEPLOYMENT.md` around lines 14 - 23, The Docker Compose deployment
section in CLOUD_DEPLOYMENT.md is missing critical production considerations for
data persistence. Add a new step 4 after the "Run the stack" command that
addresses volume persistence by ensuring postgres_data and qdrant_data volumes
are properly mounted on the host, provides guidance on implementing regular
backups of these volumes, and recommends considering external managed databases
(RDS, Cloud SQL, Neon) as alternatives for production workloads to ensure data
durability and reliability.

Comment thread CLOUD_DEPLOYMENT.md
### Qdrant (Optional)
1. Create a **New Private Service** or **Web Service**.
2. Select **Docker** as the runtime.
3. Use the image: `qdrant/qdrant:latest`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Pin Qdrant image version for production.

Line 53 uses qdrant/qdrant:latest, which is a production anti-pattern. Latest tags can change unexpectedly, breaking deployments. Pin to a specific version (e.g., qdrant/qdrant:v1.13.0).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLOUD_DEPLOYMENT.md` at line 53, The Qdrant Docker image specification uses
the `latest` tag which can change unexpectedly and cause production deployments
to break. Replace the image reference `qdrant/qdrant:latest` with a specific
pinned version like `qdrant/qdrant:v1.13.0` to ensure consistent and
reproducible deployments.

Comment thread CLOUD_DEPLOYMENT.md
Comment on lines +68 to +69
gcloud run deploy qcx --image gcr.io/YOUR_PROJECT/qcx --platform managed
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚖️ Poor tradeoff

Cloud Run deployment command is incomplete for production.

Line 68's gcloud run deploy command is missing critical flags required for a functional deployment:

  • --region: Specifies the Cloud Run region
  • --allow-unauthenticated: Required if the service should be public
  • --set-env-vars: Must pass DATABASE_URL, QDRANT_URL, NODE_ENV, and API keys

Additionally, Cloud Run is serverless and ephemeral—a direct DATABASE_URL connection without connection pooling (PgBouncer, Supavisor, or Cloud SQL Proxy) will exhaust connection limits. Add guidance to use Cloud SQL Proxy or a dedicated connection pool.

📋 Suggested improvements to Cloud Run deployment section
2.  **Deploy to Cloud Run**:
    ```bash
    gcloud run deploy qcx \
      --image gcr.io/YOUR_PROJECT/qcx \
      --platform managed \
      --region us-central1 \
      --allow-unauthenticated \
      --set-env-vars=DATABASE_URL=<YOUR_DATABASE_URL>,QDRANT_URL=<YOUR_QDRANT_URL>,NODE_ENV=production \
      --memory 1Gi \
      --cpu 1 \
      --timeout 3600
    ```

3.  **Set up Cloud SQL Proxy** (critical for connection pooling):
    - Cloud Run cannot maintain long-lived connections to Cloud SQL.
    - Use Cloud SQL Proxy or Supavisor to pool connections.
    - Example: Use `cloud-sql-proxy` sidecar or configure DATABASE_URL with `?sslmode=require` and proper connection string routing.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CLOUD_DEPLOYMENT.md` around lines 68 - 69, The gcloud run deploy qcx command
is incomplete for production deployment. Update the command to include all
required flags: add --region to specify the Cloud Run region, add
--allow-unauthenticated if the service should be public, and add --set-env-vars
to pass critical environment variables like DATABASE_URL, QDRANT_URL, NODE_ENV,
and API keys. Additionally, add a new section after the Cloud Run deployment
command that explains the connection pooling requirement for Cloud Run's
ephemeral nature, recommending the use of Cloud SQL Proxy or Supavisor to handle
database connections, since direct DATABASE_URL connections will exhaust
connection limits in a serverless environment.

Comment on lines +69 to +78
ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "calendar_notes" ADD CONSTRAINT "calendar_notes_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "chat_participants" ADD CONSTRAINT "chat_participants_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "locations" ADD CONSTRAINT "locations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "locations" ADD CONSTRAINT "locations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "system_prompts" ADD CONSTRAINT "system_prompts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint

Copy link
Copy Markdown
Contributor

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

🏁 Script executed:

# Find the migration file
find . -name "0001_sync_schema_full.sql" -type f

Repository: QueueLab/QCX

Length of output: 103


🏁 Script executed:

# Check the line count of the file
wc -l ./drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 106


🏁 Script executed:

# Read lines 69-78 to verify the content
sed -n '69,78p' ./drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 2073


🏁 Script executed:

# Search for existing indexes on the FK columns mentioned
rg "CREATE INDEX.*(?:calendar_notes_user_id|calendar_notes_chat_id|chat_participants_chat_id|chat_participants_user_id|locations_user_id|locations_chat_id|system_prompts_user_id|visualizations_user_id|visualizations_chat_id|messages_location_id)" --type sql

Repository: QueueLab/QCX

Length of output: 38


🏁 Script executed:

# Search for any indexes on the tables/columns mentioned
rg "CREATE INDEX" ./drizzle/migrations/ --type sql

Repository: QueueLab/QCX

Length of output: 38


🏁 Script executed:

# Read the entire migration file to see the full structure
cat -n ./drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 5900


🏁 Script executed:

# Check if there are other migration files
ls -la ./drizzle/migrations/

Repository: QueueLab/QCX

Length of output: 451


🏁 Script executed:

# Check the other migration files for any index definitions
cat ./drizzle/migrations/0000_sweet_metal_master.sql

Repository: QueueLab/QCX

Length of output: 1499


🏁 Script executed:

# Check the other migration file
cat ./drizzle/migrations/0001_add_calendar_notes.sql

Repository: QueueLab/QCX

Length of output: 1586


🏁 Script executed:

# Search for any schema files or indexes defined elsewhere
find . -name "*.ts" -o -name "*.js" | xargs grep -l "index\|Index" | head -20

Repository: QueueLab/QCX

Length of output: 130


🏁 Script executed:

# Check the schema file
cat ./lib/schema/sandbox.ts

Repository: QueueLab/QCX

Length of output: 1007


🏁 Script executed:

# Search for drizzle schema configuration
find . -path ./node_modules -prune -o -name "schema.*" -type f -print | grep -E "\.(ts|js|sql)$"

Repository: QueueLab/QCX

Length of output: 75


🏁 Script executed:

# Check the database schema file
cat ./lib/db/schema.ts

Repository: QueueLab/QCX

Length of output: 6939


🏁 Script executed:

# Search for any index definitions in the Drizzle schema that might have been missed
rg "index\|Index" ./lib/db/schema.ts

Repository: QueueLab/QCX

Length of output: 38


Add indexes for foreign key columns to prevent performance degradation during constraint operations and joins.

Foreign key constraints are defined (lines 69-78), but supporting indexes are missing on the referenced columns (*_user_id, *_chat_id, messages.location_id). Without these indexes, FK constraint checks during parent deletes/updates and common joins on these columns will cause full table scans as data grows.

Suggested index additions
CREATE INDEX IF NOT EXISTS "calendar_notes_user_id_idx" ON "calendar_notes" ("user_id");
CREATE INDEX IF NOT EXISTS "calendar_notes_chat_id_idx" ON "calendar_notes" ("chat_id");
CREATE INDEX IF NOT EXISTS "chat_participants_chat_id_idx" ON "chat_participants" ("chat_id");
CREATE INDEX IF NOT EXISTS "chat_participants_user_id_idx" ON "chat_participants" ("user_id");
CREATE INDEX IF NOT EXISTS "locations_user_id_idx" ON "locations" ("user_id");
CREATE INDEX IF NOT EXISTS "locations_chat_id_idx" ON "locations" ("chat_id");
CREATE INDEX IF NOT EXISTS "system_prompts_user_id_idx" ON "system_prompts" ("user_id");
CREATE INDEX IF NOT EXISTS "visualizations_user_id_idx" ON "visualizations" ("user_id");
CREATE INDEX IF NOT EXISTS "visualizations_chat_id_idx" ON "visualizations" ("chat_id");
CREATE INDEX IF NOT EXISTS "messages_location_id_idx" ON "messages" ("location_id");
🧰 Tools
🪛 SQLFluff (4.2.2)

[error] 69-69: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 70-70: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 71-71: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 72-72: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 73-73: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 74-74: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 75-75: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 76-76: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 77-77: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)


[error] 78-78: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@drizzle/migrations/0001_sync_schema_full.sql` around lines 69 - 78, Add
CREATE INDEX statements for all foreign key columns to improve query performance
and prevent full table scans during constraint operations and joins. After the
foreign key constraint additions for calendar_notes, chat_participants,
locations, system_prompts, visualizations, and messages tables, create indexes
on the user_id, chat_id, and location_id columns following a consistent naming
convention such as tablename_columnname_idx. Ensure each index statement uses
CREATE INDEX IF NOT EXISTS to avoid conflicts during re-runs of the migration.

ALTER TABLE "system_prompts" ADD CONSTRAINT "system_prompts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "visualizations" ADD CONSTRAINT "visualizations_chat_id_chats_id_fk" FOREIGN KEY ("chat_id") REFERENCES "public"."chats"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint

Copy link
Copy Markdown
Contributor

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

🏁 Script executed:

fd -t f "0001_sync_schema_full.sql"

Repository: QueueLab/QCX

Length of output: 101


🏁 Script executed:

sed -n '75,80p' drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 1063


🏁 Script executed:

cat -n drizzle/migrations/0001_sync_schema_full.sql | grep -A 5 -B 5 "messages_location_id_locations_id_fk"

Repository: QueueLab/QCX

Length of output: 1503


🏁 Script executed:

wc -l drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 104


🌐 Web query:

PostgreSQL NOT VALID foreign key constraint validation

💡 Result:

In PostgreSQL, you add a foreign key constraint with NOT VALID using ALTER TABLE... ADD CONSTRAINT... NOT VALID, and then later check existing rows with ALTER TABLE... VALIDATE CONSTRAINT. 1) Add the FK as NOT VALID (skip the initial scan) Example: ALTER TABLE referencing_table ADD CONSTRAINT fk_name FOREIGN KEY (referencing_cols) REFERENCES referenced_table(referenced_cols) NOT VALID; With NOT VALID, the potentially lengthy scan of existing rows is skipped at add time, and the constraint is still enforced for subsequent INSERT/UPDATE operations on the referencing table. [1][2] 2) Validate existing rows when ready Later run: ALTER TABLE referencing_table VALIDATE CONSTRAINT fk_name; This validates a foreign key (or check / not-null constraint) previously created as NOT VALID by scanning the referencing table to ensure there are no rows that violate the constraint. [1] 3) Locking / operational impact - The motivation for NOT VALID is reducing the impact on concurrent updates: the ADD CONSTRAINT... NOT VALID command can be committed immediately without scanning the whole table. [1] - During VALIDATE CONSTRAINT, validation acquires only a SHARE UPDATE EXCLUSIVE lock on the referencing table (and for foreign keys, also requires a ROW SHARE lock on the referenced table). [1] 4) Notes about “NOT VALID foreign key constraint validation” - VALIDATE CONSTRAINT only has work to do if the constraint is currently marked NOT VALID (i.e., convalidated=false in pg_constraint). [3][4] - Once the constraint is in place, new violations cannot be inserted; only the pre-existing rows are checked later when you run VALIDATE CONSTRAINT. [1] If you share your exact CREATE/ALTER statements and PostgreSQL version, I can tailor the exact SQL (including constraint name) for your case.

Citations:


🏁 Script executed:

grep -n "CREATE TABLE.*messages" drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 38


🏁 Script executed:

fd -t f "*.sql" drizzle/migrations/ | head -20

Repository: QueueLab/QCX

Length of output: 341


🏁 Script executed:

fd -t f ".sql" drizzle/migrations/ | head -20

Repository: QueueLab/QCX

Length of output: 195


🏁 Script executed:

cat drizzle/migrations/0000_sweet_metal_master.sql | head -100

Repository: QueueLab/QCX

Length of output: 1499


🏁 Script executed:

grep -n "location_id" drizzle/migrations/0001_add_calendar_notes.sql

Repository: QueueLab/QCX

Length of output: 38


🏁 Script executed:

cat drizzle/migrations/0001_add_calendar_notes.sql

Repository: QueueLab/QCX

Length of output: 1586


🏁 Script executed:

cat drizzle/migrations/0001_sync_schema_full.sql

Repository: QueueLab/QCX

Length of output: 5340


Stage FK validation on messages.location_id to reduce migration impact.

Adding the constraint without NOT VALID will scan the entire messages table during deployment. On a large table, this can cause lock contention and delay migrations. Add the FK with NOT VALID to skip the initial scan, then validate in a separate step to distribute the lock impact.

Safer FK rollout
-ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
+ALTER TABLE "messages"
+  ADD CONSTRAINT "messages_location_id_locations_id_fk"
+  FOREIGN KEY ("location_id")
+  REFERENCES "public"."locations"("id")
+  ON DELETE set null ON UPDATE no action
+  NOT VALID;--> statement-breakpoint
+ALTER TABLE "messages"
+  VALIDATE CONSTRAINT "messages_location_id_locations_id_fk";--> statement-breakpoint
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ALTER TABLE "messages" ADD CONSTRAINT "messages_location_id_locations_id_fk" FOREIGN KEY ("location_id") REFERENCES "public"."locations"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
ALTER TABLE "messages"
ADD CONSTRAINT "messages_location_id_locations_id_fk"
FOREIGN KEY ("location_id")
REFERENCES "public"."locations"("id")
ON DELETE set null ON UPDATE no action
NOT VALID;--> statement-breakpoint
ALTER TABLE "messages"
VALIDATE CONSTRAINT "messages_location_id_locations_id_fk";--> statement-breakpoint
🧰 Tools
🪛 SQLFluff (4.2.2)

[error] 78-78: ADD CONSTRAINT ... FOREIGN KEY should use NOT VALID to avoid locking the table while validating existing rows.

(PG01)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@drizzle/migrations/0001_sync_schema_full.sql` at line 78, The foreign key
constraint "messages_location_id_locations_id_fk" being added to the "messages"
table will cause a full table scan during deployment, potentially causing lock
contention on large tables. Split this into two steps: first, add the constraint
with NOT VALID to skip the initial validation scan, then in a separate
subsequent migration statement, validate the constraint using ALTER TABLE
"messages" VALIDATE CONSTRAINT "messages_location_id_locations_id_fk". This
approach distributes the locking impact across two separate operations.

Source: Linters/SAST tools

Comment thread lib/db/index.ts
Comment on lines +10 to +15
if (!process.env.DATABASE_URL && process.env.NODE_ENV === 'production') {
console.warn('DATABASE_URL environment variable is not set. Database features will be unavailable.');
}

const poolConfig: PoolConfig = {
connectionString: process.env.DATABASE_URL,
connectionString: process.env.DATABASE_URL || 'postgres://localhost:5432/postgres',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove localhost fallback for production DB configuration.

Line 15 silently falls back to postgres://localhost:5432/postgres. In production, a missing DATABASE_URL should fail fast, not connect to a fallback target.

Safer config split by environment
-if (!process.env.DATABASE_URL && process.env.NODE_ENV === 'production') {
-  console.warn('DATABASE_URL environment variable is not set. Database features will be unavailable.');
-}
+if (!process.env.DATABASE_URL && process.env.NODE_ENV === 'production') {
+  throw new Error('DATABASE_URL environment variable must be set in production.');
+}

 const poolConfig: PoolConfig = {
-  connectionString: process.env.DATABASE_URL || 'postgres://localhost:5432/postgres',
+  connectionString:
+    process.env.DATABASE_URL ??
+    (process.env.NODE_ENV === 'development'
+      ? 'postgres://localhost:5432/postgres'
+      : undefined),
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!process.env.DATABASE_URL && process.env.NODE_ENV === 'production') {
console.warn('DATABASE_URL environment variable is not set. Database features will be unavailable.');
}
const poolConfig: PoolConfig = {
connectionString: process.env.DATABASE_URL,
connectionString: process.env.DATABASE_URL || 'postgres://localhost:5432/postgres',
if (!process.env.DATABASE_URL && process.env.NODE_ENV === 'production') {
throw new Error('DATABASE_URL environment variable must be set in production.');
}
const poolConfig: PoolConfig = {
connectionString:
process.env.DATABASE_URL ??
(process.env.NODE_ENV === 'development'
? 'postgres://localhost:5432/postgres'
: undefined),
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/db/index.ts` around lines 10 - 15, The poolConfig initialization silently
falls back to a localhost connection string when DATABASE_URL is missing, which
undermines the production safety check. Remove the fallback from the
connectionString property assignment and instead use environment-dependent
logic: in production environments, throw an error or exit the process if
DATABASE_URL is not defined, while in development environments you can keep a
fallback to localhost. This ensures the application fails fast in production
when critical configuration is missing rather than attempting to connect to an
unintended target.


const canvas = await html2canvas(element, {
scale: 2, // Increase scale for higher DPI/sharpness
scale: 3, // Increased scale for ultra-sharp text and images

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's find and read the report-generator.ts file
find . -name "report-generator.ts" -type f

Repository: QueueLab/QCX

Length of output: 88


🏁 Script executed:

# List the repo structure to locate the file
git ls-files | grep -E "(report|generator)" | head -20

Repository: QueueLab/QCX

Length of output: 183


🏁 Script executed:

# Read the report-generator.ts file to see the actual implementation
wc -l lib/utils/report-generator.ts

Repository: QueueLab/QCX

Length of output: 89


🏁 Script executed:

# Read the full file
cat -n lib/utils/report-generator.ts

Repository: QueueLab/QCX

Length of output: 3849


Avoid unbounded high-res PNG rendering for long reports.

Lines 34, 52, 73, and 81 force scale: 3 and PNG format for the entire document without adaptive scaling or memory limits. With a fixed 800px width, the canvas becomes 2400px wide; adding PNG's lossless encoding and long template content can cause significant memory bloat, canvas overflows, and browser stalls.

💡 Suggested fix
+    const rect = element.getBoundingClientRect()
+    const MAX_PIXELS = 24_000_000
+    const requestedScale = 3
+    const safeScale = Math.min(
+      requestedScale,
+      Math.sqrt(MAX_PIXELS / Math.max(rect.width * rect.height, 1))
+    )
+
     const canvas = await html2canvas(element, {
-      scale: 3, // Increased scale for ultra-sharp text and images
+      scale: safeScale,
       useCORS: true,
       logging: false,
       allowTaint: true,
       backgroundColor: '`#ffffff`',
       imageTimeout: 15000,
       onclone: (clonedDoc) => {
         // Ensure the cloned element is visible for html2canvas
         const clonedElement = clonedDoc.getElementById(elementId)
         if (clonedElement) {
           clonedElement.style.position = 'static'
           clonedElement.style.left = '0'
           clonedElement.style.visibility = 'visible'
           clonedElement.style.width = '800px' // Fix width for consistent scaling
         }
       }
     })

+    const usePng = safeScale >= 2.5
-    const imgData = canvas.toDataURL('image/png') // Use PNG for lossless quality and sharper text
+    const imgData = usePng
+      ? canvas.toDataURL('image/png')
+      : canvas.toDataURL('image/jpeg', 0.92)

     // A4 dimensions in px at 72 DPI are roughly 595 x 842
     // But we use the internal pageSize values for flexibility
     const pdf = new jsPDF({
       orientation: 'portrait',
       unit: 'px',
       format: 'a4'
     })

     const pdfWidth = pdf.internal.pageSize.getWidth()
     const pdfHeight = pdf.internal.pageSize.getHeight()

     const imgProps = pdf.getImageProperties(imgData)
     const ratio = imgProps.height / imgProps.width
     const scaledHeight = pdfWidth * ratio

     let heightLeft = scaledHeight
     let position = 0

     // Add first page
-    pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, scaledHeight, undefined, 'FAST')
+    pdf.addImage(imgData, usePng ? 'PNG' : 'JPEG', 0, position, pdfWidth, scaledHeight, undefined, 'FAST')
     heightLeft -= pdfHeight

     // Add subsequent pages if content overflows
     while (heightLeft > 0) {
       position = heightLeft - scaledHeight
       pdf.addPage()
-      pdf.addImage(imgData, 'PNG', 0, position, pdfWidth, scaledHeight, undefined, 'FAST')
+      pdf.addImage(imgData, usePng ? 'PNG' : 'JPEG', 0, position, pdfWidth, scaledHeight, undefined, 'FAST')
       heightLeft -= pdfHeight
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/utils/report-generator.ts` at line 34, The report generator is using a
fixed scale of 3 with PNG format across the entire document (at lines 34, 52,
73, and 81), which causes excessive memory consumption and canvas overflow for
long reports. Instead of hardcoding scale: 3 everywhere, implement adaptive
scaling based on report content length or page count, use a lower scale (e.g.,
2) as default with PNG only for short reports, and conditionally switch to JPEG
format for longer documents to reduce memory footprint. Additionally, add memory
or dimension checks to prevent canvas overflow and browser stalls when rendering
large reports.

Comment on lines +3 to +17
test('verify report template rendering', async ({ page }) => {
// Mocking AI state/messages for report generation
await page.goto('/');

// We need to simulate a chat to have messages to export
await page.fill('[data-testid="chat-input"]', 'Analyze the Eiffel Tower');
await page.click('[data-testid="chat-submit"]');

// Wait for some response to appear
await page.waitForSelector('[data-testid="bot-message"]', { timeout: 30000 });

// Now try to trigger the download button which should be in the header or settings
// The DownloadReportButton is used in the SettingsView or Header?
// Let's check where it is used.
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

The spec does not currently verify report rendering/download behavior.

Despite the test name, it stops after waiting for a bot message. There’s no interaction with the download control and no assertion that a report template/PDF flow succeeds. This leaves the new report export path effectively untested and likely flaky due to live-response dependency.

💡 Suggested direction
 test('verify report template rendering', async ({ page }) => {
@@
-  // Now try to trigger the download button which should be in the header or settings
-  // The DownloadReportButton is used in the SettingsView or Header?
-  // Let's check where it is used.
+  const downloadButton = page.getByTitle('Download PDF Report');
+  await expect(downloadButton).toBeVisible();
+
+  const [download] = await Promise.all([
+    page.waitForEvent('download'),
+    downloadButton.click()
+  ]);
+
+  await expect(download.suggestedFilename()).resolves.toMatch(/_report\.pdf$/i);
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/verify_report.spec.ts` around lines 3 - 17, The test `verify report
template rendering` is incomplete and does not actually verify the report
download behavior. After waiting for the bot message with the waitForSelector
for data-testid="bot-message", you need to locate and interact with the download
report button (likely DownloadReportButton control), trigger the report
generation, and add assertions to verify that the report/PDF flow succeeds (such
as checking for a download event, verifying a dialog appears, or confirming the
response contains expected report content). This will ensure the report export
path is properly tested and reduce flakiness by avoiding dependency on live chat
responses without validation of the actual export functionality.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants