Skip to content

Conversation

afterrburn
Copy link
Contributor

@afterrburn afterrburn commented Sep 5, 2025

Summary by CodeRabbit

  • New Features

    • Introduced chat experience with session management, streaming AI replies, and pagination.
    • Integrated tutorials: fetch steps, render code from repo files, track user progress, and display a “Dynamic Island” tutorial UI.
    • Added in-app code editor and enhanced code block viewing/copying.
    • Persisted user sessions/state via KV store and anonymous user cookie.
  • Documentation

    • Added agent and tutorials structure guides; new Pulse Agent README.
  • Configuration

    • Updated env vars (separate agent IDs, API key); ignore .env files.
  • UI/Style

    • New theme utilities for backgrounds, cards, buttons, and scrollbars.
  • Chores

    • Upgraded Next/React; added SWR, Zod, UUID, and related deps.

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: 3

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

10-17: Avoid creating empty sessions; early-return on blank input

You already trim and only persist when non-empty; also guard navigation to skip creating an empty session.

-    const handleMessageSend = (message: string) => {
-        const newSessionId = uuidv4();
-        const trimmed = message.trim();
-        if (trimmed) {
-            sessionStorage.setItem(`initialMessage:${newSessionId}`, trimmed);
-        }
-        router.push(`/chat/${newSessionId}`);
-    };
+    const handleMessageSend = (message: string) => {
+        const text = message.trim();
+        if (!text) return;
+        const newSessionId = uuidv4();
+        sessionStorage.setItem(`initialMessage:${newSessionId}`, text);
+        router.push(`/chat/${newSessionId}`);
+    };

To verify the consumer reads and clears the scoped key:

#!/bin/bash
set -euo pipefail
echo "Search for consumer reading/removing initialMessage by sessionId..."
fd -t f --strip-cwd-prefix 'page.tsx' app | while read -r f; do
  echo "---- $f ----"
  rg -n -C2 -S "sessionStorage\\.(getItem|removeItem)\\(|initialMessage:" "$f" || true
done
🧹 Nitpick comments (7)
app/chat/page.tsx (2)

22-33: Make the recommendations grid responsive and DRY

Stack on small screens and render from data to reduce duplication.

-                <div className="grid grid-cols-2 gap-4 mb-8">
-                    <RecommendationCard
-                        title="List all tutorials"
-                        subtitle="available in Agentuity."
-                        onClick={() => handleMessageSend("List all tutorials available in Agentuity")}
-                    />
-                    <RecommendationCard
-                        title="How do I do agent handoff"
-                        subtitle="in the Python SDK?"
-                        onClick={() => handleMessageSend("How do I do agent handoff in the Python SDK?")}
-                    />
-                </div>
+                <div className="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
+                    {recommendations.map((r) => (
+                        <RecommendationCard
+                            key={r.title}
+                            title={r.title}
+                            subtitle={r.subtitle}
+                            onClick={() => handleMessageSend(r.message)}
+                        />
+                    ))}
+                </div>

Add near the top of ChatPage:

const recommendations = [
  {
    title: 'List all tutorials',
    subtitle: 'available in Agentuity.',
    message: 'List all tutorials available in Agentuity'
  },
  {
    title: 'How do I do agent handoff',
    subtitle: 'in the Python SDK?',
    message: 'How do I do agent handoff in the Python SDK?'
  }
] as const;

49-55: Add type="button" and focus-visible styles for a11y

Prevents unintended form submits and improves keyboard focus.

-        <button
-            onClick={onClick}
-            className="flex flex-col items-start p-3 rounded-lg text-left transition-all duration-200 hover:scale-[1.02] border"
-        >
+        <button
+            type="button"
+            onClick={onClick}
+            className="flex flex-col items-start p-3 rounded-lg text-left transition-all duration-200 hover:scale-[1.02] border
+                       focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40"
+        >
app/chat/[sessionId]/page.tsx (5)

171-171: Typo in class name: likely meant agentuity‑scrollbar.

Fixes inconsistent scrollbar class.

-    <div className="flex-1 flex flex-col min-w-0 h-full overflow-hidden overflow-y-auto uity-scrollbar relative">
+    <div className="flex-1 flex flex-col min-w-0 h-full overflow-hidden overflow-y-auto agentuity-scrollbar relative">

177-180: Add explicit type to button (a11y + behavior).

Prevents unintended form submission if nested in a form.

-            <button
+            <button
+              type="button"
               onClick={() => revalidateSessions?.()}
               className="px-2 py-0.5 text-xs rounded bg-red-500/30 hover:bg-red-500/40 border border-red-500/50"
             >

196-196: Pass setEditorOpen directly (simpler and typed).

Avoids wrapping and preserves the boolean argument.

-                  setEditorOpen={() => { setEditorOpen(true) }}
+                  setEditorOpen={setEditorOpen}

141-149: Remove unnecessary type assertions.

sessionId is already string via useParams generic.

-    const temporarySession: Session = {
-      sessionId: sessionId as string,
+    const temporarySession: Session = {
+      sessionId: sessionId,
@@
-    sessionService.createSession({
-      sessionId: sessionId as string,
+    sessionService.createSession({
+      sessionId: sessionId,

203-207: Avoid array index as key (minor).

Use stable keys for better reconciliation (even for skeletons).

Apply this diff and add the constant once (outside the component):

-                    {Array.from({ length: 5 }).map((_, i) => (
-                      <div key={i} className="space-y-2">
+                    {SKELETON_KEYS.map((k) => (
+                      <div key={k} className="space-y-2">
                         <Skeleton className="h-4 w-2/3" />
                         <Skeleton className="h-3 w-1/2" />
                       </div>
                     ))}

Add near the top-level (outside the component):

const SKELETON_KEYS = ['s1','s2','s3','s4','s5'] as const;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8dd208 and 92e4294.

📒 Files selected for processing (2)
  • app/chat/[sessionId]/page.tsx (1 hunks)
  • app/chat/page.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
app/chat/[sessionId]/page.tsx (6)
app/chat/types.ts (2)
  • Session (54-54)
  • Message (37-37)
app/chat/SessionContext.tsx (1)
  • useSessions (14-20)
app/chat/services/sessionService.ts (1)
  • sessionService (296-296)
app/chat/components/ChatMessagesArea.tsx (1)
  • ChatMessagesArea (15-70)
components/ui/skeleton.tsx (1)
  • Skeleton (7-14)
app/chat/components/CodeEditor.tsx (1)
  • CodeEditor (20-135)
app/chat/page.tsx (1)
app/chat/components/ChatInput.tsx (1)
  • ChatInput (12-74)
🪛 Biome (2.1.2)
app/chat/[sessionId]/page.tsx

[error] 114-114: This hook does not specify its dependency on handleSendMessage.

This dependency is being used here, but is not specified in the hook dependency list.

Unsafe fix: Add the missing dependency to the list.

(lint/correctness/useExhaustiveDependencies)


[error] 114-114: This hook does not specify its dependency on revalidateSessions.

This dependency is being used here, but is not specified in the hook dependency list.

This dependency is being used here, but is not specified in the hook dependency list.

Unsafe fix: Add the missing dependency to the list.

(lint/correctness/useExhaustiveDependencies)


[error] 114-114: This hook does not specify its dependency on sessions.find.

This dependency is being used here, but is not specified in the hook dependency list.

Unsafe fix: Add the missing dependency to the list.

(lint/correctness/useExhaustiveDependencies)


[error] 177-180: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)


[error] 203-203: Avoid using the index of an array as key property in an element.

This is the source of the key value.

The order of the items may change, and this also affects performances and component state.
Check the React documentation.

(lint/suspicious/noArrayIndexKey)

app/chat/page.tsx

[error] 49-52: Provide an explicit type prop for the button element.

The default type of a button is submit, which causes the submission of a form when placed inside a form element. This is likely not the behaviour that you want inside a React application.
Allowed button types are: submit, button or reset

(lint/a11y/useButtonType)

⏰ 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)
app/chat/[sessionId]/page.tsx (2)

70-80: Resolved — MessageSchema already includes optional tutorialData

app/chat/types.ts declares tutorialData: TutorialDataSchema.optional() on MessageSchema (Message = z.infer), so adding tutorialData to messages is type-safe.


217-226: Incorrect — CodeEditor accepts executingFiles.
app/chat/components/CodeEditor.tsx declares executingFiles: string[] in CodeEditorProps, so passing executingFiles from app/chat/[sessionId]/page.tsx is valid.

Likely an incorrect or invalid review comment.

@@ -0,0 +1,233 @@
'use client';

import { useEffect, useState } from 'react';
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix stale effect deps; add fallback load; clear error on mount.

Prevents stale closures (Biome errors), and avoids a “stuck skeleton” when no session is found and there’s no initialMessage. Also resets prior error state.

Apply this diff:

-import { useEffect, useState } from 'react';
+import { useEffect, useState, useCallback } from 'react';
@@
-  const handleSendMessage = async (content: string, sessionId: string) => {
+  const handleSendMessage = useCallback(async (content: string, sessionId: string) => {
     if (!content || !sessionId) return;
@@
-    }
-  };
+    }
+  }, [setSessions]);
@@
-  useEffect(() => {
+  useEffect(() => {
+    // reset any previous banner
+    setCreationError(null);
     const foundSession = sessions.find(s => s.sessionId === sessionId);
     if (foundSession) {
       setSession(foundSession);
       return;
     }
@@
-    const initialMessage = sessionStorage.getItem(storageKey);
-    if (!initialMessage) {
-      return;
-    }
+    const initialMessage = sessionStorage.getItem(storageKey);
+    if (!initialMessage) {
+      // no cached seed and not in context — try to refresh the global list
+      revalidateSessions?.();
+      return;
+    }
@@
-  }, [sessionId]);
+  }, [sessionId, sessions, revalidateSessions, handleSendMessage]);

Notes:

  • If sessionService exposes a getSession(sessionId) call, prefer fetching it here before calling revalidateSessions.

Also applies to: 24-112, 114-164

Comment on lines +50 to +101
await sessionService.addMessageToSessionStreaming(
sessionId,
newMessage,
{
onTextDelta: (textDelta) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg => {
if (msg.id === assistantMessage.id) {
return {
...msg,
content: msg.content + textDelta
};
}
return msg;
});
return { ...prev, messages: updatedMessages };
});
},

onTutorialData: (tutorialData) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, tutorialData: tutorialData }
: msg
);
return { ...prev, messages: updatedMessages };
});
},

onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
},

onError: (error) => {
console.error('Error sending message:', error);
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: 'Sorry, I encountered an error. Please try again.' }
: msg
);
return { ...prev, messages: updatedMessages };
});
}
}
);

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider cancelation/cleanup for streaming to avoid setState after unmount.

If the component unmounts mid‑stream, callbacks can attempt state updates.

Pattern to adopt (if sessionService supports AbortSignal or a cancel handle):

+  const inFlight = useRef<AbortController[]>([]);
   const handleSendMessage = useCallback(async (content: string, sessionId: string) => {
+    const ac = new AbortController();
+    inFlight.current.push(ac);
     try {
       await sessionService.addMessageToSessionStreaming(
         sessionId,
         newMessage,
-        {
+        {
+          signal: ac.signal,
           onTextDelta: ...
         }
       );
     } finally {
+      inFlight.current = inFlight.current.filter(c => c !== ac);
     }
   }, [setSessions]);
+
+  useEffect(() => {
+    return () => {
+      inFlight.current.forEach(c => c.abort());
+      inFlight.current = [];
+    };
+  }, []);

If the API doesn’t accept signal, expose and use a cancel function instead.

I can adapt this to your actual sessionService API.

📝 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
await sessionService.addMessageToSessionStreaming(
sessionId,
newMessage,
{
onTextDelta: (textDelta) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg => {
if (msg.id === assistantMessage.id) {
return {
...msg,
content: msg.content + textDelta
};
}
return msg;
});
return { ...prev, messages: updatedMessages };
});
},
onTutorialData: (tutorialData) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, tutorialData: tutorialData }
: msg
);
return { ...prev, messages: updatedMessages };
});
},
onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
},
onError: (error) => {
console.error('Error sending message:', error);
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: 'Sorry, I encountered an error. Please try again.' }
: msg
);
return { ...prev, messages: updatedMessages };
});
}
}
);
const inFlight = useRef<AbortController[]>([]);
const handleSendMessage = useCallback(async (content: string, sessionId: string) => {
const ac = new AbortController();
inFlight.current.push(ac);
try {
await sessionService.addMessageToSessionStreaming(
sessionId,
newMessage,
{
signal: ac.signal,
onTextDelta: (textDelta) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg => {
if (msg.id === assistantMessage.id) {
return {
...msg,
content: msg.content + textDelta
};
}
return msg;
});
return { ...prev, messages: updatedMessages };
});
},
onTutorialData: (tutorialData) => {
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, tutorialData: tutorialData }
: msg
);
return { ...prev, messages: updatedMessages };
});
},
onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
},
onError: (error) => {
console.error('Error sending message:', error);
setSession(prev => {
if (!prev) return prev;
const updatedMessages = prev.messages.map(msg =>
msg.id === assistantMessage.id
? { ...msg, content: 'Sorry, I encountered an error. Please try again.' }
: msg
);
return { ...prev, messages: updatedMessages };
});
}
}
);
} finally {
inFlight.current = inFlight.current.filter(c => c !== ac);
}
}, [setSessions]);
useEffect(() => {
return () => {
inFlight.current.forEach(c => c.abort());
inFlight.current = [];
};
}, []);
🤖 Prompt for AI Agents
In app/chat/[sessionId]/page.tsx around lines 50 to 101, the streaming callbacks
can call setState after the component unmounts; create an AbortController (or
obtain a cancel handle) before calling
sessionService.addMessageToSessionStreaming, pass its signal or cancel handle
into the API if supported, and add a cleanup in the enclosing useEffect to call
controller.abort() (or the cancel function) so streaming callbacks stop;
additionally, inside each callback (onTextDelta, onTutorialData, onFinish,
onError) bail out early if the signal is aborted (or a mounted flag is false) to
avoid calling setState after unmount and ensure the service’s returned cancel
function (if any) is invoked on cleanup.

Comment on lines +82 to +85
onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
},
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

Ensure sessions list updates even if session isn’t present.

Currently map won’t add a new session that isn’t already in the array.

Apply this diff:

-          onFinish: (finalSession) => {
-            setSession(finalSession);
-            setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
-          },
+          onFinish: (finalSession) => {
+            setSession(finalSession);
+            setSessions(prev => {
+              const idx = prev.findIndex(s => s.sessionId === sessionId);
+              if (idx === -1) return [...prev, finalSession];
+              const next = prev.slice();
+              next[idx] = finalSession;
+              return next;
+            });
+          },
📝 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
onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => prev.map(s => s.sessionId === sessionId ? finalSession : s));
},
onFinish: (finalSession) => {
setSession(finalSession);
setSessions(prev => {
const idx = prev.findIndex(s => s.sessionId === sessionId);
if (idx === -1) return [...prev, finalSession];
const next = prev.slice();
next[idx] = finalSession;
return next;
});
},
🤖 Prompt for AI Agents
In app/chat/[sessionId]/page.tsx around lines 82-85, the onFinish handler only
maps existing sessions so it silently fails to add a session that isn’t present;
change the sessions updater to perform an upsert: use the functional setter to
check if prev contains a session with the same sessionId and if so return
prev.map(...) replacing it, otherwise return a new array that adds finalSession
(e.g., prepend or append) so the sessions list is updated whether or not the
session already exists; keep setSession(finalSession) as-is.

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)
package.json (3)

26-26: Remove unnecessary @types/uuid (uuid v11 includes types).
Leaving it can cause type conflicts in TS projects.

Apply:

-    "@types/uuid": "^10.0.0",

38-38: Node-only dependency: ensure node-pty never reaches Edge/client bundles.
Keep usage strictly server-only (dynamic import, server folder) and mark API routes with export const runtime = 'nodejs'.


45-45: Ensure ESM import style for uuid everywhere.
Use import { v4 as uuidv4 } from "uuid"; and no require('uuid').

#!/bin/bash
rg -nP "require\\(['\"]uuid['\"]\\)" -g "**/*.{ts,tsx,js,jsx}" -n || true
rg -nP "import\\s+uuid\\s+from\\s+['\"]uuid['\"]" -g "**/*.{ts,tsx,js,jsx}" -n || true
🧹 Nitpick comments (2)
package.json (2)

1-22: Optional: declare supported Node.js engines to reduce CI/Dev drift.
Align with Next 15.x requirement (>=18.18) or your chosen runtime (e.g., 20/22).

 {
   "name": "docs",
   "version": "1.0.0",
   "private": true,
+  "engines": {
+    "node": ">=18.18.0"
+  },
   "scripts": {

Also applies to: 37-41


63-63: Override format: prefer a caret or pin
Verified package.json .overrides.esbuild == ">=0.25.0" (npm ls shows no esbuild resolved). Use a caret-compatible range or pin to avoid tooling ambiguity.

-    "esbuild": ">=0.25.0"
+    "esbuild": "^0.25.0"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2cdb42 and a2ffc61.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (1)
  • package.json (3 hunks)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (1)
package.json (1)

58-58: LGTM: add ts-node for scripts/tooling.
Matches typical usage for local scripts and TS helpers.

Comment on lines +37 to 41
"next": "^15.5.2",
"node-pty": "^1.0.0",
"react": "^19.1.1",
"react-dom": "^19.1.1",
"react-markdown": "^10.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Next 15.5.2 + React 19.1.1: sanity-check peer deps and build.
Run to confirm clean graph/build.


🏁 Script executed:

#!/bin/bash
set -e
npm ls next react react-dom || true
npm run build --silent || true

Length of output: 1000


Build fails — missing 'gray-matter' required by docs script

npm run build errors with MODULE_NOT_FOUND: 'gray-matter' (require stack: scripts/generate-docs-json.js). Add "gray-matter" to package.json (devDependencies) or make the script optional/guarded, then run npm install and re-run the build to confirm Next 15.5.2 + React 19.1.1 build/peer-dep sanity. (Location: scripts/generate-docs-json.js + package.json.)

🤖 Prompt for AI Agents
In package.json around lines 37 to 41 and scripts/generate-docs-json.js, the
build fails because the docs generation script requires the 'gray-matter'
package but it is not declared; add "gray-matter" to package.json (preferably
under devDependencies) with an appropriate version, run npm install (or yarn) to
install it, or alternatively modify scripts/generate-docs-json.js to guard the
require (try/catch or optional import) and make the docs step optional during
build; after making the change, run npm run build to confirm the error is
resolved and Next 15.5.2 + React 19.1.1 build/peer-dep sanity.

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 (2)
app/api/sessions/[sessionId]/messages/route.ts (2)

76-80: Guarantee a timestamp for user messages.

If message.timestamp is absent, sessions will store undefined. Set it to now.

Apply this diff:

-    // Ensure timestamp is in ISO string format
-    if (message.timestamp) {
-      message.timestamp = toISOString(message.timestamp);
-    }
+    // Ensure timestamp is set and in ISO string format
+    if (message.timestamp) {
+      message.timestamp = toISOString(message.timestamp);
+    } else {
+      message.timestamp = getCurrentTimestamp();
+    }

254-323: SSE parsing can drop data across chunk boundaries and ‘finish’ may persist multiple times; rework with buffering + single‑finish gate and concurrency‑safe persist.

Current split-by-newline parsing can truncate JSON across chunks; finish block can run more than once and uses stale updatedSession, overwriting concurrent updates. Buffer SSE lines, gate finish, and re-fetch/merge before persisting.

Apply this diff:

-    // Process streaming response
-    let accumulatedContent = "";
-    let finalTutorialData: TutorialData | undefined = undefined;
-
-    const transformStream = new TransformStream({
-      async transform(chunk, controller) {
-        // Forward the chunk to the client
-        controller.enqueue(chunk);
-
-        // Process the chunk to accumulate the full response
-        const text = new TextDecoder().decode(chunk);
-        const lines = text.split("\n");
-
-        for (const line of lines) {
-          if (line.startsWith("data: ")) {
-            try {
-              const data = JSON.parse(line.slice(6)) as StreamingChunk;
-
-              if (data.type === "text-delta" && data.textDelta) {
-                accumulatedContent += data.textDelta;
-              } else if (data.type === "tutorial-data" && data.tutorialData) {
-                finalTutorialData = data.tutorialData;
-                
-                // Update user's tutorial progress
-                await TutorialStateManager.updateTutorialProgress(
-                  userId,
-                  finalTutorialData.tutorialId,
-                  finalTutorialData.currentStep,
-                  finalTutorialData.totalSteps
-                );
-              } else if (data.type === "finish") {
-                // When the stream is finished, save the assistant message
-                const assistantMessage: Message = {
-                  id: assistantMessageId,
-                  author: "ASSISTANT",
-                  content: accumulatedContent,
-                  timestamp: getCurrentTimestamp(),
-                  tutorialData: finalTutorialData,
-                };
-
-                const finalSession = {
-                  ...updatedSession,
-                  messages: [...updatedSession.messages, assistantMessage],
-                };
-
-                await setKVValue(sessionKey, finalSession, {
-                  storeName: config.defaultStoreName,
-                });
-
-                // Trigger background title generation if missing
-                // Do not await to avoid delaying the client stream completion
-                void generateAndPersistTitle(sessionId, sessionKey, finalSession);
-
-                // Send the final session in the finish event
-                controller.enqueue(
-                  new TextEncoder().encode(
-                    `data: ${JSON.stringify({
-                      type: "finish",
-                      session: finalSession,
-                    })}\n\n`
-                  )
-                );
-              }
-            } catch (error) {
-              console.error("Error processing stream chunk:", error);
-            }
-          }
-        }
-      },
-    });
+    // Process streaming response
+    let accumulatedContent = "";
+    let finalTutorialData: TutorialData | undefined = undefined;
+    let finishedSent = false;
+    const sseDecoder = new TextDecoder();
+    let sseBuffer = "";
+
+    const transformStream = new TransformStream({
+      async transform(chunk, controller) {
+        // Forward original chunk
+        controller.enqueue(chunk);
+
+        // Buffer-aware SSE parsing
+        sseBuffer += sseDecoder.decode(chunk, { stream: true });
+        const lines = sseBuffer.split("\n");
+        sseBuffer = lines.pop() ?? ""; // keep last partial
+
+        for (const line of lines) {
+          if (!line.startsWith("data: ")) continue;
+          try {
+            const data = JSON.parse(line.slice(6)) as StreamingChunk;
+
+            if (data.type === "text-delta" && data.textDelta) {
+              accumulatedContent += data.textDelta;
+            } else if (data.type === "tutorial-data" && data.tutorialData) {
+              finalTutorialData = data.tutorialData;
+              // Update user's tutorial progress (await is fine; low rate events)
+              await TutorialStateManager.updateTutorialProgress(
+                userId,
+                finalTutorialData.tutorialId,
+                finalTutorialData.currentStep,
+                finalTutorialData.totalSteps
+              );
+            } else if (data.type === "finish" && !finishedSent) {
+              finishedSent = true;
+              // Build assistant message
+              const assistantMessage: Message = {
+                id: assistantMessageId,
+                author: "ASSISTANT",
+                content: accumulatedContent,
+                timestamp: getCurrentTimestamp(),
+                tutorialData: finalTutorialData,
+              };
+              // Merge with latest before persisting (avoid overwriting concurrent updates)
+              const latest = await getKVValue<Session>(sessionKey, { storeName: config.defaultStoreName });
+              const base = latest.success && latest.data ? latest.data : updatedSession;
+              const alreadyExists = base.messages.some(m => m.id === assistantMessageId);
+              const finalSession: Session = alreadyExists
+                ? base
+                : { ...base, messages: [...base.messages, assistantMessage] };
+
+              await setKVValue(sessionKey, finalSession, { storeName: config.defaultStoreName });
+              // Background title generation (non-blocking)
+              void generateAndPersistTitle(sessionId, sessionKey, finalSession);
+
+              // Send the final session in a single finish event
+              controller.enqueue(
+                new TextEncoder().encode(
+                  `data: ${JSON.stringify({ type: "finish", session: finalSession })}\n\n`
+                )
+              );
+            }
+          } catch (error) {
+            console.error("Error processing stream chunk:", error);
+          }
+        }
+      },
+      async flush(controller) {
+        // Process any remaining buffered partial line
+        if (!sseBuffer) return;
+        for (const line of sseBuffer.split("\n")) {
+          if (!line.startsWith("data: ")) continue;
+          try {
+            const data = JSON.parse(line.slice(6)) as StreamingChunk;
+            if (data.type === "text-delta" && data.textDelta) {
+              accumulatedContent += data.textDelta;
+            } else if (data.type === "tutorial-data" && data.tutorialData) {
+              finalTutorialData = data.tutorialData;
+            }
+          } catch {
+            /* ignore */
+          }
+        }
+      },
+    });
🧹 Nitpick comments (4)
app/api/sessions/[sessionId]/messages/route.ts (4)

331-350: Harden stream error handling: await abort to avoid dangling streams.

Await writer.abort and guard it to prevent masking the original error.

Apply this diff:

-      } catch (error) {
-        console.error('Error in stream processing:', error);
-        writer.abort(error);
-      }
+      } catch (error) {
+        console.error('Error in stream processing:', error);
+        try {
+          await writer.abort(error as any);
+        } catch (abortErr) {
+          console.error('Error aborting transform stream:', abortErr);
+        }
+      }

27-33: Fix Next.js route params typing (not a Promise).

params isn’t a Promise; incorrect typing can confuse tooling.

Apply this diff:

-export async function POST(
-  request: NextRequest,
-  { params }: { params: Promise<{ sessionId: string }> }
-) {
+export async function POST(
+  request: NextRequest,
+  { params }: { params: { sessionId: string } }
+) {

65-67: Remove unnecessary await on params and simplify extraction.

Apply this diff:

-    const paramsData = await params;
-    const sessionId = paramsData.sessionId;
+    const { sessionId } = params;

101-103: Satisfy linter: avoid template literal without interpolation.

Use a plain string with \n escapes.

Apply this diff:

-        const prompt = `Generate a very short session title summarizing the conversation topic.\n\nRequirements:\n- sentence case\n- no emojis\n- <= 60 characters\n- no quotes or markdown\n- output the title only, no extra text`;
+        const prompt = 'Generate a very short session title summarizing the conversation topic.\n\nRequirements:\n- sentence case\n- no emojis\n- <= 60 characters\n- no quotes or markdown\n- output the title only, no extra text';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a2ffc61 and 6d3264a.

📒 Files selected for processing (1)
  • app/api/sessions/[sessionId]/messages/route.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/api/sessions/[sessionId]/messages/route.ts (5)
lib/validation/middleware.ts (2)
  • parseAndValidateJSON (35-61)
  • SessionMessageRequestSchema (73-76)
app/chat/utils/dateUtils.ts (2)
  • toISOString (10-16)
  • getCurrentTimestamp (35-37)
lib/kv-store.ts (2)
  • getKVValue (47-113)
  • setKVValue (122-170)
lib/env.ts (1)
  • getAgentPulseConfig (32-34)
lib/tutorial/state-manager.ts (1)
  • TutorialStateManager (5-121)
🪛 Biome (2.1.2)
app/api/sessions/[sessionId]/messages/route.ts

[error] 101-103: Do not use template literals if interpolation and special-character handling are not needed.

Safe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (1)
app/api/sessions/[sessionId]/messages/route.ts (1)

74-75: Enforce server-side author = USER (don’t trust client input)

Reject non-USER authors — a client can inject ASSISTANT messages and pollute sessions.

File: app/api/sessions/[sessionId]/messages/route.ts (around lines 74–75)

     const { message, processWithAgent } = validation.data;
+    if (message.author !== "USER") {
+      return NextResponse.json({ error: "Only USER messages are accepted here" }, { status: 400 });
+    }

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

This PR is being reviewed by Cursor Bugbot

Details

Your team is on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle for each member of your team.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

) {
try {
const result = await client.chat.completions.create({
model: 'gpt-5-mini',
Copy link

Choose a reason for hiding this comment

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

Bug: Invalid Model Name Causes API Call Failures

The gpt-5-mini model name is invalid for OpenAI API calls. This model does not exist, which causes agent requests to fail.

Additional Locations (1)

Fix in Cursor Fix in Web

{currentStepData.title}
</h2>
<p className="text-sm text-white/60">
Step {tutorial.currentStep + 1} of {tutorial.totalSteps}
Copy link

Choose a reason for hiding this comment

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

Bug: Tutorial Completion Logic Fails Due to Indexing Mismatch

The agent's tutorial completion logic (tutorialState.currentStep > currentTutorial.totalSteps) has an off-by-one error, as it doesn't correctly identify completion when currentStep matches totalSteps. This stems from an inconsistent step indexing approach across the codebase, where some components use 0-indexed steps internally while others expect 1-indexed, leading to potential logic errors.

Additional Locations (2)

Fix in Cursor Fix in Web

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

Caution

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

⚠️ Outside diff range comments (1)
examples/tutorial_hello_world_py/pyproject.toml (1)

1-10: Add a build-system so users can pip install . this example.

Without a [build-system], packaging/installation via pip may fail (no legacy setup.py here).

Apply this addition at the end of the file:

+[build-system]
+requires = ["setuptools>=69", "wheel"]
+build-backend = "setuptools.build_meta"
🧹 Nitpick comments (48)
examples/tutorial_hello_world_py/agentuity.yaml (3)

17-35: Dev server: verify env files, watch path, and port usage.

  • Ensure .env and .env.development exist and are not committed or bundled.
  • Confirm agentuity_agents/** is the intended watch root.
  • Check that PORT collisions are handled during parallel tutorial runs.

36-53: Deployment: validate server.py path and resource sizing.

  • Confirm server.py resolution matches working directory in your deploy target and that uv is available in the runtime.
  • Double‑check 250Mi/500m/300Mi are sufficient under expected load; on‑demand mode aligns with tutorial usage.

54-63: Bundler: confirm ignores and directory layout.

  • Ensure bundler ignores (and repo .gitignore) already exclude virtualenvs, build artifacts, and any env files so they’re not packaged.
  • Verify agents dir agentuity_agents matches your actual layout.
examples/tutorial_hello_world_py/README.md (4)

6-6: Add alt text to the deploy button image for accessibility (MD045).

The second image tag lacks alt text. Add a short, descriptive alt.

-            <img src="https://app.agentuity.com/img/deploy.svg" /> 
+            <img src="https://app.agentuity.com/img/deploy.svg" alt="Deploy to Agentuity" />

72-79: Specify a language on the fenced code block (MD040).

Add a language hint to improve readability and satisfy markdownlint.

-```
+```text
 ├── agents/             # Agent definitions and implementations
 ├── .venv/              # Virtual environment (created by UV)
 ├── pyproject.toml      # Project dependencies and metadata
 ├── server.py           # Server entry point
 └── agentuity.yaml      # Agentuity project configuration

---

`19-21`: **Use canonical casing “uv” instead of “UV”.**

Astral styles the tool name lowercase. Align casing in prerequisites and comments.


```diff
-- **UV**: Version 0.5.25 or higher ([Documentation](https://docs.astral.sh/uv/))
+- **uv**: Version 0.5.25 or higher ([Documentation](https://docs.astral.sh/uv/))

-├── .venv/              # Virtual environment (created by UV)
+├── .venv/              # Virtual environment (created by uv)

Also applies to: 74-74


44-59: Include a one-time dependency install step before running in dev mode.

Without installing deps, uv run server.py may fail in fresh clones. Add uv sync (or equivalent) to the Getting Started flow.

 ### Development Mode

-Run your project in development mode with:
+Install dependencies, then run your project in development mode:
 
 ```bash
+uv sync
 agentuity dev

This will start your project and open a new browser window connecting your agent to the Agentuity Console in DevMode, allowing you to test and debug your agent in real-time.

You can also start your project in development mode without connecting to the Agentuity Console:

uv run server.py

I can open a follow-up PR to apply these edits if you’d like.

</blockquote></details>
<details>
<summary>examples/tutorial_hello_world_py/server.py (5)</summary><blockquote>

`1-1`: **Import `autostart` lazily after env checks; wrap startup in `try/except`.**

Prevents side effects if the import touches env/state and gives a cleaner failure path.



```diff
-from agentuity import autostart
+# Defer importing Agentuity until after env validation

@@
-    autostart()
+    from agentuity import autostart
+    try:
+        autostart()
+    except Exception:
+        logging.exception("Agent startup failed")
+        sys.exit(1)

Also applies to: 36-36


7-13: Error message mentions only SDK key while the check accepts API or SDK key.

Make the guidance accurate and send to stderr.

-        print(
-            "\033[31m[ERROR] AGENTUITY_SDK_KEY is not set. This should have been set automatically by the Agentuity CLI or picked up from the .env file.\033[0m"
-        )
+        print(
+            "\033[31m[ERROR] Neither AGENTUITY_API_KEY nor AGENTUITY_SDK_KEY is set. Set one in your environment or in .env.\033[0m",
+            file=sys.stderr,
+        )

14-17: Avoid brittle uv detection via _; show the hint whenever .env exists and tailor the script name.

-        if os.environ.get("_", "").endswith("uv") and os.path.exists(".env"):
-            print(
-                "\033[31m[ERROR] Re-run the command with `uv run --env-file .env server.py`\033[0m"
-            )
+        if os.path.exists(".env"):
+            print(
+                f"\033[31m[ERROR] Tip: If using uv, re-run with `uv run --env-file .env {os.path.basename(sys.argv[0])}`\033[0m",
+                file=sys.stderr,
+            )

21-27: Use WARN color for non-fatal messages and write to stderr.

Red (31m) reads as error; switch to yellow (33m) and stderr.

-        print(
-            "\033[31m[WARN] You are running this agent outside of the Agentuity environment. Any automatic Agentuity features will be disabled.\033[0m"
-        )
+        print(
+            "\033[33m[WARN] You are running this agent outside of the Agentuity environment. Automatic Agentuity features will be disabled.\033[0m",
+            file=sys.stderr,
+        )
@@
-        print(
-            "\033[31m[WARN] Recommend running `agentuity dev` to run your project locally instead of `python script`.\033[0m"
-        )
+        print(
+            f"\033[33m[WARN] Recommend running `agentuity dev` to run your project locally instead of `python {os.path.basename(sys.argv[0])}`.\033[0m",
+            file=sys.stderr,
+        )

29-34: Don’t truncate level names in logs.

%(levelname)-5.5s clips “WARNING” to “WARNI”. Use full name.

     logging.basicConfig(
         stream=sys.stdout,
         level=logging.INFO,
-        format="[%(levelname)-5.5s] %(message)s",
+        format="[%(levelname)s] %(message)s",
     )
examples/tutorial_hello_world_py/pyproject.toml (3)

4-4: Capitalize “Python” in description.

Minor polish for user‑facing text.

-description = "A python version of a hello world agent"
+description = "A Python version of a hello world agent"

5-5: Re-evaluate the Python upper bound (<3.13).

Python 3.13 has been released; if the example works on 3.13, widening the range reduces friction for users.

Please confirm by running the tutorial on 3.13 and, if green, update to:

-requires-python = ">=3.10, <3.13"
+requires-python = ">=3.10, <3.14"

6-9: Constrain dependency ranges to avoid surprise breakage.

  • Pre‑1.0 libraries can introduce breaking changes in any release; bound agentuity below the next minor.
  • Add an upper bound for openai to guard against a future 2.x breaking release.
 dependencies = [
-    "agentuity>=0.0.106",
-    "openai>=1.107.0",
+    "agentuity>=0.0.106,<0.1.0",
+    "openai>=1.107.0,<2.0.0",
 ]
content/meta.json (1)

5-5: Nav label/slug check for “Tutorial”.

Confirm the site generator resolves this page key to content/Tutorial/meta.json (case‑sensitive on some hosts). If multiple tutorials are planned, consider “Tutorials” for clarity.

content/Tutorial/poc-multi/step2.mdx (1)

1-16: Add a quick “verify you’re logged in” step.

After login, add a one‑liner to verify auth (e.g., agentuity whoami or agentuity auth status) so users can confirm before proceeding.

Apply this diff to insert a verification block:

  If you're not already logged in, run:
  ```bash
  agentuity auth login

+To verify you're authenticated, run:
+bash +agentuity whoami +


</blockquote></details>
<details>
<summary>content/Tutorial/poc-multi/step1.mdx (3)</summary><blockquote>

`2-4`: **Title/description: clarify we create an Agent (and project).**

If the recommended flow is `agent create`, tweak title/description accordingly.

Apply:

```diff
-title: Create a new project
-description: Run the interactive project generator to scaffold a new Agentuity project
+title: Create a new agent and project
+description: Use the interactive generator to scaffold a new Agentuity agent (and project)

8-11: Use the preferred CLI command.

Switch from project create to agent create to match current guidance.

-```bash
-agentuity project create
-```
+```bash
+agentuity agent create
+```

If both commands exist, add a note indicating which is preferred and why.


16-20: Prereq nudge for toolchains.

Add brief install links/notes for Bun and uv to reduce setup friction.

 - Language/runtime:
   - TypeScript (Bun) or
   - Python (uv)
+  (Ensure Bun and/or uv are installed before proceeding.)
examples/tutorial_hello_world_py/.gitignore (5)

130-139: Add local env variants for completeness

Consider ignoring common local env variants used by dev tooling.

Apply this diff:

 .env
 .env.development
 .env.production
+.env.local
+.env.*.local
 .venv
 env/
 venv/

17-19: Risk: ignoring lib/ and lib64/ can hide source

If any example code lives under lib/ or lib64/, it would be excluded from VCS. If you’re not producing those dirs as build artifacts, drop these patterns.

Apply this diff if appropriate:

-lib/
-lib64/

6-8: Include Windows C-extension artifacts

Add *.pyd alongside *.so to cover Windows builds.

Apply this diff:

 *.so
+*.pyd

78-80: Consistency: treat ipynb checkpoints as a directory

Minor consistency tweak.

Apply this diff:

-.ipynb_checkpoints
+.ipynb_checkpoints/

165-171: Optional: ignore common editor/OS noise in examples

To keep example PRs clean, explicitly ignore IDE and OS files.

Apply this diff:

-#.idea/
+# Editors/OS
+.idea/
+.vscode/
+.DS_Store
examples/tutorial_hello_world_py/main.py (3)

1-1: Add return type and a brief docstring for clarity.

Tiny polish that helps static tooling and readers.

-def main():
+def main() -> None:
+    """Entrypoint for the Python hello-world tutorial."""

1-1: Optional: Add a shebang to allow direct execution.

Handy when making the file executable.

+#!/usr/bin/env python3
 def main():

2-2: Standardize example name (hyphen vs underscore)

Mixed usages found — directory is examples/tutorial_hello_world_py but metadata and the greeting use tutorial-hello-world-py. Choose one convention; minimal fix is to update the greeting in main.py:

-    print("Hello from tutorial-hello-world-py!")
+    print("Hello from tutorial_hello_world_py!")

Occurrences: examples/tutorial_hello_world_py/main.py, examples/tutorial_hello_world_py/pyproject.toml, examples/tutorial_hello_world_py/uv.lock, examples/tutorial_hello_world_py/agentuity.yaml, content/Tutorial/poc-multi/step4.mdx.

examples/tutorial-hello-world/src/agents/agent-hello/index.ts (2)

34-35: Harden input fallback for empty strings.

?? won’t catch ''. Trim and use || so empty/whitespace falls back.

Apply:

-          content: (await req.data.text()) ?? 'Hello, OpenAI',
+          content: ((await req.data.text())?.trim() || 'Hello, OpenAI'),

29-37: Make model configurable; consider cancellation.

  • Parameterize the model via env for easier local/testing overrides.
  • If AgentContext exposes an AbortSignal, pass it to the SDK to support request cancellation.

Apply:

-      model: 'gpt-5-mini',
+      model: process.env.OPENAI_MODEL ?? 'gpt-5-mini',

If an abort signal is available (e.g., ctx.signal), wire it in per the OpenAI SDK’s request options. Please confirm the available field on AgentContext.

examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py (5)

1-5: Initialize client with explicit timeout/retries and centralize model config.

Prevents hangs and eases model swaps across environments.

Apply this diff:

 from agentuity import AgentRequest, AgentResponse, AgentContext
 from openai import AsyncOpenAI
+import os
 
-client = AsyncOpenAI()
+DEFAULT_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini")
+client = AsyncOpenAI(timeout=30.0, max_retries=2)

21-33: Sanitize user input and set temperature.

Guarantees a non-empty prompt and more deterministic replies.

Apply this diff:

 async def run(request: AgentRequest, response: AgentResponse, context: AgentContext):
     try:
-        result = await client.chat.completions.create(
-            model="gpt-5-mini",
-            messages=[
+        raw = await request.data.text()
+        user = raw.strip() if raw and raw.strip() else "Hello, OpenAI"
+        result = await client.chat.completions.create(
+            model=DEFAULT_MODEL,
+            temperature=0.2,
+            messages=[
                     {
                     "role": "system",
                     "content": "You are a helpful assistant that provides concise and accurate information.",
                 },
                 {
                     "role": "user",
-                    "content": await request.data.text() or "Hello, OpenAI",
+                    "content": user,
                 },
             ],
         )

37-37: Guard against empty choices to avoid IndexError/None.

Minor robustness improvement.

Apply this diff:

-        return response.text(result.choices[0].message.content)
+        content = result.choices[0].message.content if getattr(result, "choices", None) else None
+        return response.text(content or "Sorry, no content returned from the model.")

38-41: Don’t catch blind Exception; log traceback.

Use exception logging to preserve stack traces. Optionally add specific OpenAI exceptions.

Apply this diff:

-    except Exception as e:
-        context.logger.error(f"Error running agent: {e}")
-
-        return response.text("Sorry, there was an error processing your request.")
+    except Exception:
+        context.logger.exception("Error running agent")
+        return response.text("Sorry, there was an error processing your request.")

Optional (if you want narrower handling), also import and catch OpenAI errors:

-from openai import AsyncOpenAI
+from openai import AsyncOpenAI, APIError, RateLimitError, APITimeoutError
@@
-    except Exception:
+    except (RateLimitError, APITimeoutError) as e:
+        context.logger.warning("Transient OpenAI error: %s", e)
+        return response.text("The service is busy. Please try again in a moment.")
+    except APIError:
+        context.logger.exception("OpenAI API error")
+        return response.text("Upstream error. Please try again later.")
+    except Exception:
         context.logger.exception("Error running agent")
         return response.text("Sorry, there was an error processing your request.")

21-35: Optional: align with SSE streaming.

If your tutorial expects token streaming, switch to the streaming API here and forward deltas over your SSE response handler.

Happy to provide a minimal streaming variant tailored to your AgentResponse if you share its streaming interface.

examples/tutorial-hello-world/tsconfig.json (4)

20-21: Avoid restricting global type resolution to SDK; keep only Bun types in "types".

Specifying "types" narrows global type inclusions. Including "@agentuity/sdk" here isn’t needed (SDK types are imported per‑module) and can mask other ambient types. Keep Bun types only.

Apply this diff:

-    "types": ["@types/bun", "@agentuity/sdk"],
+    "types": ["@types/bun"],

4-4: Disable allowJs for a TS‑only tutorial to catch drift early.

If no .js files are intended, set allowJs to false.

-    "allowJs": true,
+    "allowJs": false,

6-6: Drop JSX setting if React isn’t used in this example.

Removes noise from editor/tooling.

-    "jsx": "react-jsx",
+    // "jsx": "react-jsx",

15-16: Consider enabling unused checks once the sample stabilizes.

Helpful for learners and CI.

-    "noUnusedLocals": false,
-    "noUnusedParameters": false,
+    "noUnusedLocals": true,
+    "noUnusedParameters": true,
examples/tutorial-hello-world/package.json (1)

5-6: Remove misleading "main"/"module" fields; rely on bundler output via scripts.

This app runs via .agentuity/index.js; keeping "main": "index.js" (nonexistent) and "module": "index.ts" is confusing.

   "version": "0.0.1",
-  "main": "index.js",
   "type": "module",
@@
-  "module": "index.ts"
+  "engines": {
+    "bun": ">=1.2.4"
+  }

Also applies to: 34-34

examples/tutorial-hello-world/README.md (2)

6-6: Add alt text to image for a11y (MD045).

-            <img src="https://app.agentuity.com/img/deploy.svg" /> 
+            <img src="https://app.agentuity.com/img/deploy.svg" alt="Deploy to Agentuity" />

65-70: Specify a language on the fenced code block (MD040).

-```
+```text
 ├── agents/             # Agent definitions and implementations
 ├── node_modules/       # Dependencies
 ├── package.json        # Project dependencies and scripts
 └── agentuity.yaml      # Agentuity project configuration

</blockquote></details>
<details>
<summary>examples/tutorial-hello-world/index.ts (4)</summary><blockquote>

`3-9`: **Make process.isBun optional in type augmentation.**

At runtime this may be undefined under Node; reflect that in types.

```diff
-    interface Process {
-      isBun: boolean;
-    }
+    interface Process {
+      isBun?: boolean;
+    }

11-15: Error text should reflect both accepted env vars.

The guard allows AGENTUITY_API_KEY or AGENTUITY_SDK_KEY; message mentions only one.

-    '\x1b[31m[ERROR] AGENTUITY_SDK_KEY is not set. This should have been set automatically by the Agentuity CLI or picked up from the .env file.\x1b[0m'
+    '\x1b[31m[ERROR] AGENTUITY_API_KEY or AGENTUITY_SDK_KEY is not set. This should be set by the Agentuity CLI or picked up from the .env file.\x1b[0m'

28-41: Use yellow for warnings and tailor suggestion text.

Improve severity signaling and keep guidance consistent.

-  console.warn(
-    '\x1b[31m[WARN] You are running this agent outside of the Agentuity environment. Any automatic Agentuity features will be disabled.\x1b[0m'
-  );
+  console.warn(
+    '\x1b[33m[WARN] Running outside the Agentuity environment; automatic features are disabled.\x1b[0m'
+  );
@@
-    console.warn(
-      '\x1b[31m[WARN] Recommend running `agentuity dev` to run your project locally instead of `bun run start`.\x1b[0m'
-    );
+    console.warn(
+      '\x1b[33m[WARN] Recommend `agentuity dev` instead of `bun run start`.\x1b[0m'
+    );
@@
-    console.warn(
-      '\x1b[31m[WARN] Recommend running `agentuity dev` to run your project locally instead of `npm start`.\x1b[0m'
-    );
+    console.warn(
+      '\x1b[33m[WARN] Recommend `agentuity dev` instead of `npm start`.\x1b[0m'
+    );

43-52: Use a portable directory reference; import.meta.dirname isn’t standard.

Bun provides import.meta.dir; Node has import.meta.url. Use a fallback.

-runner(true, import.meta.dirname).catch((err) => {
+runner(true, (import.meta as any).dir ?? new URL('.', import.meta.url).pathname).catch((err) => {
examples/tutorial-hello-world/AGENTS.md (2)

7-12: Unify CLI wording: prefer a single verb (“agent new”).

Elsewhere (README) uses agentuity agent new. Keep it consistent.

-- Prefer using the `agentuity agent create` command to create a new Agent
+- Prefer using the `agentuity agent new` command to create a new Agent

50-51: Fix method signature typo.

Add parentheses to object<T>().

-- `request.data.object<T>: Promise<T>`: Gets the payload as a typed object
+- `request.data.object<T>(): Promise<T>`: Gets the payload as a typed object
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d3264a and 6d70c4e.

⛔ Files ignored due to path filters (2)
  • examples/tutorial-hello-world/bun.lock is excluded by !**/*.lock
  • examples/tutorial_hello_world_py/uv.lock is excluded by !**/*.lock
📒 Files selected for processing (30)
  • content/Tutorial/meta.json (1 hunks)
  • content/Tutorial/poc-multi/index.mdx (1 hunks)
  • content/Tutorial/poc-multi/meta.json (1 hunks)
  • content/Tutorial/poc-multi/step1.mdx (1 hunks)
  • content/Tutorial/poc-multi/step2.mdx (1 hunks)
  • content/Tutorial/poc-multi/step3.mdx (1 hunks)
  • content/Tutorial/poc-multi/step4.mdx (1 hunks)
  • content/meta.json (1 hunks)
  • examples/tutorial-hello-world/.editorconfig (1 hunks)
  • examples/tutorial-hello-world/.gitignore (1 hunks)
  • examples/tutorial-hello-world/AGENTS.md (1 hunks)
  • examples/tutorial-hello-world/README.md (1 hunks)
  • examples/tutorial-hello-world/agentuity.yaml (1 hunks)
  • examples/tutorial-hello-world/biome.json (1 hunks)
  • examples/tutorial-hello-world/index.ts (1 hunks)
  • examples/tutorial-hello-world/package.json (1 hunks)
  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts (1 hunks)
  • examples/tutorial-hello-world/tsconfig.json (1 hunks)
  • examples/tutorial_hello_world_py/.editorconfig (1 hunks)
  • examples/tutorial_hello_world_py/.gitignore (1 hunks)
  • examples/tutorial_hello_world_py/.python-version (1 hunks)
  • examples/tutorial_hello_world_py/AGENTS.md (1 hunks)
  • examples/tutorial_hello_world_py/README.md (1 hunks)
  • examples/tutorial_hello_world_py/agentuity.yaml (1 hunks)
  • examples/tutorial_hello_world_py/agentuity_agents/__init__.py (1 hunks)
  • examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/__init__.py (1 hunks)
  • examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py (1 hunks)
  • examples/tutorial_hello_world_py/main.py (1 hunks)
  • examples/tutorial_hello_world_py/pyproject.toml (1 hunks)
  • examples/tutorial_hello_world_py/server.py (1 hunks)
✅ Files skipped from review due to trivial changes (12)
  • examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/init.py
  • content/Tutorial/poc-multi/step4.mdx
  • examples/tutorial_hello_world_py/.python-version
  • content/Tutorial/poc-multi/step3.mdx
  • examples/tutorial_hello_world_py/.editorconfig
  • examples/tutorial-hello-world/biome.json
  • examples/tutorial_hello_world_py/AGENTS.md
  • content/Tutorial/poc-multi/meta.json
  • examples/tutorial_hello_world_py/agentuity_agents/init.py
  • content/Tutorial/meta.json
  • examples/tutorial-hello-world/.gitignore
  • examples/tutorial-hello-world/.editorconfig
🧰 Additional context used
🧠 Learnings (11)
📚 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:

  • examples/tutorial-hello-world/index.ts
  • examples/tutorial-hello-world/tsconfig.json
  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 Learning: 2025-07-23T12:40:22.412Z
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 : The file should export a default function

Applied to files:

  • examples/tutorial-hello-world/index.ts
  • examples/tutorial-hello-world/package.json
  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 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:

  • examples/tutorial-hello-world/index.ts
  • examples/tutorial-hello-world/tsconfig.json
  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 Learning: 2025-07-23T12:40:22.412Z
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 : All code should be in Typescript format

Applied to files:

  • examples/tutorial-hello-world/index.ts
  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 Learning: 2025-07-23T12:40:26.540Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/agentuity.mdc:0-0
Timestamp: 2025-07-23T12:40:26.540Z
Learning: Applies to agent-docs/**/agentuity.yaml : Do not suggest edits to the Agentuity AI Configuration file (agentuity.yaml)

Applied to files:

  • examples/tutorial_hello_world_py/agentuity.yaml
  • examples/tutorial-hello-world/agentuity.yaml
📚 Learning: 2025-07-23T12:40:22.412Z
Learnt from: CR
PR: agentuity/docs#0
File: agent-docs/.cursor/rules/agent.mdc:0-0
Timestamp: 2025-07-23T12:40:22.412Z
Learning: Prefer using the `agentuity agent create` command to create a new Agent

Applied to files:

  • content/Tutorial/poc-multi/step1.mdx
  • content/Tutorial/poc-multi/index.mdx
📚 Learning: 2025-07-23T12:40:22.412Z
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

Applied to files:

  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 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:

  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts
  • examples/tutorial-hello-world/AGENTS.md
📚 Learning: 2025-09-10T14:24:52.800Z
Learnt from: afterrburn
PR: agentuity/docs#279
File: agent-docs/src/agents/agent-pulse/types.ts:2-7
Timestamp: 2025-09-10T14:24:52.800Z
Learning: The Agentuity SDK (agentuity/sdk) only exports specific types: AgentRequest, AgentResponse, AgentContext, and VectorUpsertParams. It does not export general message types like ConversationMessage or ChatMessage.

Applied to files:

  • examples/tutorial-hello-world/src/agents/agent-hello/index.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 the storage APIs for persisting data

Applied to files:

  • examples/tutorial-hello-world/AGENTS.md
📚 Learning: 2025-07-23T12:40:22.412Z
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

Applied to files:

  • examples/tutorial-hello-world/AGENTS.md
🧬 Code graph analysis (2)
examples/tutorial-hello-world/src/agents/agent-hello/index.ts (1)
examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py (1)
  • welcome (6-19)
examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py (1)
examples/tutorial-hello-world/src/agents/agent-hello/index.ts (1)
  • welcome (6-21)
🪛 Ruff (0.12.2)
examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py

38-38: Do not catch blind exception: Exception

(BLE001)


39-39: Use logging.exception instead of logging.error

Replace with exception

(TRY400)

🪛 markdownlint-cli2 (0.17.2)
examples/tutorial-hello-world/AGENTS.md

6-6: Images should have alternate text (alt text)

(MD045, no-alt-text)


65-65: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

examples/tutorial_hello_world_py/README.md

6-6: Images should have alternate text (alt text)

(MD045, no-alt-text)


72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

⏰ 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). (2)
  • GitHub Check: Workers Builds: docs
  • GitHub Check: Workers Builds: docs
🔇 Additional comments (15)
examples/tutorial_hello_world_py/agentuity.yaml (3)

1-6: LGTM: Generated Agentuity config header and schema reference.

Acknowledging team guidance: not proposing edits to agentuity.yaml.


9-15: Confirm CLI/tooling satisfies version constraint '>=0.0.166'.

Please verify CI and local dev images pin or enforce a compatible Agentuity CLI/runtime version so builds don’t drift.


64-71: LGTM: Agent metadata (id/name/description).

Looks consistent with the tutorial.

examples/tutorial_hello_world_py/pyproject.toml (1)

6-9: No action needed — required minimum versions are available on PyPI.

agentuity 0.0.106 is present (latest 0.0.106); openai 1.107.0 is present (latest 1.107.2).

examples/tutorial_hello_world_py/.gitignore (2)

1-180: Comprehensive Python .gitignore — solid baseline

Good coverage across Python tooling, packaging, tests, and secret hygiene for an example project.


110-119: Keep .pdm.toml ignored; commit pyproject.toml and pdm.lock
PDM docs recommend committing pyproject.toml and pdm.lock; .pdm.toml is local and should remain in .gitignore (pdm.toml without the leading dot may be shared).

examples/tutorial_hello_world_py/main.py (1)

1-6: LGTM for a minimal tutorial script.

Simple, readable, and correctly guarded with if __name__ == "__main__":.

examples/tutorial-hello-world/src/agents/agent-hello/index.ts (4)

4-4: LGTM: single OpenAI client at module scope.

Reusing a single client instance is good practice.


6-21: Welcome payload looks clean and mirrors the Python example.

No issues.


23-27: Default export named Agent aligns with house rules.

Matches retrieved learnings (TS, default export, naming).


1-1: SDK import path is correct — keep @agentuity/sdk (scoped).

  • examples/tutorial-hello-world/src/agents/agent-hello/index.ts imports from '@agentuity/sdk' and package.json entries confirm it (agent-docs/package.json: "@agentuity/sdk": "^0.0.124"; examples/tutorial-hello-world/package.json: "@agentuity/sdk": "^0.0.145").
  • Repo learning that mentions agentuity/sdk (unscoped) is out-of-date; no change needed to this import.

Likely an incorrect or invalid review comment.

examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py (1)

6-19: Welcome payload looks good.

Matches the TS tutorial structure and provides sensible starter prompts.

examples/tutorial-hello-world/agentuity.yaml (1)

1-82: No suggestions per repo guidance for Agentuity config files.

examples/tutorial-hello-world/AGENTS.md (1)

31-36: Return type confirmed — keep Promise
The SDK publicly exports AgentResponseType; the handler should return Promise (examples use response.json / response.text / response.stream).

examples/tutorial-hello-world/package.json (1)

31-33: Verify dependency versions and Bun 1.2.x compatibility.

  • npm dist-tags: @agentuity/sdk latest = 0.0.145; openai latest = 5.20.2. PR deps show "@agentuity/sdk": "^0.0.145" and "openai": "^5.20.0" (caret will allow 5.20.2).
  • examples/tutorial-hello-world/package.json could not be opened (No such file). Confirm the correct package.json path or run: jq -r '.dependencies' <path-to-package.json>. After confirming, verify Bun 1.2.x compatibility for openai@5.x and @agentuity/sdk (ESM/runtime changes may break Bun).

@@ -0,0 +1,23 @@
---
title: Create a new Agent with the CLI
description: Quickstart to scaffold an Agentuity project using agentuity project create, authenticate with Agentuity, and run locally.
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Align CLI wording with preferred command.

Earlier guidance prefers agentuity agent create. Update the description to avoid confusion with project create.

Apply this diff:

-description: Quickstart to scaffold an Agentuity project using agentuity project create, authenticate with Agentuity, and run locally.
+description: Quickstart to scaffold an Agentuity project using agentuity agent create, authenticate with Agentuity, and run locally.

Run this to find any other instances to update:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 '\bagentuity\s+project\s+create\b|agent\s+create\b'

Length of output: 3846


Align CLI wording with preferred command (agentuity agent create)

Replace "agentuity project create" → "agentuity agent create" in the tutorial docs.

Apply this diff:

-description: Quickstart to scaffold an Agentuity project using agentuity project create, authenticate with Agentuity, and run locally.
+description: Quickstart to scaffold an Agentuity project using agentuity agent create, authenticate with Agentuity, and run locally.

Also update:

  • content/Tutorial/poc-multi/step1.mdx (line 10): change agentuity project createagentuity agent create.

Do not change content/CLI/project.mdx (the project create examples belong in project docs).

📝 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
description: Quickstart to scaffold an Agentuity project using agentuity project create, authenticate with Agentuity, and run locally.
description: Quickstart to scaffold an Agentuity project using agentuity agent create, authenticate with Agentuity, and run locally.
🤖 Prompt for AI Agents
In content/Tutorial/poc-multi/index.mdx around line 3 and
content/Tutorial/poc-multi/step1.mdx at line 10, update the CLI command wording:
replace occurrences of "agentuity project create" with "agentuity agent create".
Only change these two tutorial files; do not modify content/CLI/project.mdx.

async def run(request: AgentRequest, response: AgentResponse, context: AgentContext):
try:
result = await client.chat.completions.create(
model="gpt-5-mini",
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Avoid hardcoding an invalid/unknown model; make it configurable.

"gpt-5-mini" may not exist for your account and will 404. Read from env/config with a safe default.

Apply this diff:

-            model="gpt-5-mini",
+            model=DEFAULT_MODEL,

If you want me to wire this to your config/KV helper instead of env, say so and I’ll patch it.


🌐 Web query:

What OpenAI Chat Completions/Responses models are currently available and recommended for lightweight text assistants?

💡 Result:

Recommended lightweight Chat Completions / Responses models (current as of Sep 15, 2025):

  • GPT‑4.1 mini — OpenAI’s current small, fast, efficient “mini” model and the recommended lightweight default for assistants. [1][2]
  • o3‑mini — low-cost reasoning-focused mini model for cheaper, faster reasoning-heavy tasks. [5][6]
  • o4‑mini / o‑series mini models — alternative small models in the “o” reasoning family for cost/latency tradeoffs. [5]

Notes:

  • The Responses API and Chat Completions support the GPT‑4.1 series and the o‑series (o3, o3‑mini, o4‑mini) — choose GPT‑4.1 mini for highest general instruction-following and speed; use o3‑mini when you need lower-cost reasoning. [1][5]

Sources: OpenAI GPT‑4.1 announcement and docs; OpenAI model release notes; Responses API & o‑series model docs. [1][2][5][6]


Do not hardcode an unknown model — make the model configurable and default to a supported mini model

"gpt-5-mini" is not a known Responses/ChatCompletions model and may 404; read the model from env/config and default to a supported lightweight model (recommended: "gpt-4.1-mini" or "o3-mini").

File: examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py — line 24

Apply this diff:

-            model="gpt-5-mini",
+            model=DEFAULT_MODEL,

Ensure DEFAULT_MODEL is set via env/config and CI uses a valid model.

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

🤖 Prompt for AI Agents
In examples/tutorial_hello_world_py/agentuity_agents/agent_hello_py/agent.py
around line 24, the model is hardcoded to "gpt-5-mini" which may 404; change
this to read a DEFAULT_MODEL from environment/config (e.g., os.environ.get or a
config module) and fall back to a supported lightweight model such as
"gpt-4.1-mini" or "o3-mini"; replace the literal model string with that
DEFAULT_MODEL variable, ensure the DEFAULT_MODEL is documented and settable in
CI/env files, and update CI/test configs to use a valid model value.

Comment on lines 16 to 22
const cmd = process.env._ || '';

if (cmd.endsWith('node')) {
console.error(
'\x1b[31m[ERROR] Re-run the command with `node --env-file .env index.ts`\x1b[0m'
);
}
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 Node hint: Node can’t run .ts directly. Suggest running the bundled JS.

Current suggestion (node --env-file .env index.ts) will fail without a TS runner.

-  const cmd = process.env._ || '';
-
-  if (cmd.endsWith('node')) {
+  const isNode = process.release?.name === 'node';
+  if (isNode) {
     console.error(
-      '\x1b[31m[ERROR] Re-run the command with `node --env-file .env index.ts`\x1b[0m'
+      '\x1b[31m[ERROR] Re-run with `node --env-file=.env .agentuity/index.js` (after `agentuity bundle`).\x1b[0m'
     );
   }
📝 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 cmd = process.env._ || '';
if (cmd.endsWith('node')) {
console.error(
'\x1b[31m[ERROR] Re-run the command with `node --env-file .env index.ts`\x1b[0m'
);
}
const isNode = process.release?.name === 'node';
if (isNode) {
console.error(
'\x1b[31m[ERROR] Re-run with `node --env-file=.env .agentuity/index.js` (after `agentuity bundle`).\x1b[0m'
);
}
🤖 Prompt for AI Agents
In examples/tutorial-hello-world/index.ts around lines 16–22, the current error
message incorrectly suggests running a .ts file with plain node; update the
message to advise running the compiled JS or a TS runner. Replace the hint to
say something like "Re-run the command with the bundled JavaScript (e.g. node
dist/index.js) or use a TypeScript runner (e.g. npx ts-node index.ts)" so users
have valid options that will actually work.

Comment on lines 39 to 41
return resp.text(
result.choices[0]?.message.content ?? 'Something went wrong'
);
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 optional chaining to avoid runtime TypeError on empty choices.

result.choices[0]?.message.content still dereferences .content even when message is undefined. Use full chaining.

Apply:

-      result.choices[0]?.message.content ?? 'Something went wrong'
+      result.choices?.[0]?.message?.content ?? 'Something went wrong'
📝 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
return resp.text(
result.choices[0]?.message.content ?? 'Something went wrong'
);
return resp.text(
result.choices?.[0]?.message?.content ?? 'Something went wrong'
);
🤖 Prompt for AI Agents
examples/tutorial-hello-world/src/agents/agent-hello/index.ts around lines 39 to
41: the expression uses optional chaining only on choices[0] but still
dereferences .content even when message is undefined, which can throw a
TypeError; change the access to fully safe optional chaining (e.g., check
message before .content or use result.choices[0]?.message?.content) and fall
back to the existing default string so resp.text receives a defined string.

return NextResponse.json({ error: "User ID not found" }, { status: 401 });
}
// Helper: sanitize title per requirements
function sanitizeTitle(input: string): string {
Copy link
Member

Choose a reason for hiding this comment

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

These probably shouldn't be defined inside the POST request

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 (4)
app/api/sessions/[sessionId]/messages/route.ts (4)

77-81: Always set a timestamp for incoming user messages.

Guarantee a timestamp even when the client omits it.

-    // Ensure timestamp is in ISO string format
-    if (message.timestamp) {
-      message.timestamp = toISOString(message.timestamp);
-    }
+    // Ensure timestamp is set and in ISO string format
+    if (message.timestamp) {
+      message.timestamp = toISOString(message.timestamp);
+    } else {
+      message.timestamp = getCurrentTimestamp();
+    }

332-351: Harden stream piping: wrap close, await abort, and avoid masking errors.

-    (async () => {
+    (async () => {
       try {
         while (true) {
           const { done, value } = await reader.read();
           if (done) {
             break;
           }
           try {
             await writer.write(value);
           } catch (writeError) {
             console.error('Error writing to transform stream:', writeError);
             throw writeError;
           }
         }
-        await writer.close();
+        try {
+          await writer.close();
+        } catch (closeError) {
+          console.error('Error closing transform stream writer:', closeError);
+          throw closeError;
+        }
       } catch (error) {
         console.error('Error in stream processing:', error);
-        writer.abort(error);
+        try {
+          await writer.abort(error as any);
+        } catch (abortError) {
+          console.error('Error aborting writer:', abortError);
+        }
       }
     })();

190-207: Do not rely on try/catch for KV client failures; handle the response.

setKVValue does not throw on HTTP errors. This can proceed with an inconsistent state.

-    try {
-      await setKVValue(sessionKey, updatedSession, {
-        storeName: config.defaultStoreName,
-      });
-    } catch (error) {
-      console.error(
-        `Failed to save session after adding message. SessionId: ${sessionId}, Error details:`,
-        error instanceof Error ? error.message : String(error),
-        error instanceof Error && error.stack ? `Stack: ${error.stack}` : ''
-      );
-      return NextResponse.json(
-        {
-          error: "Failed to save message to session",
-          details: "Unable to persist the message. Please try again."
-        },
-        { status: 500 }
-      );
-    }
+    const updateResp = await setKVValue(sessionKey, updatedSession, {
+      storeName: config.defaultStoreName,
+    });
+    if (!updateResp.success) {
+      return NextResponse.json(
+        {
+          error: updateResp.error || "Failed to save message to session",
+        },
+        { status: updateResp.statusCode || 500 }
+      );
+    }

255-324: SSE parsing is not chunk-safe and can emit multiple finishes; add buffering and a finish gate.

-    // Process streaming response
-    let accumulatedContent = "";
-    let finalTutorialData: TutorialData | undefined = undefined;
-
-    const transformStream = new TransformStream({
+    // Process streaming response
+    let accumulatedContent = "";
+    let finalTutorialData: TutorialData | undefined = undefined;
+    let finishedSent = false;
+    const sseDecoder = new TextDecoder();
+    let sseBuffer = "";
+
+    const transformStream = new TransformStream({
       async transform(chunk, controller) {
         // Forward the chunk to the client
         controller.enqueue(chunk);
 
-        // Process the chunk to accumulate the full response
-        const text = new TextDecoder().decode(chunk);
-        const lines = text.split("\n");
+        // Buffer-aware SSE parsing
+        sseBuffer += sseDecoder.decode(chunk, { stream: true });
+        const lines = sseBuffer.split("\n");
+        sseBuffer = lines.pop() ?? ""; // keep last partial
 
         for (const line of lines) {
           if (line.startsWith("data: ")) {
             try {
               const data = JSON.parse(line.slice(6)) as StreamingChunk;
 
               if (data.type === "text-delta" && data.textDelta) {
                 accumulatedContent += data.textDelta;
               } else if (data.type === "tutorial-data" && data.tutorialData) {
                 finalTutorialData = data.tutorialData;
 
                 // Update user's tutorial progress
                 await TutorialStateManager.updateTutorialProgress(
                   userId,
                   finalTutorialData.tutorialId,
                   finalTutorialData.currentStep,
                   finalTutorialData.totalSteps
                 );
-              } else if (data.type === "finish") {
+              } else if (data.type === "finish" && !finishedSent) {
+                finishedSent = true;
                 // When the stream is finished, save the assistant message
                 const assistantMessage: Message = {
                   id: assistantMessageId,
                   author: "ASSISTANT",
                   content: accumulatedContent,
                   timestamp: getCurrentTimestamp(),
                   tutorialData: finalTutorialData,
                 };
 
-                const finalSession = {
-                  ...updatedSession,
-                  messages: [...updatedSession.messages, assistantMessage],
-                };
-
-                await setKVValue(sessionKey, finalSession, {
-                  storeName: config.defaultStoreName,
-                });
+                // Merge with latest before persisting (avoid overwriting concurrent updates)
+                const latest = await getKVValue<Session>(sessionKey, { storeName: config.defaultStoreName });
+                const base = latest.success && latest.data ? latest.data : updatedSession;
+                const already = base.messages.some(m => m.id === assistantMessageId);
+                const finalSession: Session = already
+                  ? base
+                  : { ...base, messages: [...base.messages, assistantMessage] };
+
+                const persistResp = await setKVValue(sessionKey, finalSession, { storeName: config.defaultStoreName });
+                if (!persistResp.success) {
+                  console.error('Failed to persist assistant message:', persistResp.error || persistResp.statusCode);
+                }
 
                 // Trigger background title generation if missing
                 // Do not await to avoid delaying the client stream completion
                 void generateAndPersistTitle(sessionId, sessionKey, finalSession);
 
                 // Send the final session in the finish event
                 controller.enqueue(
                   new TextEncoder().encode(
                     `data: ${JSON.stringify({
                       type: "finish",
                       session: finalSession,
                     })}\n\n`
                   )
                 );
               }
             } catch (error) {
               console.error("Error processing stream chunk:", error);
             }
           }
         }
       },
+      async flush() {
+        // Process any remaining buffered line(s)
+        if (!sseBuffer) return;
+        for (const line of sseBuffer.split("\n")) {
+          if (!line.startsWith("data: ")) continue;
+          try {
+            const data = JSON.parse(line.slice(6)) as StreamingChunk;
+            if (data.type === "text-delta" && data.textDelta) {
+              accumulatedContent += data.textDelta;
+            } else if (data.type === "tutorial-data" && data.tutorialData) {
+              finalTutorialData = data.tutorialData;
+            }
+          } catch { /* ignore */ }
+        }
+      }
     });
🧹 Nitpick comments (5)
app/api/sessions/[sessionId]/messages/route.ts (5)

102-104: Replace unnecessary template literal to satisfy linter.

No interpolation present; use a normal string literal.

-        const prompt = `Generate a very short session title summarizing the conversation topic.\n\nRequirements:\n- sentence case\n- no emojis\n- <= 60 characters\n- no quotes or markdown\n- output the title only, no extra text`;
+        const prompt = 'Generate a very short session title summarizing the conversation topic.\n\nRequirements:\n- sentence case\n- no emojis\n- <= 60 characters\n- no quotes or markdown\n- output the title only, no extra text';

137-157: Title-gen SSE parsing can drop content across chunk boundaries. Buffer and decode with streaming.

-        let accumulated = '';
-        const textDecoder = new TextDecoder();
-        while (true) {
-          const { done, value } = await reader.read();
-          if (done) break;
-          if (value) {
-            const text = textDecoder.decode(value);
-            for (const line of text.split('\n')) {
-              if (line.startsWith('data: ')) {
-                try {
-                  const ev = JSON.parse(line.slice(6));
-                  if (ev.type === 'text-delta' && ev.textDelta) accumulated += ev.textDelta;
-                  if (ev.type === 'finish') {
-                    try { await reader.cancel(); } catch { }
-                    break;
-                  }
-                } catch { }
-              }
-            }
-          }
-        }
+        let accumulated = '';
+        const decoder = new TextDecoder();
+        let buffer = '';
+        let finished = false;
+        while (true) {
+          const { done, value } = await reader.read();
+          if (done) break;
+          if (!value) continue;
+          buffer += decoder.decode(value, { stream: true });
+          const lines = buffer.split('\n');
+          buffer = lines.pop() ?? '';
+          for (const line of lines) {
+            if (!line.startsWith('data: ')) continue;
+            try {
+              const ev = JSON.parse(line.slice(6));
+              if (ev.type === 'text-delta' && ev.textDelta) accumulated += ev.textDelta;
+              else if (ev.type === 'finish' && !finished) {
+                finished = true;
+                try { await reader.cancel(); } catch { /* ignore */ }
+              }
+            } catch { /* ignore parse */ }
+          }
+        }
+        // Flush any remaining buffered line
+        if (buffer.startsWith('data: ')) {
+          try {
+            const ev = JSON.parse(buffer.slice(6));
+            if (ev.type === 'text-delta' && ev.textDelta) accumulated += ev.textDelta;
+          } catch { /* ignore */ }
+        }

162-169: Check KV write success when persisting generated title.

setKVValue returns { success, statusCode } and does not throw.

-        await setKVValue(sessionKey, current, { storeName: config.defaultStoreName });
+        const titleResp = await setKVValue(sessionKey, current, { storeName: config.defaultStoreName });
+        if (!titleResp.success) {
+          console.error('[title-gen] failed to persist title:', titleResp.error || titleResp.statusCode);
+        }

86-178: Move title generation helper out of the route handler for testability and reuse.

Define generateAndPersistTitle in a separate module (e.g., app/chat/services/sessionTitleService.ts) and import here.


353-359: SSE delivery: disable proxy buffering.

Add X-Accel-Buffering: no to improve real-time streaming behind Nginx.

     return new NextResponse(transformStream.readable, {
       headers: {
         "Content-Type": "text/event-stream",
         "Cache-Control": "no-cache",
         Connection: "keep-alive",
+        "X-Accel-Buffering": "no",
       },
     });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d70c4e and 198d769.

📒 Files selected for processing (1)
  • app/api/sessions/[sessionId]/messages/route.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/api/sessions/[sessionId]/messages/route.ts (8)
app/api/sessions/route.ts (1)
  • POST (75-149)
app/api/sessions/[sessionId]/route.ts (1)
  • POST (189-266)
app/api/users/tutorial-state/route.ts (1)
  • POST (39-71)
lib/validation/middleware.ts (2)
  • parseAndValidateJSON (35-61)
  • SessionMessageRequestSchema (73-76)
app/chat/utils/dateUtils.ts (2)
  • toISOString (10-16)
  • getCurrentTimestamp (35-37)
lib/kv-store.ts (2)
  • getKVValue (47-113)
  • setKVValue (122-170)
lib/env.ts (1)
  • getAgentPulseConfig (32-34)
lib/tutorial/state-manager.ts (1)
  • TutorialStateManager (5-121)
🪛 Biome (2.1.2)
app/api/sessions/[sessionId]/messages/route.ts

[error] 102-104: Do not use template literals if interpolation and special-character handling are not needed.

Safe fix: Replace with string literal

(lint/style/noUnusedTemplateLiteral)

⏰ 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

@afterrburn afterrburn merged commit a61babf into main Sep 17, 2025
2 checks passed
@afterrburn afterrburn deleted the seng/chat-prototype branch September 17, 2025 13:05
devin-ai-integration bot added a commit that referenced this pull request Sep 17, 2025
afterrburn pushed a commit that referenced this pull request Sep 17, 2025
This reverts commit a61babf.

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
devin-ai-integration bot added a commit that referenced this pull request Sep 17, 2025
* 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>
devin-ai-integration bot added a commit that referenced this pull request Sep 17, 2025
- Add runtime environment detection to prevent @agentuity/sdk loading in Cloudflare Workers
- Implement conditional agent loader that only loads agent code in Node.js environments
- Fix TypeScript errors in agent-docs tools.ts for ai package v5.0.45 API
- Restore chat prototype functionality from PR #279 with instrumentation fix
- Add webpack ignore rule for agent-docs to prevent build-time loading
- Update tsconfig to exclude agent-docs from TypeScript compilation

This resolves the 'Failed to prepare server Error: An error occurred while loading the instrumentation hook' error on agentuity.dev by preventing OpenTelemetry auto-instrumentations from loading in Cloudflare Workers runtime while preserving full chat functionality in Node.js environments.

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>
afterrburn added a commit that referenced this pull request Sep 17, 2025
* 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>
@coderabbitai coderabbitai bot mentioned this pull request Sep 17, 2025
afterrburn added a commit that referenced this pull request Sep 20, 2025
* Seng/chat prototype (#279)

* 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>

* remove unused component

* gracefully return empty array when tutorial does not exist

* cleanup agent-docs readme and bun

* move agent IDs to config since they are not secrets

* update agent url configs

* fix config issue

* fix env

---------

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>
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