Skip to content

Conversation

afterrburn
Copy link
Contributor

@afterrburn afterrburn commented Sep 17, 2025

Summary by CodeRabbit

  • New Features
    • Full chat experience: sessions, live streaming assistant replies, tutorial-aware responses, sidebar, input, message list, code viewer/editor, and MDX code-embedding support.
  • APIs
    • Tutorial and tutorial-step endpoints (with extracted snippets), session CRUD and messaging (streaming), and per-user tutorial state routes.
  • Agents
    • New Pulse conversational agent with tutorial actions and tooling.
  • Persistence
    • KV-backed sessions/tutorial progress and persistent per-user cookie.
  • Documentation
    • Expanded overview and tutorials authoring guide; new agent README.
  • Bug Fixes
    • Removed noisy console logging.
  • Chores
    • Env/.gitignore updates, config tweaks, and dependency upgrades.
  • Style
    • New dark theme CSS and UI polish.

afterrburn and others added 2 commits September 17, 2025 08:12
* add totalChunks to metadata for tracing

* improve RAG retrieval process

* POC UI for chat based documentation

* update Start / Continue course

* expand text

* fix scrollbar problem and chat input resizing

* adding progress tracker

* center the progress bar

* testing out new terminal component and websocket servert

* fix terminal issue not staying on

* fix weird terminal display

* fix self is not defined error

* remove unnecessary terminal message

* typo

* fix weird flow

* remove duplicated butotn

* playing with coding web server

* remove websocket server

* creating api for tutorials

* fix interface

* modify tutorials workflow -- vibecoded

* dummy demo code execution api next.js

* New pulse agent using response api tools calling

* re-build the entire Pulse agent with new design

* adding tutorial step workflow

* simplify tutorial reader to have consistent api

* cleaning up some more steps

* breaking frontend to smaller components;

* link doc-qa to pulse agent

* removing unused import

* fix chat input box and have split pane for code editor

* enhancing file display

* simplify chat interface -- removing unnecessary code block displays

* add editor close button

* make side bar icons smaller

* implement chunk streaming structure

* clean up some items

* Revert "Implement Copy Page Dropdown Functionality (#239)"

This reverts commit 5eb9f16.

* fix tutorial step data handling issue

* add kv store api service

* remove unused interfaces

* remove unneeded conversation type

* reformat chat history

* add kv store api

* Simplify and refactor chat to connect with kv store

* add uuid package

* update example env

* share session context

* removing debug

* Adding session cache with SWR

* add .env to gitignore

* sync with main

* adjust chat message area width and dynamic spacing with sessionsbar

* add code editor content

* remove redundant comments

* display tutorial instruction content

* add user based session management

* enable split pane resize

* adding sessions cursor

* sessions paginated loading

* clean up env variables

* enabling direct llm access flag

* add title generation

* remove session update redundancy

* render session messages directly

* fix streaming bug on UI

* merge conflict resolution

* remove tutorial agent set up that is not currently needed

* remove package json

* rebuilt package json and remove /api/chat and /api/terminal that were mock/test

* delete dummy terminal websocket server

* Add tutorial structure rules and enhance tutorial API responses

- Introduced a new markdown file defining the structure and authoring guidelines for tutorials.
- Updated the tutorial API to return detailed step data, including snippets and metadata.
- Refactored tutorial step fetching logic to improve error handling and data retrieval.
- Implemented a new `<CodeFromFiles />` component for rendering code snippets from files.
- Enhanced chat message rendering to support tutorial content and snippets.

* chore(lockfile): sync package-lock with package.json to fix npm ci (add data-uri-to-buffer@2.0.2)

* sync package

* fix build error

* synchronize name of totalSteps

* fix linter failure

* cleaning up debug log and unused modules

* remove debug log from ChatMessage

* remove dummy tutorial content

* simplify code pieces

* add total steps

* remove unused components

* removing unused module

* Remove integration md

* replace div with interactable button

* remove unused import

* toIsoString formatting

* gracefully handle setKVValue error

* improve tool param wording

* remove unused websocket server

* add user tutorial status

* add tutorial state management

* refactor tutorial state route handlers to improve JSON body parsing and error handling

* update ChatMessage component to format code snippets with labels above code fences for improved readability

* remove python tutorial mdx

* Fix CodeRabbit issues: implement validation middleware and improve error handling (#283)

* Fix CodeRabbit issues: implement validation middleware, fix config imports, handle KV errors

- Add comprehensive body validation middleware for /sessions, /tutorials, /users endpoints
- Fix config import issues by moving to static imports at top of files
- Add proper KV persistence error handling with success checks
- Validate tutorialId as string and prevent path traversal attacks
- Fix implicit any types on request body parameters
- Replace parseInt with Number.parseInt for consistency
- Add proper 400 error responses with detailed validation messages
- Use existing types from app/chat/types.ts for validation
- Prevent TypeError when no progress exists by handling 404 responses gracefully

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* Fix TypeScript compilation errors in validation middleware

- Add SessionMessageValidationResult and SessionMessageOnlyValidationResult types
- Fix validation function return type mismatches in session routes
- Add proper bounds checking for stepIndex in tutorial route
- Ensure all validation errors use consistent error structure
- Generate missing docs.json file to resolve import errors

All TypeScript compilation errors resolved, ready for CI

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* Refactor validation middleware to be generic and scalable

- Add FieldSchema and ValidationSchema interfaces for declarative validation
- Implement validateField and validateObject for schema-based validation
- Add overloaded parseAndValidateJSON to accept both validators and schemas
- Maintain backward compatibility with existing validation functions
- Fix TypeScript compilation errors with explicit Message type annotations
- Enable reusable validation for current and future types

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* Refactor validation to use Zod schemas and eliminate duplicate source of truth

- Replace TypeScript interfaces with Zod schemas in app/chat/types.ts
- Derive types using z.infer<typeof Schema> instead of separate interfaces
- Update validation middleware to use Zod's safeParse and error handling
- Maintain all existing validation behavior while using industry-standard Zod
- Fix TypeScript compilation errors and import issues
- All API endpoints now use consistent Zod-based validation

This eliminates the duplicate source of truth between validation schemas and TypeScript interfaces, making the codebase more maintainable and following modern best practices.

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* Complete Zod migration for messages API endpoint

- Replace custom validation logic with SessionMessageRequestSchema
- Simplify validation code by using Zod's built-in validation
- Maintain all existing functionality while using industry-standard validation

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* Complete Zod migration: remove redundant interfaces and convert utility functions

- Remove unused SessionMessageValidationResult and SessionMessageOnlyValidationResult interfaces
- Convert validateStepNumber and validateTutorialId to use Zod schemas internally
- Add StepNumberSchema and TutorialIdSchema for consistent validation
- Maintain backward compatibility with existing function signatures
- Complete elimination of duplicate source of truth between validation and types
- All validation now uses Zod schemas as single source of truth

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>

* delete lib/validation/types.ts unused module

* defensively check tutorials state

* update tools description and enhance the path checking

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: srith@agentuity.com <rithsenghorn@gmail.com>
Co-authored-by: afterrburn <sun_rsh@outlook.com>

* Apply suggestion from @coderabbitai[bot]

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Seng Rith <50646727+afterrburn@users.noreply.github.com>

* fix typo

* clean up

* small fixes

* revert css

* remove tutorial

* remove Tutorial page

* remove outdated readme

* remove unnecessary dependencies

* remove debug logging

* example of how tutorial is structured

* Revert "example of how tutorial is structured"

This reverts commit 6d70c4e.

* move helper out of the POST body

---------

Signed-off-by: Seng Rith <50646727+afterrburn@users.noreply.github.com>
Co-authored-by: afterrburn <sun_rsh@outlook.com>
Co-authored-by: Seng Rith <50646727+senghorn@users.noreply.github.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Walkthrough

Adds a Pulse conversational agent, tutorial content readers and APIs, session/user session management with SSE streaming, a CodeFromFiles MDX component, chat UI components/hooks/services, KV-backed state helpers, validation middleware, config/env changes, and theme CSS.

Changes

Cohort / File(s) Summary of changes
Docs & authoring
\.cursor/rules/overview.mdc, \.cursor/rules/tutorials-structure.mdc, agent-docs/README.md
Expanded overview and new tutorials-structure doc; added dev pre-step for Bun and local run instructions.
Agent registry & docs
agent-docs/agentuity.yaml, agent-docs/package.json, agent-docs/RAG-*.md
Added agent-pulse agent entry; minor package.json EOF formatting; removed several RAG design/user-story docs.
Env & gitignore
.env.example, .gitignore
.env.example removes AGENT_ID/AGENT_FULL_URL and adds AGENTUITY_API_KEY; .env added to .gitignore.
App config & env helpers
lib/config.ts, lib/env.ts
New central config constant and agent-specific getters (getAgentQaConfig/getAgentPulseConfig); removed old AGENT_ID/AGENT_FULL_URL flow.
KV client
lib/kv-store.ts
New HTTP KV client with get/set/delete/search and AGENTUITY_API_KEY auth wrapper and typed responses.
Pulse agent core
agent-docs/src/agents/agent-pulse/*
New Pulse agent modules: system prompt builder, parser/types, state, state manager, tools, tutorial client, streaming processor/types, and main streaming endpoint.
Doc-QA & RAG tweaks
agent-docs/src/agents/doc-qa/*
Removed stray console.log and adjusted RAG system prompt fallback/toning rules.
CodeFromFiles MDX
components/CodeFromFiles.tsx, app/(docs)/[[...slug]]/page.tsx
New server-side CodeFromFiles component (repo-relative snippet tabs) and MDX registration.
Tutorial readers & APIs
lib/tutorial/*, app/api/tutorials/*.ts
Filesystem tutorial readers, types, barrel export, and API routes to list tutorials and fetch step content/snippets.
Tutorial state & user API
lib/tutorial/state-manager.ts, lib/tutorial/types.ts, app/api/users/tutorial-state/route.ts
KV-backed per-user tutorial progress manager and GET/POST/DELETE tutorial-state route.
Sessions & streaming
app/api/sessions/*.ts, app/api/sessions/[sessionId]/messages/route.ts, app/api/rag-search/route.ts
Session CRUD endpoints, streaming POST that proxies agent SSE, persists messages, updates tutorial progress; rag-search now uses QA agent config.
Validation & middleware
lib/validation/middleware.ts, middleware.ts
Zod-based parse/validate helpers/schemas and middleware that sets persistent chat_user_id cookie.
Chat UI, services & types
app/chat/**/*, components/ui/skeleton.tsx, app/global.css
New chat layout/context/pages, message UI, input, sidebar, code block/editor, markdown renderer, hooks (useAutoResize/useStreaming), sessionService SSE client, Zod runtime types, skeleton UI, and CSS theme (duplicate block present).
Streaming plumbing
agent-docs/src/agents/agent-pulse/streaming/*, app/chat/utils/useStreaming.ts, app/chat/services/sessionService.ts
Streaming chunk types, server-side streaming processor emitting SSE-like events, and client-side streaming handling with incremental UI updates and tutorial-data integration.
Misc utilities & readers
lib/tutorial/*, lib/validation/*
Tutorial discovery/readers, validation middleware and helpers, and supporting types.
Deps & package manifest
package.json
Added/updated dependencies: swr, uuid, zod, allotment, @types/uuid, node-pty, next/react upgrades; removed cmdk.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant B as Browser
  participant API as Next API (/api/sessions/:id/messages)
  participant AG as Agent (Pulse)
  participant TOOLS as Agent Tools (DocQA/Tutorial)
  participant TAPI as Tutorial API (/api/tutorials)
  participant KV as KV Store

  B->>API: POST user message (processWithAgent=true)
  API->>KV: load session (user_session key)
  API->>AG: open SSE with message + short history + tutorialState
  AG->>TOOLS: tool call (e.g., startTutorialById)
  TOOLS->>TAPI: fetch tutorial meta/step
  TAPI-->>TOOLS: return step (mdx + snippets)
  TOOLS-->>AG: tool result
  AG-->>API: stream events (text-delta / status / tutorial-data / finish)
  API->>B: proxy SSE events
  API->>KV: persist assistant message & tutorial progress (on finish)
Loading
sequenceDiagram
  autonumber
  participant Docs as MDX page
  participant CF as CodeFromFiles (server)
  participant FS as Repo filesystem

  Docs->>CF: render CodeFromFiles(snippets)
  CF->>FS: read repo-root files (/...)
  FS-->>CF: file contents or error placeholders
  CF-->>Docs: rendered tabs with code blocks
  Docs-->>B: serve HTML with code tabs
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • jhaynie
  • mcongrove
  • rblalock

Poem

In burrows of code I nibble bytes,
I stitch tutorials through long nights.
I stream small deltas, hop by hop—
snippets and steps, I never stop.
Cookie in paw, KV trail unfurled—
a fluffy Pulse that helps the world. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix chat prototype build" is concise and directly related to the primary changes in this PR, which focus on chat functionality, API routes, types, and dependency/config updates intended to make the chat prototype build and run; it clearly communicates the developer's intent without noisy detail.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-chat-prototype-build

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

Copy link

cloudflare-workers-and-pages bot commented Sep 17, 2025

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 67225dc Sep 20 2025, 03:51 PM

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 26

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
agent-docs/src/agents/doc-qa/rag.ts (1)

13-18: Uncaught errors before try/catch (rephrase/retrieve).
If rephrase or retrieval throws, no fallback is returned.

-export default async function answerQuestion(
-  ctx: AgentContext,
-  prompt: string
-) {
-  // First, rephrase the prompt for better vector search
-  const rephrasedPrompt = await rephraseVaguePrompt(ctx, prompt);
-
-  // Use the rephrased prompt for document retrieval
-  const relevantDocs = await retrieveRelevantDocs(ctx, rephrasedPrompt);
+export default async function answerQuestion(
+  ctx: AgentContext,
+  prompt: string
+) {
+  try {
+    // First, rephrase the prompt for better vector search
+    const rephrasedPrompt = await rephraseVaguePrompt(ctx, prompt);
+
+    // Use the rephrased prompt for document retrieval
+    const relevantDocs = await retrieveRelevantDocs(ctx, rephrasedPrompt);

…and close the existing catch by returning the same fallback, plus move the systemPrompt and generateObject inside this try.

-  try {
-    const result = await generateObject({
+    const result = await generateObject({
       model: openai('gpt-4o'),
@@
-  } catch (error) {
+  } catch (error) {
     ctx.logger.error('Error generating answer: %o', error);
@@
-    return fallbackAnswer;
-  }
+    return fallbackAnswer;
+  }

Comment on lines +21 to +27
- startTutorialAtStep: Starting the user off at a specific step of a tutorial.
2. General assistance
- askDocsAgentTool: retrieve Agentuity documentation snippets
=== TOOL-USAGE RULES (must follow) ===
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Tool naming mismatch: startTutorialAtStep vs startTutorialById.
Inconsistent names will confuse the model.

Unify on one tool name and capabilities (start, set step, increment). Example:

 1. Tutorial management  
-   - startTutorialAtStep: Starting the user off at a specific step of a tutorial.
+   - startTutorialAtStep: Start a tutorial at a specific step.
@@
-- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
+- Use startTutorialAtStep only after the user selects a tutorial. For new tutorials, set step = 1. Valid steps are [1, totalSteps].

If an “increment step” tool exists, list it; otherwise remove the “simply increment the step” sentence or map it to a defined tool.

📝 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
- startTutorialAtStep: Starting the user off at a specific step of a tutorial.
2. General assistance
- askDocsAgentTool: retrieve Agentuity documentation snippets
=== TOOL-USAGE RULES (must follow) ===
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
- startTutorialAtStep: Start a tutorial at a specific step.
2. General assistance
- askDocsAgentTool: retrieve Agentuity documentation snippets
=== TOOL-USAGE RULES (must follow) ===
- Use startTutorialAtStep only after the user selects a tutorial. For new tutorials, set step = 1. Valid steps are [1, totalSteps].
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/context/builder.ts around lines 21 to 27,
the tool name is inconsistent between "startTutorialAtStep" and
"startTutorialById"; unify to a single canonical name and update all references
and rules accordingly, e.g., choose either startTutorialById or
startTutorialAtStep and replace the other; ensure the tool capability
description covers starting a tutorial and setting a specific step (with valid
bounds 1..totalSteps) and clarify whether an "increment step" tool exists—if it
does, add it to the list and map the “simply increment the step” sentence to
that tool; if not, remove or reword that sentence so it only references defined
tools and enforce the rule that new tutorials start at step 1.

=== END OF PROMPT ===
Stream your reasoning steps clearly.`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove chain‑of‑thought leakage.
“Stream your reasoning steps clearly.” risks exposing internal reasoning. Replace with instruction to think silently.

-Stream your reasoning steps clearly.`;
+Reason step‑by‑step internally; output only the final answer.`;
📝 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
Stream your reasoning steps clearly.`;
Reason step‑by‑step internally; output only the final answer.`;
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/context/builder.ts around line 46, replace
the prompt fragment "Stream your reasoning steps clearly." because it requests
chain-of-thought and may leak internal reasoning; change it to an instruction
that requires silent internal reasoning such as "Think through the steps
internally and provide the final answer concisely." Ensure the new text does not
ask for step-by-step inner monologue, keeps the response focused on the final
answer, and preserves surrounding formatting/quoting.

Comment on lines +28 to +41
const tutorialContent = JSON.stringify(tutorials.data, null, 2);
const currentTutorialInfo = buildCurrentTutorialInfo(
tutorials.data,
tutorialState
);

return `===AVAILABLE TUTORIALS====
${tutorialContent}
${currentTutorialInfo}
Note: You should not expose the details of the tutorial IDs to the user.
`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Don’t feed raw tutorial IDs into the prompt.

You stringify the entire tutorial list, which likely includes internal IDs. The note “do not expose IDs” relies on the model’s compliance. Strip IDs before embedding to reduce leakage risk.

-    const tutorialContent = JSON.stringify(tutorials.data, null, 2);
+    // Remove internal identifiers from model context
+    const sanitizedTutorials = tutorials.data.map(({ id, ...rest }) => rest);
+    const tutorialContent = JSON.stringify(sanitizedTutorials, null, 2);

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/index.ts around lines 28 to 41, the code
embeds the raw tutorials (including internal IDs) into the prompt by
stringifying tutorials.data and current tutorial info; instead, create a
sanitized copy of tutorials.data with ID fields removed (e.g., map over items
and omit id/_id) and pass that sanitized list into JSON.stringify, and ensure
buildCurrentTutorialInfo is changed or called with the sanitized data (or its
returned object is stripped of any id fields) so neither the full tutorial dump
nor the current tutorial info contains internal IDs before embedding into the
prompt.

Comment on lines +90 to +96
// Build messages for the conversation
const messages: ConversationMessage[] = [
...parsedRequest.conversationHistory,
{ author: "USER", content: parsedRequest.message },
];

let tools: any;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Conversation history is mis-shaped → user turns become assistant turns.

parseAgentRequest returns history items with { role, content }, but you build messages: ConversationMessage[] expecting { author, content }. Then you map using msg.author === "USER", which is undefined for history rows, so they all become assistant messages. Fix by normalizing to model messages directly.

Apply this diff:

-    // Build messages for the conversation
-    const messages: ConversationMessage[] = [
-      ...parsedRequest.conversationHistory,
-      { author: "USER", content: parsedRequest.message },
-    ];
+    // Build OpenAI-compatible messages (normalize history)
+    const modelMessages = [
+      ...(parsedRequest.conversationHistory ?? []).map((m: any) => ({
+        role:
+          (m.role?.toLowerCase?.() as "user" | "assistant") ??
+          (m.author === "USER" ? "user" : "assistant"),
+        content: m.content ?? "",
+      })),
+      { role: "user" as const, content: parsedRequest.message },
+    ];
@@
-    const result = await streamText({
+    const result = await streamText({
       model: openai("gpt-4o"),
-      messages: messages.map((msg) => ({
-        role: msg.author === "USER" ? "user" : "assistant",
-        content: msg.content,
-      })),
+      messages: modelMessages,
       tools,
       maxSteps: 3,
       system: systemPrompt,
     });

Also applies to: 116-124

🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/index.ts around lines 90-96 (and similarly
116-124), the conversation history items use { role, content } but the code
expects { author, content }, causing history turns to be treated as assistants;
normalize the history into the ConversationMessage shape by mapping
parsedRequest.conversationHistory to objects with author set from role (e.g.,
role === 'user' ? 'USER' : 'ASSISTANT') and content copied, then append the
current user message as { author: 'USER', content: parsedRequest.message };
ensure subsequent code that checks msg.author continues to work with this
normalized shape and apply the same normalization at the other location noted.

Comment on lines +106 to +113
// Build tutorial context and system prompt
const tutorialContext = await buildContext(
ctx,
parsedRequest.tutorialData
);
systemPrompt = await buildSystemPrompt(tutorialContext, ctx);
}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

System prompt currently asks to “Stream your reasoning steps clearly.”

The builder sets a CoT‑eliciting instruction. Avoid instructing the model to expose hidden reasoning; keep answers concise and non‑sensitive.

Please update buildSystemPrompt(...) in context/builder.ts to remove that line and reinforce “explain at a high level; no hidden chain-of-thought.”

🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/index.ts around lines 106–113, update the
system prompt builder (in context/builder.ts) so it no longer instructs the
model to "Stream your reasoning steps clearly"; instead, open
context/builder.ts, locate buildSystemPrompt(...), remove the CoT instruction
line, and replace it with a concise directive such as "Explain answers at a high
level; do not reveal internal chain‑of‑thought or step‑by‑step hidden
reasoning," making sure the wording enforces high‑level explanations and avoids
prompting hidden reasoning.

@@ -0,0 +1,77 @@
import { readdir } from "fs/promises";
import { join } from "path";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Block path traversal in readTutorialById and import resolve/sep.

User‑controlled tutorialId can escape basePath (e.g., ../). Enforce containment.

-import { join } from "path";
+import { join, resolve, sep } from "path";
@@
-export async function readTutorialById(basePath: string, tutorialId: string): Promise<TutorialWithPath | null> {
+export async function readTutorialById(basePath: string, tutorialId: string): Promise<TutorialWithPath | null> {
   try {
-    const tutorialPath = join(basePath, tutorialId);
-    const contentPath = join(tutorialPath, "content");
+    const base = resolve(basePath) + sep;
+    const tutorialPath = resolve(basePath, tutorialId) + sep;
+    if (!tutorialPath.startsWith(base)) {
+      console.warn(`Blocked path traversal attempt for tutorialId='${tutorialId}'`);
+      return null;
+    }
+    const contentPath = join(tutorialPath, "content");

Also applies to: 60-71

Comment on lines +40 to +44
tutorials.push({
...tutorial,
path: tutorialPath,
directoryName: tutorialDir.name
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid leaking absolute filesystem paths in API responses.

Returning path exposes server internals. Prefer directoryName or a relative/id-only field.

Also applies to: 67-71

🤖 Prompt for AI Agents
In lib/tutorial/all-tutorials-reader.ts around lines 40-44 and 67-71, the code
currently pushes an absolute filesystem path into the API response; remove the
path field to avoid leaking server internals and instead use directoryName or a
relative/id-only value. Replace any usage of tutorial.path with
tutorial.directoryName or a computed relative identifier (e.g., basename or
repo-relative path) before pushing to tutorials, ensure other code that consumes
this object expects the new field, and update tests and callers accordingly so
no absolute filesystem paths are returned by the API.

Comment on lines +48 to +56
const key = this.getTutorialKey(userId);
try {
await setKVValue(key, state, { storeName: config.defaultStoreName });
} catch (error) {
console.error(
`Failed to update tutorial state. UserId: ${userId}, Error details:`,
error instanceof Error ? error.message : String(error)
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Writes ignore KV failures (setKVValue doesn’t throw)

setKVValue returns { success: false } on errors; current try/catch won’t catch it, so failures are silently ignored.

-    try {
-      await setKVValue(key, state, { storeName: config.defaultStoreName });
-    } catch (error) {
+    try {
+      const res = await setKVValue(key, state, { storeName: config.defaultStoreName });
+      if (!res.success) {
+        throw new Error(res.error || 'Unknown KV write error');
+      }
+    } catch (error) {
       console.error(
         `Failed to update tutorial state. UserId: ${userId}, Error details:`,
         error instanceof Error ? error.message : String(error)
       );
     }
📝 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
const key = this.getTutorialKey(userId);
try {
await setKVValue(key, state, { storeName: config.defaultStoreName });
} catch (error) {
console.error(
`Failed to update tutorial state. UserId: ${userId}, Error details:`,
error instanceof Error ? error.message : String(error)
);
}
const key = this.getTutorialKey(userId);
try {
const res = await setKVValue(key, state, { storeName: config.defaultStoreName });
if (!res.success) {
throw new Error(res.error || 'Unknown KV write error');
}
} catch (error) {
console.error(
`Failed to update tutorial state. UserId: ${userId}, Error details:`,
error instanceof Error ? error.message : String(error)
);
}
🤖 Prompt for AI Agents
In lib/tutorial/state-manager.ts around lines 48 to 56, the code assumes
setKVValue throws on failure but it returns an object like { success: false };
update the logic to await setKVValue and inspect its return value, and if
success is false log the failure (include userId and any error/message returned)
and handle it (either return a failure result or rethrow an Error) instead of
relying on try/catch alone so KV failures are not silently ignored.

Comment on lines +94 to +103
static async completeTutorial(userId: string, tutorialId: string): Promise<void> {
const state = await this.getUserTutorialState(userId);

if (state.tutorials[tutorialId]) {
state.tutorials[tutorialId].completedAt = new Date().toISOString();
state.tutorials[tutorialId].lastAccessedAt = new Date().toISOString();

const key = this.getTutorialKey(userId);
await setKVValue(key, state, { storeName: config.defaultStoreName });
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Same silent-failure risk on completion path

Check KV write result and surface errors.

-      const key = this.getTutorialKey(userId);
-      await setKVValue(key, state, { storeName: config.defaultStoreName });
+      const key = this.getTutorialKey(userId);
+      const res = await setKVValue(key, state, { storeName: config.defaultStoreName });
+      if (!res.success) {
+        throw new Error(res.error || 'Unknown KV write error');
+      }
📝 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
static async completeTutorial(userId: string, tutorialId: string): Promise<void> {
const state = await this.getUserTutorialState(userId);
if (state.tutorials[tutorialId]) {
state.tutorials[tutorialId].completedAt = new Date().toISOString();
state.tutorials[tutorialId].lastAccessedAt = new Date().toISOString();
const key = this.getTutorialKey(userId);
await setKVValue(key, state, { storeName: config.defaultStoreName });
}
static async completeTutorial(userId: string, tutorialId: string): Promise<void> {
const state = await this.getUserTutorialState(userId);
if (state.tutorials[tutorialId]) {
state.tutorials[tutorialId].completedAt = new Date().toISOString();
state.tutorials[tutorialId].lastAccessedAt = new Date().toISOString();
const key = this.getTutorialKey(userId);
const res = await setKVValue(key, state, { storeName: config.defaultStoreName });
if (!res.success) {
throw new Error(res.error || 'Unknown KV write error');
}
}
🤖 Prompt for AI Agents
In lib/tutorial/state-manager.ts around lines 94 to 103, the call to setKVValue
when marking a tutorial completed is a silent failure risk; update the code to
await and validate the KV write result and surface errors: wrap the setKVValue
call in a try/catch, await its result into a variable, check for falsy/erroneous
response (or an error field depending on setKVValue contract) and throw or log a
descriptive Error including the key, userId, tutorialId and the underlying
error/details, and rethrow the error so callers can handle it instead of
silently continuing.

"react-dom": "^19.0.0",
"next": "^15.5.2",
"node-pty": "^1.0.0",
"react": "^19.1.1",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

node-pty is not compatible with Cloudflare/Edge runtimes.
Native module; will fail if bundled into worker. Gate behind dynamic import in Node-only code or move to devDeps if used only locally.

  • Ensure no imports of node-pty run in edge/worker paths.
  • If needed at build/dev only, move to devDependencies.

Run to confirm there are no imports shipped to the worker:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 '\bnode-pty\b|from\s+[\'"]node-pty[\'"]'

Length of output: 68


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Searching for 'node-pty' across repository (excluding node_modules)..."
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden -S -g '!**/node_modules/**' "node-pty" -C3 || true
else
  grep -RIn --exclude-dir=node_modules "node-pty" . || true
fi

echo
echo "Checking package and lock files for mentions..."
for f in package.json package-lock.json yarn.lock pnpm-lock.yaml; do
  if [ -f "$f" ]; then
    echo "---- $f ----"
    if command -v rg >/dev/null 2>&1; then
      rg -n "node-pty" "$f" || true
    else
      grep -n "node-pty" "$f" || true
    fi
  fi
done

Length of output: 491


Block or move node-pty — native module incompatible with Edge/Cloudflare workers

node-pty is declared in package.json (line 38) and appears in package-lock.json; it's a native module and will fail if bundled into an Edge/Cloudflare Worker.

  • If only needed for build/dev, move "node-pty" to devDependencies.
  • Otherwise ensure all imports are gated to Node-only runtime (dynamic import behind a Node/runtime check) and confirm your bundler does not include it in worker/edge bundles.
  • Verify no worker/edge entrypoint statically imports or references "node-pty".

Locations: package.json (line 38); package-lock.json (entries referencing node-pty).

🤖 Prompt for AI Agents
In package.json around line 39, node-pty is declared as a regular dependency but
is a native module incompatible with Edge/Cloudflare workers; either move
"node-pty" to devDependencies if it’s only used at build/dev time, or keep it in
dependencies but ensure all imports are gated to Node-only runtime (use dynamic
import behind a runtime/Node check) and confirm your bundler excludes node-pty
from worker/edge bundles; additionally audit worker/edge entrypoints and
package-lock.json to remove any static references so no worker/edge code
statically imports or references node-pty.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
app/api/tutorials/route.ts (2)

45-45: totalSteps excludes 'index' here — update step endpoint to match

This route is correct. Ensure app/api/tutorials/[id]/steps/[stepNumber]/route.ts returns stepSlugs.length (not pages.length) for consistency.

Patch for the steps route:

-        totalSteps: pages.length
+        totalSteps: stepSlugs.length

Run to verify:

#!/bin/bash
rg -n "totalSteps:" app/api/tutorials/\[id]/steps/\[stepNumber]/route.ts -n -C2

20-21: Unify response shape with sibling tutorial APIs

Return { success: true, data } on success and { success: false, error, message } on failure to match other tutorial endpoints and avoid client breakage.

-      return NextResponse.json([]);
+      return NextResponse.json({ success: true, data: [] });
-    if (pages.length === 0) {
-      return NextResponse.json([]);
-    }
+    if (pages.length === 0) {
+      return NextResponse.json({ success: true, data: [] });
+    }
-    return NextResponse.json(results);
+    return NextResponse.json({ success: true, data: results });
-    return NextResponse.json(
-      { error: 'Failed to read tutorials' },
-      { status: 500 }
-    );
+    return NextResponse.json(
+      {
+        success: false,
+        error: 'Failed to read tutorials',
+        message: error instanceof Error ? error.message : String(error),
+      },
+      { status: 500 }
+    );

Also applies to: 24-26, 78-86

lib/env.ts (1)

62-67: Fix TypeScript env augmentation target and add missing vars.

Augment NodeJS.ProcessEnv, not a top-level ProcessEnv. Also type AGENTUITY_BASE_URL (used by lib/config.ts). Prior bot comment noted this; still applicable.

-declare global {
-  interface ProcessEnv {
-    AGENT_BASE_URL?: string;
-    AGENT_BEARER_TOKEN?: string;
-  }
-}
+declare global {
+  namespace NodeJS {
+    interface ProcessEnv {
+      AGENT_BASE_URL?: string;
+      AGENTUITY_BASE_URL?: string;
+      AGENT_BEARER_TOKEN?: string;
+    }
+  }
+}
🧹 Nitpick comments (10)
.env.example (2)

4-6: Clarify base URL format and precedence.

Add a note whether a trailing slash is required and how this interacts with AGENT_BASE_URL (dev vs prod). Helps avoid double slashes and misconfig.

Apply:

 # For KV store API service
-AGENTUITY_BASE_URL=https://api.agentuity.com
+# No trailing slash. Used for prod; AGENT_BASE_URL is for local dev.
+AGENTUITY_BASE_URL=https://api.agentuity.com

8-8: Add newline at EOF to satisfy dotenv-linter.

Fixes EndingBlankLine warning.

-AGENTUITY_API_KEY=your-key-here
+AGENTUITY_API_KEY=your-key-here
+
agent-docs/README.md (1)

50-50: Consider updating the preface text for consistency.

The change from "Run your project in development mode with:" to just that line without modification maintains the existing flow, but since you added a preface for the bun install step, this line now feels like it's continuing the previous instruction rather than starting a new one.

Consider updating the preface to be more explicit:

-Run your project in development mode with:
+Then run your project in development mode with:

or

-Run your project in development mode with:
+Next, run your project in development mode with:
app/api/tutorials/route.ts (2)

44-46: Harden child meta parsing; remove redundant fallback

Validate pages as an array of strings and drop the unnecessary || 0.

-          const pagesList = childMeta.pages ?? [];
-          const totalSteps = pagesList.filter(p => p !== 'index').length || 0;
+          const pagesList = Array.isArray(childMeta.pages)
+            ? childMeta.pages.filter((p): p is string => typeof p === 'string')
+            : [];
+          const totalSteps = pagesList.filter(p => p !== 'index').length;

30-33: Slug validation: switch to allowlist regex (stronger than blacklist)

Prevents odd/edge slugs while still blocking traversal.

-      if (entry.includes('..') || entry.includes('/') || entry.includes('\\')) {
-        continue;
-      }
+      // Allow only letters, numbers, dashes, and underscores; must start alnum
+      const isValidSlug = /^[a-z0-9][a-z0-9-_]*$/i.test(entry);
+      if (!isValidSlug) {
+        // eslint-disable-next-line no-console
+        console.warn('Skipping invalid tutorial slug in parent meta:', entry);
+        continue;
+      }
app/api/rag-search/route.ts (4)

2-2: QA config in production path — confirm intent or gate behind env flag.

Importing and using getAgentQaConfig() hard-binds this route to QA. If that’s temporary, gate the selection with an env flag or switch to Pulse in production builds.

Apply to select config based on an env toggle:

-import { getAgentQaConfig } from '@/lib/env';
+import { getAgentQaConfig, getAgentPulseConfig } from '@/lib/env';

 // ...

-const agentConfig = getAgentQaConfig();
+const agentConfig =
+  process.env.USE_QA_AGENT === 'true'
+    ? getAgentQaConfig()
+    : getAgentPulseConfig();

95-113: Add a fetch timeout to avoid hung requests on serverless.

Unbounded fetch can pin a lambda for minutes. Use AbortController with a sane timeout.

-    const agentConfig = getAgentQaConfig();
+    const agentConfig = getAgentQaConfig();

     // Prepare headers
     const headers: Record<string, string> = {
       'Content-Type': 'application/json',
     };

     // Add bearer token if provided
     if (agentConfig.bearerToken) {
       headers['Authorization'] = `Bearer ${agentConfig.bearerToken}`;
     }
 
-    const response = await fetch(agentConfig.url, {
+    const controller = new AbortController();
+    const timeout = setTimeout(() => controller.abort(), 15_000);
+    const response = await fetch(agentConfig.url, {
       method: 'POST',
       headers,
-      body: JSON.stringify({ message: query }),
+      body: JSON.stringify({ message: query }),
+      signal: controller.signal,
     });
+    clearTimeout(timeout);

133-158: Cap and sanitize document results.

If the agent returns many docs or non-string values, responses can balloon or throw. Limit count and guard type.

-    if (
-      data.documents &&
-      Array.isArray(data.documents) &&
-      data.documents.length > 0
-    ) {
-      const uniqueDocuments = [...new Set(data.documents as string[])];
+    if (Array.isArray(data?.documents) && data.documents.length > 0) {
+      const uniqueDocuments = [...new Set(
+        (data.documents as unknown[]).filter((d): d is string => typeof d === 'string')
+      )].slice(0, 10);

160-161: Reduce noisy console logs in prod.

Guard with NODE_ENV !== 'production' or a debug flag.

-  console.log('Returning RAG results:', results.length, 'items');
+  if (process.env.NODE_ENV !== 'production') {
+    console.log('Returning RAG results:', results.length, 'items');
+  }
lib/env.ts (1)

43-56: Validate both QA and Pulse configs; keep logs actionable without secrets.

Catching misconfig early helps CI fail fast.

 export const validateEnv = (): boolean => {
   try {
-    getAgentQaConfig();
-    console.log('✓ Environment variables validated');
+    getAgentQaConfig();
+    getAgentPulseConfig();
+    console.log('✓ Agent config validated (baseUrl + QA/Pulse IDs)');
     return true;
   } catch (error) {
     console.error('❌ Environment validation failed:', error);
-    console.error('💡 Make sure to set base URL via:');
-    console.error('   - AGENT_BASE_URL env var, or');
-    console.error('   - Use default from config.baseUrl');
-    console.error('💡 Optionally set AGENT_BEARER_TOKEN for authentication');
+    console.error('💡 Set base URL via AGENT_BASE_URL or AGENTUITY_BASE_URL (config.baseUrl).');
+    console.error('💡 Optionally set AGENT_BEARER_TOKEN for authentication (not logged).');
     return false;
   }
 };
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 65170ce and 36d12c0.

⛔ Files ignored due to path filters (1)
  • agent-docs/bun.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .env.example (1 hunks)
  • agent-docs/RAG-TODO.md (0 hunks)
  • agent-docs/RAG-design.md (0 hunks)
  • agent-docs/RAG-user-stories.md (0 hunks)
  • agent-docs/README.md (1 hunks)
  • app/api/rag-search/route.ts (2 hunks)
  • app/api/tutorials/route.ts (1 hunks)
  • lib/config.ts (1 hunks)
  • lib/env.ts (1 hunks)
  • lib/kv-store.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • agent-docs/RAG-user-stories.md
  • agent-docs/RAG-TODO.md
  • agent-docs/RAG-design.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • lib/config.ts
  • lib/kv-store.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Import types from 'agentuity/sdk'

Applied to files:

  • app/api/rag-search/route.ts
  • lib/env.ts
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Consider agent communication for complex workflows

Applied to files:

  • lib/env.ts
🧬 Code graph analysis (3)
app/api/rag-search/route.ts (1)
lib/env.ts (1)
  • getAgentQaConfig (32-34)
app/api/tutorials/route.ts (2)
app/api/tutorials/[id]/route.ts (1)
  • GET (9-43)
app/api/tutorials/[id]/steps/[stepNumber]/route.ts (1)
  • GET (11-178)
lib/env.ts (1)
lib/config.ts (1)
  • config (6-11)
🪛 dotenv-linter (3.3.0)
.env.example

[warning] 8-8: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (2)
.env.example (1)

7-8: Align env var name and add placeholder in .env.example

Comment references AGENTUITY_SDK_KEY but the file uses AGENTUITY_API_KEY — update the comment and add a non-empty placeholder to indicate the value is required.

File: .env.example (lines 7–8)

-# API key can be found in agent-docs .env AGENTUITY_SDK_KEY
-AGENTUITY_API_KEY=
+# Copy value from agent-docs .env AGENTUITY_SDK_KEY into AGENTUITY_API_KEY (do not commit real keys)
+AGENTUITY_API_KEY=your-key-here

Verification: .gitignore contains .env entries (so .env is ignored); rg reported "No files were searched" for the variable-usage checks — re-run the ripgrep checks without the restrictive glob to confirm there are no lingering AGENT_ID/AGENT_FULL_URL references and that AGENTUITY_API_KEY/AGENTUITY_BASE_URL are referenced by the code/configs.

agent-docs/README.md (1)

45-48: LGTM! Clear and helpful addition.

Adding the bun install step before development mode is a good practice that ensures dependencies are properly installed. The phrasing is clear and the placement is logical.

Comment on lines 13 to 30
const buildAgentConfig = (agentId: string): AgentConfig => {
const baseUrl = process.env.AGENT_BASE_URL;
const bearerToken = process.env.AGENT_BEARER_TOKEN;

// Validate required environment variables
if (!fullUrl && (!baseUrl || !agentId)) {
throw new Error(
'Missing required environment variables. Either set AGENT_FULL_URL or both AGENT_BASE_URL and AGENT_ID'
);
}
if (!baseUrl) {
throw new Error(
'Missing required configuration. Set AGENT_BASE_URL or ensure config.baseUrl is defined.'
);
}
if (!agentId) {
throw new Error('Missing required agent ID in config');
}

const url = fullUrl || `${baseUrl}/${agentId}`;
return {
url: `${baseUrl}/${agentId}`,
bearerToken: bearerToken || undefined,
};
};
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Blocker: base URL env mismatch and missing fallback cause runtime failure.

buildAgentConfig reads AGENT_BASE_URL, but lib/config.ts defines baseUrl from AGENTUITY_BASE_URL. If only AGENTUITY_BASE_URL is set (per config), baseUrl here is undefined and throws, contradicting the error text.

Fix by sourcing from config.baseUrl (and normalizing slashes). Back-compat keeps AGENT_BASE_URL if present.

-const buildAgentConfig = (agentId: string): AgentConfig => {
-  const baseUrl = process.env.AGENT_BASE_URL;
-  const bearerToken = process.env.AGENT_BEARER_TOKEN;
+const buildAgentConfig = (agentId: string): AgentConfig => {
+  const envBase = process.env.AGENT_BASE_URL;
+  const baseUrlRaw = envBase ?? config.baseUrl;
+  const bearerToken = process.env.AGENT_BEARER_TOKEN;
 
-  if (!baseUrl) {
+  if (!baseUrlRaw) {
     throw new Error(
-      'Missing required configuration. Set AGENT_BASE_URL or ensure config.baseUrl is defined.'
+      'Missing required configuration. Define AGENT_BASE_URL or AGENTUITY_BASE_URL (via config.baseUrl).'
     );
   }
   if (!agentId) {
     throw new Error('Missing required agent ID in config');
   }
 
-  return {
-    url: `${baseUrl}/${agentId}`,
+  const baseUrl = baseUrlRaw.replace(/\/+$/, '');
+  return {
+    url: `${baseUrl}/${agentId}`,
     bearerToken: bearerToken || 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
const buildAgentConfig = (agentId: string): AgentConfig => {
const baseUrl = process.env.AGENT_BASE_URL;
const bearerToken = process.env.AGENT_BEARER_TOKEN;
// Validate required environment variables
if (!fullUrl && (!baseUrl || !agentId)) {
throw new Error(
'Missing required environment variables. Either set AGENT_FULL_URL or both AGENT_BASE_URL and AGENT_ID'
);
}
if (!baseUrl) {
throw new Error(
'Missing required configuration. Set AGENT_BASE_URL or ensure config.baseUrl is defined.'
);
}
if (!agentId) {
throw new Error('Missing required agent ID in config');
}
const url = fullUrl || `${baseUrl}/${agentId}`;
return {
url: `${baseUrl}/${agentId}`,
bearerToken: bearerToken || undefined,
};
};
const buildAgentConfig = (agentId: string): AgentConfig => {
const envBase = process.env.AGENT_BASE_URL;
const baseUrlRaw = envBase ?? config.baseUrl;
const bearerToken = process.env.AGENT_BEARER_TOKEN;
if (!baseUrlRaw) {
throw new Error(
'Missing required configuration. Define AGENT_BASE_URL or AGENTUITY_BASE_URL (via config.baseUrl).'
);
}
if (!agentId) {
throw new Error('Missing required agent ID in config');
}
const baseUrl = baseUrlRaw.replace(/\/+$/, '');
return {
url: `${baseUrl}/${agentId}`,
bearerToken: bearerToken || undefined,
};
};

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (2)
app/chat/SessionContext.tsx (1)

1-1: Good fix: added "use client".

This resolves the client-component requirement noted earlier.

lib/env.ts (1)

70-75: Use NodeJS.ProcessEnv for global augmentation (not a top‑level ProcessEnv).

The current augmentation can break TS type resolution.

-declare global {
-  interface ProcessEnv {
-    AGENT_BASE_URL?: string;
-    AGENT_BEARER_TOKEN?: string;
-  }
-}
+declare global {
+  namespace NodeJS {
+    interface ProcessEnv {
+      AGENT_BASE_URL?: string;
+      AGENT_BEARER_TOKEN?: string;
+      AGENTUITY_API_KEY?: string; // alias supported by buildAgentConfig
+    }
+  }
+}
🧹 Nitpick comments (4)
app/chat/SessionContext.tsx (2)

8-8: Consider nullability for currentSessionId.

If the UI can load with no active session, prefer string | null to model “no selection.”

-  currentSessionId: string;
+  currentSessionId: string | null;

13-14: Add displayName for easier React DevTools debugging (optional).

 export const SessionContext = createContext<SessionContextType | undefined>(undefined);
+
+SessionContext.displayName = 'SessionContext';
lib/env.ts (2)

58-61: Correct guidance text to reference config.agentBaseUrl.

Message currently mentions config.baseUrl which isn’t what buildAgentConfig uses.

-    console.error('   - Use default from config.baseUrl');
+    console.error('   - Or rely on config.agentBaseUrl');

26-32: Optional: detect localhost via URL parsing.

String includes works but parsing is more robust (handles ports/paths cleanly).

-  if (baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost')) {
+  const host = (() => { try { return new URL(baseUrl).hostname; } catch { return ''; } })();
+  if (host === '127.0.0.1' || host === 'localhost') {
     if (!agentId.startsWith('agent_')) {
       finalAgentId = `agent_${agentId}`;
     }
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36d12c0 and 66c9233.

📒 Files selected for processing (4)
  • .env.example (1 hunks)
  • app/chat/SessionContext.tsx (1 hunks)
  • lib/config.ts (1 hunks)
  • lib/env.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/config.ts
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Consider agent communication for complex workflows

Applied to files:

  • lib/env.ts
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Import types from 'agentuity/sdk'

Applied to files:

  • lib/env.ts
🧬 Code graph analysis (2)
lib/env.ts (1)
lib/config.ts (1)
  • config (6-12)
app/chat/SessionContext.tsx (1)
app/chat/types.ts (1)
  • Session (54-54)
🪛 dotenv-linter (3.3.0)
.env.example

[warning] 5-5: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (3)
app/chat/SessionContext.tsx (1)

15-21: Verify error message matches the actual provider name.

If you don’t have a dedicated SessionProvider component and are using SessionContext.Provider directly, update the message. Otherwise, ignore.

-    throw new Error('useSessions must be used within a SessionProvider');
+    throw new Error('useSessions must be used within SessionContext.Provider');
lib/env.ts (2)

5-6: AgentConfig shape looks good.


13-37: Standardize env var names: prefer AGENTUITY_SDK_KEY (fallback to AGENTUITY_API_KEY); fix lib/kv-store.ts.

  • No occurrences of AGENTUITY_BASE_URL or AGENT_FULL_URL found.
  • lib/kv-store.ts still validates/uses AGENTUITY_API_KEY (validation + Authorization headers). Change to use process.env.AGENTUITY_SDK_KEY || process.env.AGENTUITY_API_KEY and update error messages. Locations: lib/kv-store.ts (≈lines 31, 65, 142, 196, 237, 250).
  • agent-docs/index.ts already checks for either AGENTUITY_API_KEY or AGENTUITY_SDK_KEY; align implementation with docs/changelog (canonical = AGENTUITY_SDK_KEY).
⛔ Skipped due to learnings
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/agent.mdc:0-0
Timestamp: 2025-07-23T12:40:22.412Z
Learning: Applies to agent-docs/**/src/agents/**/index.ts : Prefer loading types from the node modules package `agentuity/sdk` in the node_modules folder
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/agent.mdc:0-0
Timestamp: 2025-07-23T12:40:22.412Z
Learning: Applies to agent-docs/**/src/agents/**/index.ts : Prefer naming the default function Agent or the name of the Agent based on the context of the Agent description

Comment on lines +4 to +5
# API key can be found in agent-docs .env AGENTUITY_SDK_KEY
AGENTUITY_API_KEY=
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Align auth var name with runtime (AGENT_BEARER_TOKEN) or support alias.

env.ts reads AGENT_BEARER_TOKEN; this sample introduces AGENTUITY_API_KEY. Either update env.ts to accept AGENTUITY_API_KEY as an alias (recommended; see my env.ts diff) or change the sample to AGENT_BEARER_TOKEN to avoid 401s. Also add a trailing newline to satisfy dotenv-linter.

Recommended sample tweak:

-# API key can be found in agent-docs .env AGENTUITY_SDK_KEY
-AGENTUITY_API_KEY=
+# Authentication (set one)
+# Prefer AGENT_BEARER_TOKEN; AGENTUITY_API_KEY is accepted as an alias.
+AGENT_BEARER_TOKEN=
+# AGENTUITY_API_KEY=
+
📝 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
# API key can be found in agent-docs .env AGENTUITY_SDK_KEY
AGENTUITY_API_KEY=
# Authentication (set one)
# Prefer AGENT_BEARER_TOKEN; AGENTUITY_API_KEY is accepted as an alias.
AGENT_BEARER_TOKEN=
# AGENTUITY_API_KEY=
🧰 Tools
🪛 dotenv-linter (3.3.0)

[warning] 5-5: [EndingBlankLine] No blank line at the end of the file

(EndingBlankLine)

🤖 Prompt for AI Agents
In .env.example around lines 4 to 5, the sample variable AGENTUITY_API_KEY
mismatches the runtime env var AGENT_BEARER_TOKEN and will cause auth failures;
update the sample to AGENT_BEARER_TOKEN= and add a trailing newline to satisfy
dotenv-linter; alternatively, if you prefer keeping AGENTUITY_API_KEY, update
env.ts to accept it as an alias by reading both process.env.AGENT_BEARER_TOKEN
and process.env.AGENTUITY_API_KEY (prefer AGENT_BEARER_TOKEN if present) so the
runtime accepts either name and avoid 401s.

Comment on lines +2 to +3
import { createContext, useContext } from 'react';
import { Session } from './types';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Import the SetStateAction type to prevent TS build errors.

The interface below references React.SetStateAction, but React isn’t imported as a namespace. Import the type directly.

-import { createContext, useContext } from 'react';
+import { createContext, useContext, type SetStateAction } from 'react';
📝 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
import { createContext, useContext } from 'react';
import { Session } from './types';
import { createContext, useContext, type SetStateAction } from 'react';
import { Session } from './types';
🤖 Prompt for AI Agents
In app/chat/SessionContext.tsx around lines 2 to 3, the interface uses
React.SetStateAction but React isn't imported as a namespace; modify the import
to include the SetStateAction type (for example: import { createContext,
useContext, type SetStateAction } from 'react') so the type reference resolves
and TypeScript build errors are eliminated.

Comment on lines +5 to +11
interface SessionContextType {
sessions: Session[];
setSessions: (updater: React.SetStateAction<Session[]>, options?: { revalidate: boolean }) => void;
currentSessionId: string;
// A simple trigger to revalidate sessions; implementation may vary under the hood
revalidateSessions?: () => void | Promise<any>;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix type references and tighten optionalities.

Switch to SetStateAction (matches the import), and consider making revalidate optional; also avoid Promise.

 interface SessionContextType {
   sessions: Session[];
-  setSessions: (updater: React.SetStateAction<Session[]>, options?: { revalidate: boolean }) => void;
-  currentSessionId: string;
+  setSessions: (updater: SetStateAction<Session[]>, options?: { revalidate?: boolean }) => void;
+  currentSessionId: string;
   // A simple trigger to revalidate sessions; implementation may vary under the hood
-  revalidateSessions?: () => void | Promise<any>;
+  revalidateSessions?: () => void | Promise<void>;
 }

Optional: export the interface if consumers (e.g., a Provider) need the type.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/chat/SessionContext.tsx around lines 5 to 11, tighten the types: change
setSessions to the standard React dispatch signature
(React.Dispatch<React.SetStateAction<Session[]>>) instead of a custom function
signature, make the revalidate flag optional (options?: { revalidate?: boolean
}) if present, replace Promise<any> with a specific void return
(revalidateSessions?: () => void | Promise<void>), and export the interface if
it needs to be consumed externally (export interface SessionContextType).

Comment on lines 13 to 22
const buildAgentConfig = (agentId: string): AgentConfig => {
const baseUrl = process.env.AGENT_BASE_URL;
const bearerToken = process.env.AGENT_BEARER_TOKEN;

if (!baseUrl) {
throw new Error(
'Missing required configuration. Set AGENT_BASE_URL or ensure config.baseUrl is defined.'
);
}
if (!agentId) {
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix base URL source, support API key alias, and normalize slashes.

buildAgentConfig ignores config.agentBaseUrl and only reads AGENT_BASE_URL, causing failures when only config is set. It also risks double slashes and doesn’t accept AGENTUITY_API_KEY. Use config.agentBaseUrl fallback, strip trailing slashes, and accept the alias.

 const buildAgentConfig = (agentId: string): AgentConfig => {
-  const baseUrl = process.env.AGENT_BASE_URL;
-  const bearerToken = process.env.AGENT_BEARER_TOKEN;
+  const envBase = process.env.AGENT_BASE_URL ?? config.agentBaseUrl;
+  const baseUrl = envBase ? envBase.replace(/\/+$/, '') : undefined;
+  const bearerToken =
+    process.env.AGENT_BEARER_TOKEN ?? process.env.AGENTUITY_API_KEY;
 
-  if (!baseUrl) {
+  if (!baseUrl) {
     throw new Error(
-      'Missing required configuration. Set AGENT_BASE_URL or ensure config.baseUrl is defined.'
+      'Missing required configuration. Define AGENT_BASE_URL or set config.agentBaseUrl.'
     );
   }
   if (!agentId) {
     throw new Error('Missing required agent ID in config');
   }
 
   // For localhost/127.0.0.1, ensure agent ID has 'agent_' prefix
@@
-  return {
-    url: `${baseUrl}/${finalAgentId}`,
-    bearerToken: bearerToken || undefined,
-  };
+  return {
+    url: `${baseUrl}/${finalAgentId}`,
+    bearerToken: bearerToken || undefined,
+  };
 };

Also applies to: 34-37

🤖 Prompt for AI Agents
In lib/env.ts around lines 13-22 (also apply same change to lines 34-37),
buildAgentConfig currently only reads process.env.AGENT_BASE_URL and
AGENT_BEARER_TOKEN, causing it to ignore config.agentBaseUrl and the
AGENTUITY_API_KEY alias, and it can produce double slashes; update it to: use
config.agentBaseUrl as a fallback when process.env.AGENT_BASE_URL is not set,
accept either process.env.AGENT_BEARER_TOKEN or process.env.AGENTUITY_API_KEY
for the bearer token, and normalize the baseUrl by removing any trailing slashes
(and ensure a single leading protocol/host format) before returning the
AgentConfig; make the identical fixes at lines 34-37 where the same logic is
duplicated.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
lib/env.ts (1)

71-74: Correct global env augmentation target and include API key alias.
Augment NodeJS.ProcessEnv, not a top-level ProcessEnv; add AGENTUITY_API_KEY alias. Only add AGENT_PULSE_ID if you reintroduce env-driven IDs.

-declare global {
-  interface ProcessEnv {
-    AGENT_BASE_URL?: string;
-    AGENT_BEARER_TOKEN?: string;
-  }
-}
+declare global {
+  namespace NodeJS {
+    interface ProcessEnv {
+      AGENT_BASE_URL?: string;
+      AGENT_BEARER_TOKEN?: string;
+      AGENTUITY_API_KEY?: string;
+      // AGENT_PULSE_ID?: string; // uncomment only if env-driven IDs are used
+    }
+  }
+}
🧹 Nitpick comments (2)
lib/env.ts (2)

13-38: Normalize base URL, accept API key alias, and harden localhost detection.

  • Prevent double slashes by trimming trailing slashes.
  • Accept AGENTUITY_API_KEY as an alias for AGENT_BEARER_TOKEN.
  • Determine localhost via parsed hostname to avoid false positives (e.g., dev-localhost.example.com).

Apply:

-const buildAgentConfig = (agentId: string): AgentConfig => {
-  const baseUrl = config.agentBaseUrl;
-  const bearerToken = process.env.AGENT_BEARER_TOKEN;
+const buildAgentConfig = (agentId: string): AgentConfig => {
+  const rawBase = config.agentBaseUrl;
+  const bearerToken =
+    process.env.AGENT_BEARER_TOKEN ?? process.env.AGENTUITY_API_KEY;
 
-  if (!baseUrl) {
+  if (!rawBase) {
     throw new Error(
       'Missing required configuration. Set AGENT_BASE_URL or ensure config.agentBaseUrl is defined.'
     );
   }
   if (!agentId) {
     throw new Error('Missing required agent ID in config');
   }
 
-  // For localhost/127.0.0.1, ensure agent ID has 'agent_' prefix
-  let finalAgentId = agentId;
-  if (baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost')) {
+  // For localhost/127.0.0.1, ensure agent ID has 'agent_' prefix
+  const baseUrl = rawBase.replace(/\/+$/, '');
+  const host = (() => {
+    try {
+      return new URL(baseUrl + '/').hostname;
+    } catch {
+      return '';
+    }
+  })();
+  let finalAgentId = agentId;
+  if (host === 'localhost' || host === '127.0.0.1' || host === '0.0.0.0') {
     if (!agentId.startsWith('agent_')) {
       finalAgentId = `agent_${agentId}`;
     }
   }
 
   return {
     url: `${baseUrl}/${finalAgentId}`,
     bearerToken: bearerToken || undefined,
   };
 };

52-63: Fix guidance text and validate both configs.
Message references config.baseUrl but code uses config.agentBaseUrl. Also validate Pulse to catch misconfig early.

   try {
-    getAgentQaConfig();
-    console.log('✓ Environment variables validated');
+    getAgentQaConfig();
+    getAgentPulseConfig();
+    console.log('✓ Environment validated: agent configs resolvable');
     return true;
   } catch (error) {
     console.error('❌ Environment validation failed:', error);
     console.error('💡 Make sure to set base URL via:');
     console.error('   - AGENT_BASE_URL env var, or');
-    console.error('   - Use default from config.baseUrl');
-    console.error('💡 Optionally set AGENT_BEARER_TOKEN for authentication');
+    console.error('   - Use default from config.agentBaseUrl');
+    console.error('💡 Optionally set AGENT_BEARER_TOKEN or AGENTUITY_API_KEY for authentication');
     return false;
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 66c9233 and 9b61379.

📒 Files selected for processing (1)
  • lib/env.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Consider agent communication for complex workflows

Applied to files:

  • lib/env.ts
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Use TypeScript for better type safety and IDE support

Applied to files:

  • lib/env.ts
📚 Learning: 2025-07-23T12:40:34.834Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/sdk.mdc:0-0
Timestamp: 2025-07-23T12:40:34.834Z
Learning: Applies to agent-docs/src/agents/**/*.ts : Import types from 'agentuity/sdk'

Applied to files:

  • lib/env.ts
🧬 Code graph analysis (1)
lib/env.ts (1)
lib/config.ts (1)
  • config (6-12)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (2)
lib/env.ts (2)

5-6: LGTM: AgentConfig shape is clean and minimal.
No issues. Optional future: add a docstring describing url format.


40-46: Expose both QA and Pulse configs — callers confirmed.
getAgentQaConfig: lib/env.ts (validateEnv), app/api/rag-search/route.ts.
getAgentPulseConfig: app/api/sessions/[sessionId]/messages/route.ts.

@afterrburn afterrburn enabled auto-merge (squash) September 20, 2025 15:42
@afterrburn afterrburn disabled auto-merge September 20, 2025 15:43
@afterrburn afterrburn enabled auto-merge (squash) September 20, 2025 15:51
@afterrburn afterrburn disabled auto-merge September 20, 2025 15:51
@afterrburn afterrburn merged commit 96d0124 into main Sep 20, 2025
2 checks passed
@afterrburn afterrburn deleted the fix-chat-prototype-build branch September 20, 2025 16:24
devin-ai-integration bot added a commit that referenced this pull request Sep 20, 2025
afterrburn pushed a commit that referenced this pull request Sep 20, 2025
This reverts commit 96d0124.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This was referenced Sep 20, 2025
@coderabbitai coderabbitai bot mentioned this pull request Oct 1, 2025
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