Skip to content

Conversation

devin-ai-integration[bot]
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Sep 20, 2025

Empty PR 4

Summary

This PR adds a single comment line to the postcss.config.mjs file as specifically requested by @afterrburn. This is intentionally a minimal "empty PR" with no functional changes - just adds one comment line for testing/workflow purposes. This is the fourth in a series of similar empty PRs created in this session.

Review & Testing Checklist for Human

  • Verify that this minimal change was intentionally requested (it was specifically asked for as an "empty PR")
  • Confirm the build still passes (should be automatic via CI)

Notes

Summary by CodeRabbit

  • New Features

    • Introduced a new public “Pulse” agent for conversational, tutorial-guided assistance.
    • Supports streaming replies, starting tutorials by ID, step navigation, and delivering step content/snippets.
    • Integrates with documentation Q&A for contextual answers.
    • Improved resilience with clear status updates, fallbacks, and error messages.
  • Documentation

    • Added user-facing README with overview, features, and example interactions for the Pulse agent.
  • Chores

    • Minor configuration comment added; no functional impact.

Co-Authored-By: srith@agentuity.com <rithsenghorn@gmail.com>
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link

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

Deploying with  Cloudflare Workers  Cloudflare Workers

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

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 57f4196 Sep 20 2025, 08:27 PM

Copy link
Contributor

coderabbitai bot commented Sep 20, 2025

Note

Reviews paused

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Walkthrough

Adds a new public agent “agent-pulse” and its implementation: request parsing/types, system prompt builder, tools, tutorial API helpers, state manager, streaming processor, and endpoint. Includes a README and a minor comment in postcss config. No modifications to existing agents beyond inserting the new entry.

Changes

Cohort / File(s) Summary
Agent registration
agent-docs/agentuity.yaml
Adds a new public agent entry (id, name, description) for agent-pulse.
Agent Pulse entrypoint & context
agent-docs/src/agents/agent-pulse/index.ts, agent-docs/src/agents/agent-pulse/context/builder.ts
Implements the agent endpoint with streaming flow, context building with tutorial data, and a system prompt builder with logging and error handling.
Request parsing & types
agent-docs/src/agents/agent-pulse/request/parser.ts, agent-docs/src/agents/agent-pulse/request/types.ts
Adds request parser to normalize inputs and defines request-related interfaces (ConversationMessage, TutorialState, ParsedAgentRequest).
State core & manager
agent-docs/src/agents/agent-pulse/state.ts, agent-docs/src/agents/agent-pulse/state/manager.ts
Introduces agent state interfaces/implementation and a manager to handle tutorial actions (e.g., start step) and produce tutorial data payloads.
Streaming pipeline
agent-docs/src/agents/agent-pulse/streaming/processor.ts, agent-docs/src/agents/agent-pulse/streaming/types.ts
Adds streaming processor emitting text/status/tutorial-data/error/finish chunks and type definitions for structured streaming payloads.
Tools integration
agent-docs/src/agents/agent-pulse/tools.ts
Provides tools to start tutorials and query another agent; includes placeholder code execution tool; exposes ToolContext.
Tutorial API helpers
agent-docs/src/agents/agent-pulse/tutorial.ts
Adds API client functions to list tutorials, fetch tutorial meta, and fetch steps; includes interfaces and logging.
Documentation
agent-docs/src/agents/agent-pulse/README.md
New README describing architecture, responses, workflow, and examples for agent-pulse.
Config comment
postcss.config.mjs
Adds a single-line comment; no functional changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant AE as Agent Endpoint (index.ts)
  participant RP as Request Parser
  participant CTX as Context Builder
  participant TL as Tools
  participant ST as State Manager
  participant API as Tutorials API (tutorial.ts)
  participant LLM as LLM Stream
  participant SP as Streaming Processor
  participant C as Client (SSE)

  U->>AE: HTTP request (message, history, tutorialData?)
  AE->>RP: parseAgentRequest(json)
  RP-->>AE: ParsedAgentRequest
  AE->>API: getTutorialList()
  API-->>AE: Tutorials list / fallback
  AE->>CTX: buildSystemPrompt(tutorialContext)
  AE->>TL: createTools({ state, agentContext })
  AE->>LLM: start streaming (messages, tools, system prompt)
  LLM-->>SP: fullStream iterator
  loop Stream chunks
    SP-->>C: text-delta/status chunks
  end
  SP->>ST: handleTutorialState(state)
  ST->>API: getTutorialStep(tutorialId, step)
  API-->>ST: Step data
  ST-->>SP: TutorialData or null
  alt TutorialData available
    SP-->>C: tutorial-data chunk
  end
  SP-->>C: finish
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

I thump my paws—new Pulse arrives,
A stream of tips that brightly thrives.
Tutorials hop from step to step,
With tools and state that neatly prep.
SSE breezes, bytes that sing,
Docs and chats in perfect spring.
🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The title "Empty PR 4" claims this is a minimal/empty change, but the provided file summaries show substantial additions (a new agent-pulse entry in the YAML and many new source files such as README.md, index.ts, context/builder.ts, streaming/processor.ts, tools.ts, tutorial.ts, state/manager.ts, and related types), so the title is misleading and does not accurately summarize the primary changes in the branch. Rename the PR to reflect the real scope (for example, "Add agent-pulse tutorial-aware agent and streaming support") or reduce/split the changes so the PR truly matches an "empty PR"; also update the description to clearly state the intended scope and ask the author to confirm before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

@afterrburn
Copy link
Contributor

@coderabbitai ignore

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

🧹 Nitpick comments (17)
agent-docs/src/agents/agent-pulse/streaming/types.ts (3)

12-19: De-duplicate TutorialSnippet: import and re-export the existing type.

This interface duplicates TutorialSnippet already defined in tutorial.ts. Prefer a single source of truth to prevent drift.

Apply this diff:

-export interface TutorialSnippet {
-  path: string;
-  lang?: string;
-  from?: number;
-  to?: number;
-  title?: string;
-  content: string;
-}
+export type { TutorialSnippet } from "../tutorial";

21-31: Avoid redundant totalSteps in nested tutorialStep.

totalSteps appears both at the top level and inside tutorialStep. This invites inconsistency. Consider keeping it only at the top level and deriving in the UI.

 export interface TutorialData {
   tutorialId: string;
   totalSteps: number;
   currentStep: number;
   tutorialStep: {
     title: string;
     mdx: string;
     snippets: TutorialSnippet[];
-    totalSteps: number;
   };
 }

48-48: Prefer a readonly union for transport payloads.

Marking fields readonly helps ensure chunks are treated as immutable during streaming.

Example:

-export interface TextDeltaChunk {
+export interface TextDeltaChunk {
   type: 'text-delta';
-  textDelta: string;
+  readonly textDelta: string;
 }

Apply similarly to other chunk interfaces.

agent-docs/src/agents/agent-pulse/request/types.ts (1)

11-16: Doc: clarify semantics of useDirectLLM.

If both snake_case and camelCase may appear in requests, note that here or ensure the parser normalizes both.

agent-docs/src/agents/agent-pulse/request/parser.ts (3)

9-13: Remove trivially inferred type; align with linter.

message: string is inferrable.

-    let message: string = "";
+    let message = "";

29-33: Validate tutorialData shape.

Ensure required fields exist and are typed correctly.

-      tutorialData = body.tutorialData || undefined;
+      if (body.tutorialData && typeof body.tutorialData === "object") {
+        const { tutorialId, currentStep } = body.tutorialData as any;
+        if (typeof tutorialId === "string" && Number.isInteger(currentStep)) {
+          tutorialData = { tutorialId, currentStep };
+        }
+      }

42-47: Sanitize debug logging to avoid noisy large payloads.

Consider truncating or redacting large/PII fields in raw request logging.

agent-docs/src/agents/agent-pulse/README.md (1)

96-103: Reflect streaming payload model in docs.

Consider adding a short section referencing StreamingChunk types (text-delta/status/tutorial-data/error/finish) for client implementers.

agent-docs/src/agents/agent-pulse/state.ts (3)

1-3: Export enum at declaration for clarity.

Exporting at declaration simplifies imports and avoids re-export churn.

-enum ActionType {
+export enum ActionType {
   START_TUTORIAL_STEP = "start_tutorial_step"
}

(Keep the existing re-export temporarily for backward compatibility, then remove in a follow-up.)


21-39: Encapsulate action to prevent external mutation.

Prefer private field with getter to avoid unintended writes.

-class SimpleAgentState implements AgentState {
-  public action: Action | null = null;
+class SimpleAgentState implements AgentState {
+  public action: Action | null = null; // consider making this private and exposing via getAction()

If changing now is risky, add a TODO to encapsulate later.


41-46: Re-export not needed if declared exported.

If ActionType is exported at declaration, you can drop the extra export.

-export type { Action, AgentState };
-export { ActionType };
+export type { Action, AgentState };
agent-docs/src/agents/agent-pulse/streaming/processor.ts (2)

64-68: Fix parameter mutation issue.

The static analysis tool correctly identifies that mutating the accumulatedContent parameter is problematic for code clarity. Function parameters should be treated as readonly.

 async function processChunk(
 	chunk: any,
 	controller: ReadableStreamDefaultController,
 	encoder: TextEncoder,
 	ctx: AgentContext,
-	accumulatedContent: string
+	accumulatedContent: { value: string }
 ): Promise<void> {
 	try {
 		if (chunk.type === "text-delta") {
-			accumulatedContent += chunk.textDelta;
+			accumulatedContent.value += chunk.textDelta;
 			sendChunk(controller, encoder, {

And update the caller in the start function:

-			let accumulatedContent = "";
+			let accumulatedContent = { value: "" };
 
 			try {
 				// Stream only safe, user-facing content
 				for await (const chunk of result.fullStream) {

16-26: Consider adding bounds checking for streaming.

While the implementation looks solid, consider adding safeguards for potential infinite streams or malformed chunks to prevent resource exhaustion.

 		try {
+			let chunkCount = 0;
+			const MAX_CHUNKS = 10000; // Reasonable limit for safety
+			
 			// Stream only safe, user-facing content
 			for await (const chunk of result.fullStream) {
+				if (++chunkCount > MAX_CHUNKS) {
+					ctx.logger.warn("Stream exceeded maximum chunk limit");
+					break;
+				}
 				await processChunk(
agent-docs/src/agents/agent-pulse/tools.ts (1)

36-36: Fix template literal usage.

The static analysis tool correctly identifies unnecessary template literal usage.

-                return `Error fetching tutorial information`;
+                return "Error fetching tutorial information";
agent-docs/src/agents/agent-pulse/index.ts (1)

97-97: Remove unnecessary type annotation.

The static analysis tool correctly identifies that the string type annotation is trivially inferred.

-		let systemPrompt: string = "";
+		let systemPrompt = "";
agent-docs/src/agents/agent-pulse/tutorial.ts (2)

60-79: Consider caching optimization for getTutorialMeta.

The function fetches all tutorials to find one by ID. Consider caching the tutorial list or using a dedicated endpoint if available for better performance.

// Consider adding a simple cache
const tutorialCache = new Map<string, { data: Tutorial[], timestamp: number }>();
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

export async function getTutorialMeta(tutorialId: string, ctx: AgentContext): Promise<ApiResponse<Tutorial>> {
  try {
    const now = Date.now();
    const cached = tutorialCache.get('tutorials');
    
    let tutorials: Tutorial[];
    if (cached && (now - cached.timestamp) < CACHE_TTL) {
      tutorials = cached.data;
    } else {
      const response = await fetch(`${TUTORIAL_API_BASE_URL}/api/tutorials`);
      // ... existing fetch logic
      tutorials = await response.json() as Tutorial[];
      tutorialCache.set('tutorials', { data: tutorials, timestamp: now });
    }
    
    const found = tutorials.find(t => t.id === tutorialId);
    // ... rest of the function
  }
  // ... error handling
}

5-26: Consolidate duplicated Tutorial interface

Duplicate definitions found — lib/tutorial/tutorial-reader.ts:26–32 (includes steps: Step[]) and agent-docs/src/agents/agent-pulse/tutorial.ts:5–10 (missing steps). Replace the local definition with the canonical export from lib/tutorial (or have the agent-docs interface extend it) to avoid duplication.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f5c285 and 96f0098.

📒 Files selected for processing (13)
  • agent-docs/agentuity.yaml (1 hunks)
  • agent-docs/src/agents/agent-pulse/README.md (1 hunks)
  • agent-docs/src/agents/agent-pulse/context/builder.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/index.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/request/parser.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/request/types.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/state.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/state/manager.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/streaming/processor.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/streaming/types.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/tools.ts (1 hunks)
  • agent-docs/src/agents/agent-pulse/tutorial.ts (1 hunks)
  • postcss.config.mjs (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
agent-docs/**/agentuity.yaml

📄 CodeRabbit inference engine (agent-docs/.cursor/rules/agentuity.mdc)

Do not suggest edits to the Agentuity AI Configuration file (agentuity.yaml)

Files:

  • agent-docs/agentuity.yaml
agent-docs/src/agents/**/*.ts

📄 CodeRabbit inference engine (agent-docs/.cursor/rules/sdk.mdc)

agent-docs/src/agents/**/*.ts: Use TypeScript for better type safety and IDE support
Import types from '@agentuity/sdk'
Use structured error handling with try/catch blocks
Leverage the provided logger for consistent logging
Use the storage APIs for persisting data
Consider agent communication for complex workflows

Files:

  • agent-docs/src/agents/agent-pulse/request/types.ts
  • agent-docs/src/agents/agent-pulse/state/manager.ts
  • agent-docs/src/agents/agent-pulse/request/parser.ts
  • agent-docs/src/agents/agent-pulse/state.ts
  • agent-docs/src/agents/agent-pulse/context/builder.ts
  • agent-docs/src/agents/agent-pulse/index.ts
  • agent-docs/src/agents/agent-pulse/streaming/processor.ts
  • agent-docs/src/agents/agent-pulse/tutorial.ts
  • agent-docs/src/agents/agent-pulse/tools.ts
  • agent-docs/src/agents/agent-pulse/streaming/types.ts
agent-docs/**/src/agents/**/index.ts

📄 CodeRabbit inference engine (agent-docs/.cursor/rules/agent.mdc)

agent-docs/**/src/agents/**/index.ts: Prefer loading types from the node modules package @agentuity/sdk in the node_modules folder
The file should export a default function
Prefer naming the default function Agent or the name of the Agent based on the context of the Agent description
All code should be in Typescript format
Use the provided logger from the AgentContext interface such as ctx.logger.info("my message: %s", "hello")

Files:

  • agent-docs/src/agents/agent-pulse/index.ts
🧠 Learnings (7)
📚 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:

  • agent-docs/agentuity.yaml
📚 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:

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

Applied to files:

  • agent-docs/src/agents/agent-pulse/state.ts
  • agent-docs/src/agents/agent-pulse/README.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 naming the default function Agent or the name of the Agent based on the context of the Agent description

Applied to files:

  • agent-docs/src/agents/agent-pulse/index.ts
📚 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:

  • agent-docs/src/agents/agent-pulse/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 TypeScript for better type safety and IDE support

Applied to files:

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

Applied to files:

  • agent-docs/src/agents/agent-pulse/tutorial.ts
🧬 Code graph analysis (8)
agent-docs/src/agents/agent-pulse/request/types.ts (1)
lib/tutorial/types.ts (1)
  • TutorialState (20-23)
agent-docs/src/agents/agent-pulse/state/manager.ts (4)
agent-docs/src/agents/agent-pulse/state.ts (2)
  • AgentState (45-45)
  • ActionType (46-46)
agent-docs/src/agents/agent-pulse/streaming/types.ts (1)
  • TutorialData (21-31)
app/chat/types.ts (1)
  • TutorialData (27-27)
agent-docs/src/agents/agent-pulse/tutorial.ts (1)
  • getTutorialStep (81-98)
agent-docs/src/agents/agent-pulse/request/parser.ts (1)
agent-docs/src/agents/agent-pulse/request/types.ts (1)
  • ParsedAgentRequest (11-16)
agent-docs/src/agents/agent-pulse/index.ts (7)
agent-docs/src/agents/agent-pulse/request/types.ts (2)
  • TutorialState (6-9)
  • ConversationMessage (1-4)
agent-docs/src/agents/agent-pulse/tutorial.ts (2)
  • getTutorialList (37-58)
  • Tutorial (5-10)
agent-docs/src/agents/agent-pulse/request/parser.ts (1)
  • parseAgentRequest (4-49)
agent-docs/src/agents/agent-pulse/state.ts (1)
  • createAgentState (41-43)
agent-docs/src/agents/agent-pulse/tools.ts (1)
  • createTools (19-101)
agent-docs/src/agents/agent-pulse/context/builder.ts (1)
  • buildSystemPrompt (3-54)
agent-docs/src/agents/agent-pulse/streaming/processor.ts (1)
  • createStreamingProcessor (6-57)
agent-docs/src/agents/agent-pulse/streaming/processor.ts (3)
agent-docs/src/agents/agent-pulse/state/manager.ts (1)
  • handleTutorialState (6-54)
agent-docs/src/agents/agent-pulse/streaming/types.ts (1)
  • StreamingChunk (48-48)
app/chat/types.ts (1)
  • StreamingChunk (84-84)
agent-docs/src/agents/agent-pulse/tutorial.ts (1)
lib/tutorial/tutorial-reader.ts (1)
  • Tutorial (26-32)
agent-docs/src/agents/agent-pulse/tools.ts (2)
agent-docs/src/agents/agent-pulse/state.ts (2)
  • AgentState (45-45)
  • ActionType (46-46)
agent-docs/src/agents/agent-pulse/tutorial.ts (1)
  • getTutorialMeta (60-79)
agent-docs/src/agents/agent-pulse/streaming/types.ts (2)
agent-docs/src/agents/agent-pulse/tutorial.ts (1)
  • TutorialSnippet (19-26)
app/chat/types.ts (3)
  • TutorialSnippet (12-12)
  • TutorialData (27-27)
  • StreamingChunk (84-84)
🪛 Biome (2.1.2)
agent-docs/src/agents/agent-pulse/request/parser.ts

[error] 9-9: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)

agent-docs/src/agents/agent-pulse/index.ts

[error] 97-97: This type annotation is trivially inferred from its initialization.

Safe fix: Remove the type annotation.

(lint/style/noInferrableTypes)

agent-docs/src/agents/agent-pulse/streaming/processor.ts

[error] 68-68: Assigning a function parameter is confusing.

The parameter is declared here:

Developers usually expect function parameters to be readonly. To align with this expectation, use a local variable instead.

(lint/style/noParameterAssign)

agent-docs/src/agents/agent-pulse/tools.ts

[error] 36-36: 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
🔇 Additional comments (24)
postcss.config.mjs (1)

1-1: No-op comment; configuration unchanged — LGTM.

Safe to merge as a workflow test.

agent-docs/agentuity.yaml (1)

81-83: No suggestions per repo rules.

Per maintained learnings, we don’t propose edits to agent-docs/**/agentuity.yaml.

agent-docs/src/agents/agent-pulse/request/types.ts (1)

6-9: Unify TutorialState with existing lib type.

Location: agent-docs/src/agents/agent-pulse/request/types.ts (lines 6–9). Your search returned no matches — confirm whether lib/tutorial/types.ts exports TutorialState; if it does, remove the local interface and replace it with an import, e.g. import type { TutorialState } from 'lib/tutorial/types'.

agent-docs/src/agents/agent-pulse/request/parser.ts (1)

1-49: Quick repo-wide check: author vs role and use_direct_llm vs useDirectLLM

  • agent-docs/src/agents/agent-pulse/request/parser.ts was not found and repo search returned no matches for 'author'/'role' or 'use_direct_llm'/'useDirectLLM'; confirm the correct path or run from repo root: rg -n --hidden -S '\b(author|role)\b|use_direct_llm|useDirectLLM' -g '!/dist/' and make callers use the same keys the parser emits (role vs author, and a single canonical form for useDirectLLM/use_direct_llm).
agent-docs/src/agents/agent-pulse/streaming/processor.ts (4)

1-10: LGTM! Proper TypeScript imports and function signature.

The imports are correctly structured with types from the appropriate modules, and the function signature follows TypeScript best practices with clear parameter types.


28-42: Excellent error boundary and tutorial state handling.

The implementation properly handles tutorial state after streaming and includes appropriate error boundaries. The separation of concerns between streaming content and tutorial data is well-designed.


59-96: Robust chunk processing with proper error handling.

The chunk processing logic correctly handles different chunk types and includes comprehensive error handling with debugging information.


98-115: LGTM! Clean utility functions.

Both sendChunk and getToolStatusMessage are well-implemented utility functions with clear responsibilities. The SSE format is properly implemented.

agent-docs/src/agents/agent-pulse/tools.ts (6)

1-14: LGTM! Clean imports and interface definition.

The TypeScript imports and ToolContext interface are well-structured and follow the coding guidelines.


26-53: LGTM! Well-designed tutorial tool with proper validation.

The startTutorialAtStep tool correctly:

  • Validates tutorial existence before starting
  • Checks step bounds
  • Updates state appropriately
  • Provides meaningful user feedback

59-78: LGTM! Proper external agent integration.

The docs agent tool correctly structures the query and handles the external agent call. The TODO comment appropriately flags the need for document reference handling.


83-94: Placeholder tool is appropriately marked.

The code execution result tool is clearly marked as a TODO with mock implementation. This is appropriate for initial development.


97-103: LGTM! Clean tool export structure.

The tools are properly exposed with meaningful names that align with their usage in the streaming processor.


19-22: Add error handling around agentContext.getAgent for the doc-qa agent.
createTools calls await agentContext.getAgent({ name: "doc-qa" }) with no try/catch or null/undefined guard; wrap the call in try/catch and/or check docQaAgent before use and return/log a graceful fallback when the agent is unavailable (file: agent-docs/src/agents/agent-pulse/tools.ts lines 19–22). getAgent's implementation isn't in this repo — confirm whether it throws or returns undefined and adjust handling accordingly.

agent-docs/src/agents/agent-pulse/index.ts (7)

1-11: LGTM! Proper imports following coding guidelines.

All imports are correctly structured with types from @agentuity/sdk and use TypeScript as required by the coding guidelines.


79-83: Function naming follows retrieved learnings.

The default exported function is correctly named Agent as specified in the retrieved learnings for agent index files.


15-46: LGTM! Robust context building with error handling.

The buildContext function properly handles API failures with fallback mechanisms and comprehensive error logging.


84-94: LGTM! Proper request parsing and state initialization.

The agent correctly parses the incoming request and initializes the state management system.


99-112: Excellent conditional tool and prompt setup.

The conditional logic for useDirectLLM is well-implemented, allowing bypass of tools and system prompts when direct LLM access is requested.


114-128: LGTM! Proper streaming implementation.

The OpenAI streaming setup and response processing are correctly implemented with appropriate model configuration and streaming processor integration.


129-142: Comprehensive error handling.

The error handling provides appropriate logging and returns structured error responses with both user-friendly messages and debug details.

agent-docs/src/agents/agent-pulse/tutorial.ts (3)

1-3: LGTM! Proper SDK import following coding guidelines.

The import correctly uses types from @agentuity/sdk as specified in the retrieved learnings.


12-17: LGTM! Well-structured generic API response type.

The ApiResponse<T> interface provides a clean abstraction for API responses with proper success/error handling.


37-58: LGTM! Proper error handling and logging.

The getTutorialList function correctly handles HTTP errors, logs appropriately, and provides structured responses.

Comment on lines +18 to +27
=== Available Tools or Functions ===
You have access to various tools you can use -- use when appropriate!
1. Tutorial management
- startTutorialAtStep: Starting the user off at a specific step of a tutorial.
2. General assistance
- askDocsAgentTool: retrieve Agentuity documentation snippets

=== TOOL-USAGE RULES (must follow) ===
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Align tool names in prompt with actual exported tools.

Prompt lists startTutorialAtStep and askDocsAgentTool, but tooling mentions startTutorialById and queryOtherAgent. Mismatch will confuse the model.

-=== Available Tools or Functions ===
-You have access to various tools you can use -- use when appropriate!
-1. Tutorial management  
-   - startTutorialAtStep: Starting the user off at a specific step of a tutorial.
-2. General assistance
-   - askDocsAgentTool: retrieve Agentuity documentation snippets
+=== AVAILABLE TOOLS ===
+Use tools only when appropriate.
+1. Tutorial management
+   - startTutorialById: start a tutorial at a specific step.
+2. General assistance
+   - queryOtherAgent: retrieve Agentuity documentation snippets

🏁 Script executed:

#!/bin/bash
rg -nC2 -e 'createTools|startTutorialById|startTutorialAtStep|askDocsAgentTool|queryOtherAgent' agent-docs/src/agents/agent-pulse

Length of output: 4790


Align prompt tool names with exported tools (startTutorialById, queryOtherAgent).

agent-docs/src/agents/agent-pulse/context/builder.ts (lines 18–27) lists startTutorialAtStep and askDocsAgentTool, but createTools exports startTutorialById and queryOtherAgent — update the prompt to match.

-=== Available Tools or Functions ===
-You have access to various tools you can use -- use when appropriate!
-1. Tutorial management  
-   - startTutorialAtStep: Starting the user off at a specific step of a tutorial.
-2. General assistance
-   - askDocsAgentTool: retrieve Agentuity documentation snippets
+=== AVAILABLE TOOLS ===
+Use tools only when appropriate.
+1. Tutorial management
+   - startTutorialById: start a tutorial at a specific step.
+2. General assistance
+   - queryOtherAgent: retrieve Agentuity documentation snippets

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

🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/context/builder.ts around lines 18 to 27,
the tool names in the prompt (startTutorialAtStep, askDocsAgentTool) do not
match the actual exported tool names (startTutorialById, queryOtherAgent);
update the prompt text to reference startTutorialById instead of
startTutorialAtStep and queryOtherAgent instead of askDocsAgentTool, and adjust
the usage rules line that mentions startTutorialById to ensure it matches the
new name and behavior (valid step handling) while keeping askDocsAgentTool
semantics mapped to queryOtherAgent.

Comment on lines +25 to +28
=== TOOL-USAGE RULES (must follow) ===
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Correct rule to reference the right tool and valid step range.

Keep rules consistent with tool names and enforce bounds.

-- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
+- startTutorialById may be used only after the user selects a tutorial. New tutorials default to step 1. Valid steps are [1, totalSteps].
📝 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
=== TOOL-USAGE RULES (must follow) ===
- startTutorialById must only be used when user select a tutorial. If the user starts a new tutorial, the step number should be set to one. Valid step is between 1 and totalSteps of the specific tutorial.
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.
=== TOOL-USAGE RULES (must follow) ===
- startTutorialById may be used only after the user selects a tutorial. New tutorials default to step 1. Valid steps are [1, totalSteps].
- Treat askDocsAgentTool as a search helper; ignore results you judge irrelevant.


=== END OF PROMPT ===

Stream your reasoning steps clearly.`;
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove chain-of-thought leakage.

“Stream your reasoning steps clearly.” risks exposing chain-of-thought. Replace with guidance to think privately.

-Stream your reasoning steps clearly.
+Think through the problem step-by-step internally; only output the final answer and necessary steps or code.
📝 Committable suggestion

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

Suggested change
Stream your reasoning steps clearly.`;
Think through the problem step-by-step internally; only output the final answer and necessary steps or code.`;
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/context/builder.ts at line 46, the guidance
"Stream your reasoning steps clearly." exposes chain-of-thought; replace it with
a directive that instructs the model to perform internal reasoning privately and
only emit the final answer. Update the string to something like "Think through
your reasoning privately and provide a concise final answer without revealing
internal chain-of-thought." Ensure no wording asks the model to narrate its
step-by-step reasoning.

Comment on lines +13 to +17
- **`index.ts`**: Main agent logic using `generateObject` for structured responses
- **`chat-helpers.ts`**: Conversation history management
- **`tutorial-helpers.ts`**: Tutorial content fetching and formatting
- **`tutorial.ts`**: Tutorial API integration

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

🧩 Analysis chain

Keep docs aligned with code (files and names).

The README references chat-helpers.ts and tutorial-helpers.ts; ensure these exist or update names to current modules (request/, state/, streaming/, tools.ts).


🏁 Script executed:

#!/bin/bash
fd -t f 'chat-helpers.ts' 'agent-docs/src/agents/agent-pulse' || true
fd -t f 'tutorial-helpers.ts' 'agent-docs/src/agents/agent-pulse' || true

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "Find agent-docs directory:"
fd -t d agent-docs || true
echo "List agent-docs/src/agents:"
ls -la agent-docs/src/agents || true
echo "List agent-pulse directory:"
ls -la agent-docs/src/agents/agent-pulse || true
echo "Show README (first 200 lines):"
sed -n '1,200p' agent-docs/src/agents/agent-pulse/README.md || true
echo "Search for files matching helper names:"
fd -t f -g '*chat*helpers*' || true
fd -t f -g '*tutorial*helpers*' || true
fd -t f -g '*chat*helper*' || true
fd -t f -g '*tutorial*helper*' || true
echo "Search repository for references to these helpers:"
rg -n --hidden --no-ignore 'chat-helpers|tutorial-helpers|chat_helpers|tutorial_helpers' || true
echo "List TypeScript files in agent-pulse:"
fd -e ts . agent-docs/src/agents/agent-pulse || true

Length of output: 5221


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Checking candidate files for mapping to README helper entries..."
files=(
  "agent-docs/src/agents/agent-pulse/state/manager.ts"
  "agent-docs/src/agents/agent-pulse/state.ts"
  "agent-docs/src/agents/agent-pulse/request/parser.ts"
  "agent-docs/src/agents/agent-pulse/tutorial.ts"
  "agent-docs/src/agents/agent-pulse/tools.ts"
  "agent-docs/src/agents/agent-pulse/index.ts"
)
for f in "${files[@]}"; do
  if [ -f "$f" ]; then
    echo "=== $f (first 200 lines) ==="
    sed -n '1,200p' "$f" || true
    echo
    echo "=== $f exports & keyword hits ==="
    rg -n --hidden --no-ignore --pretty --line-number "export (const|function|class|default)|conversation|history|chat|tutorial|fetch|format|manager|getTutorial|get_tutorial|startTutorial" "$f" || true
    echo
  else
    echo "=== $f NOT FOUND ==="
  fi
done
echo "Also showing README lines 1-40 for exact context:"
sed -n '1,40p' agent-docs/src/agents/agent-pulse/README.md || true

Length of output: 24565


Update agent-pulse README to match actual module filenames

README (agent-docs/src/agents/agent-pulse/README.md lines 13–17) references chat-helpers.ts and tutorial-helpers.ts which do not exist. Replace those entries with the real modules used by the agent, e.g.:

  • request/parser.ts — conversation history parsing/management
  • state/manager.ts (or state.ts) — agent state/action handling
  • tutorial.ts — tutorial API integration
  • tools.ts — agent tools (start tutorial / agent-to-agent queries)
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/README.md around lines 13–17 the README
lists non-existent modules (chat-helpers.ts, tutorial-helpers.ts); update the
bullet list to reflect actual filenames used by the agent: replace
chat-helpers.ts with request/parser.ts for conversation history
parsing/management, replace tutorial-helpers.ts with state/manager.ts (or
state.ts) for agent state/action handling, keep tutorial.ts for tutorial API
integration, and add tools.ts for agent tools (start tutorial / agent-to-agent
queries); ensure wording matches existing repo file names and paths exactly.

Comment on lines +17 to +18
useDirectLLM = body.use_direct_llm || false;
// Process conversation history
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

Normalize both camelCase and snake_case for useDirectLLM.

Accept both body.useDirectLLM and body.use_direct_llm.

-      useDirectLLM = body.use_direct_llm || false;
+      useDirectLLM = (typeof body.useDirectLLM === "boolean" ? body.useDirectLLM : body.use_direct_llm) || false;
📝 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
useDirectLLM = body.use_direct_llm || false;
// Process conversation history
useDirectLLM = (typeof body.useDirectLLM === "boolean" ? body.useDirectLLM : body.use_direct_llm) || false;
// Process conversation history
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/request/parser.ts around lines 17-18, the
code only reads body.use_direct_llm; update the assignment so it accepts both
camelCase and snake_case by checking body.useDirectLLM first, then
body.use_direct_llm, and fall back to false; coerce the selected value to a
boolean to avoid truthiness pitfalls (e.g., use Boolean(...) or === true) so
useDirectLLM reliably becomes true or false.

Comment on lines +19 to +27
if (Array.isArray(body.conversationHistory)) {
conversationHistory = body.conversationHistory.map((msg: any) => {
// Extract only role and content
return {
role: msg.role || (msg.author ? msg.author.toUpperCase() : "USER"),
content: msg.content || "",
};
});
}
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 conversationHistory shape and harden parsing.

Return ConversationMessage[] with role field; coerce/validate role; guard non-object entries.

-      if (Array.isArray(body.conversationHistory)) {
-        conversationHistory = body.conversationHistory.map((msg: any) => {
-          // Extract only role and content
-          return {
-            role: msg.role || (msg.author ? msg.author.toUpperCase() : "USER"),
-            content: msg.content || "",
-          };
-        });
-      }
+      if (Array.isArray(body.conversationHistory)) {
+        conversationHistory = body.conversationHistory
+          .filter((m: any) => m && typeof m === "object")
+          .map((msg: any) => {
+            const rawRole = (msg.role ?? msg.author ?? "USER");
+            const roleUC = String(rawRole).toUpperCase();
+            const role: "USER" | "ASSISTANT" = roleUC === "ASSISTANT" ? "ASSISTANT" : "USER";
+            return {
+              role,
+              content: typeof msg.content === "string" ? msg.content : "",
+            };
+          });
+      }
📝 Committable suggestion

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

Suggested change
if (Array.isArray(body.conversationHistory)) {
conversationHistory = body.conversationHistory.map((msg: any) => {
// Extract only role and content
return {
role: msg.role || (msg.author ? msg.author.toUpperCase() : "USER"),
content: msg.content || "",
};
});
}
if (Array.isArray(body.conversationHistory)) {
conversationHistory = body.conversationHistory
.filter((m: any) => m && typeof m === "object")
.map((msg: any) => {
const rawRole = (msg.role ?? msg.author ?? "USER");
const roleUC = String(rawRole).toUpperCase();
const role: "USER" | "ASSISTANT" = roleUC === "ASSISTANT" ? "ASSISTANT" : "USER";
return {
role,
content: typeof msg.content === "string" ? msg.content : "",
};
});
}
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/request/parser.ts around lines 19 to 27,
the current mapping assumes each entry is an object and copies
msg.role/msg.author and msg.content directly; update the parser to return a
ConversationMessage[] by: 1) first checking
Array.isArray(body.conversationHistory) and skipping non-object entries; 2) for
each entry ensure it's an object, extract and normalize role by validating
against allowed roles (e.g., "USER","ASSISTANT","SYSTEM") or defaulting to
"USER"; 3) coerce content to a string (default ""); and 4) return objects shaped
exactly as ConversationMessage { role: normalizedRole, content: coercedContent }
so parsing is hardened against malformed input.

Comment on lines +35 to +40
return {
message,
conversationHistory,
tutorialData,
useDirectLLM,
};
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

Return typed conversationHistory.

Tighten the return type to ConversationMessage[] (not any[]).

-      conversationHistory,
+      conversationHistory: conversationHistory as { role: "USER" | "ASSISTANT"; content: string }[],
📝 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 {
message,
conversationHistory,
tutorialData,
useDirectLLM,
};
return {
message,
conversationHistory: conversationHistory as { role: "USER" | "ASSISTANT"; content: string }[],
tutorialData,
useDirectLLM,
};
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/request/parser.ts around lines 35 to 40,
the returned conversationHistory is currently typed as any[]; change the return
type to ConversationMessage[] by updating the function's return type/interface
to reflect ConversationMessage[] and ensure conversationHistory is
declared/converted as ConversationMessage[] before returning (import the
ConversationMessage type if needed or assert/validate the array items to that
type). Make sure the returned object signature uses the tightened type so
TypeScript enforces ConversationMessage[] instead of any[].

Comment on lines +1 to +4
export interface ConversationMessage {
author: "USER" | "ASSISTANT";
content: string;
}
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 shape: use role instead of author to match parser output.

Parser produces role, not author. This mismatch will surface at compile/runtime boundaries.

-export interface ConversationMessage {
-  author: "USER" | "ASSISTANT";
-  content: string;
-}
+export interface ConversationMessage {
+  role: "USER" | "ASSISTANT";
+  content: string;
+}
📝 Committable suggestion

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

Suggested change
export interface ConversationMessage {
author: "USER" | "ASSISTANT";
content: string;
}
export interface ConversationMessage {
role: "USER" | "ASSISTANT";
content: string;
}
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/request/types.ts around lines 1-4, the
ConversationMessage interface uses an author field but the parser emits a role
field; rename author to role and update the type to match the parser's values
(e.g., "user" | "assistant" or whatever casing the parser uses), then update all
imports/usages to refer to message.role instead of message.author to keep
compile/runtime alignment.

Comment on lines +23 to +44
switch (action.type) {
case ActionType.START_TUTORIAL_STEP:
if (action.tutorialId) {
const tutorialStep = await getTutorialStep(action.tutorialId, action.currentStep, ctx);
if (tutorialStep.success && tutorialStep.data) {
const tutorialData: TutorialData = {
tutorialId: action.tutorialId,
totalSteps: action.totalSteps,
currentStep: action.currentStep,
tutorialStep: {
title: (tutorialStep.data.meta?.title as string) || tutorialStep.data.slug,
mdx: tutorialStep.data.mdx,
snippets: tutorialStep.data.snippets,
totalSteps: action.totalSteps
}
};
state.clearAction();
ctx.logger.info("Tutorial state processed successfully");
return tutorialData;
}
}
break;
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

Validate and clamp step; avoid unsafe casts; clear action on failures.

Guard tutorialId/currentStep/totalSteps, clamp step within [1, totalSteps], and avoid unsafe string cast on meta.title.

     switch (action.type) {
       case ActionType.START_TUTORIAL_STEP:
-        if (action.tutorialId) {
-          const tutorialStep = await getTutorialStep(action.tutorialId, action.currentStep, ctx);
+        {
+          const { tutorialId, currentStep, totalSteps } = action;
+          if (!tutorialId || !Number.isInteger(currentStep) || currentStep <= 0) {
+            ctx.logger.warn("Invalid tutorial action payload: %o", action);
+            state.clearAction();
+            break;
+          }
+          const clampedStep = totalSteps && Number.isInteger(totalSteps)
+            ? Math.min(Math.max(1, currentStep), Math.max(1, totalSteps))
+            : currentStep;
+          const tutorialStep = await getTutorialStep(tutorialId, clampedStep, ctx);
           if (tutorialStep.success && tutorialStep.data) {
             const tutorialData: TutorialData = {
-              tutorialId: action.tutorialId,
-              totalSteps: action.totalSteps,
-              currentStep: action.currentStep,
+              tutorialId,
+              totalSteps: totalSteps ?? tutorialStep.data.meta?.totalSteps ?? clampedStep,
+              currentStep: clampedStep,
               tutorialStep: {
-                title: (tutorialStep.data.meta?.title as string) || tutorialStep.data.slug,
+                title: typeof tutorialStep.data.meta?.title === "string" && tutorialStep.data.meta.title.length
+                  ? tutorialStep.data.meta.title
+                  : tutorialStep.data.slug,
                 mdx: tutorialStep.data.mdx,
-                snippets: tutorialStep.data.snippets,
-                totalSteps: action.totalSteps
+                snippets: tutorialStep.data.snippets ?? []
               }
             };
             state.clearAction();
             ctx.logger.info("Tutorial state processed successfully");
             return tutorialData;
           }
-        }
+          state.clearAction();
+        }
         break;
📝 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
switch (action.type) {
case ActionType.START_TUTORIAL_STEP:
if (action.tutorialId) {
const tutorialStep = await getTutorialStep(action.tutorialId, action.currentStep, ctx);
if (tutorialStep.success && tutorialStep.data) {
const tutorialData: TutorialData = {
tutorialId: action.tutorialId,
totalSteps: action.totalSteps,
currentStep: action.currentStep,
tutorialStep: {
title: (tutorialStep.data.meta?.title as string) || tutorialStep.data.slug,
mdx: tutorialStep.data.mdx,
snippets: tutorialStep.data.snippets,
totalSteps: action.totalSteps
}
};
state.clearAction();
ctx.logger.info("Tutorial state processed successfully");
return tutorialData;
}
}
break;
case ActionType.START_TUTORIAL_STEP:
{
const { tutorialId, currentStep, totalSteps } = action;
if (!tutorialId || !Number.isInteger(currentStep) || currentStep <= 0) {
ctx.logger.warn("Invalid tutorial action payload: %o", action);
state.clearAction();
break;
}
const clampedStep = totalSteps && Number.isInteger(totalSteps)
? Math.min(Math.max(1, currentStep), Math.max(1, totalSteps))
: currentStep;
const tutorialStep = await getTutorialStep(tutorialId, clampedStep, ctx);
if (tutorialStep.success && tutorialStep.data) {
const tutorialData: TutorialData = {
tutorialId,
totalSteps: totalSteps ?? tutorialStep.data.meta?.totalSteps ?? clampedStep,
currentStep: clampedStep,
tutorialStep: {
title: typeof tutorialStep.data.meta?.title === "string" && tutorialStep.data.meta.title.length
? tutorialStep.data.meta.title
: tutorialStep.data.slug,
mdx: tutorialStep.data.mdx,
snippets: tutorialStep.data.snippets ?? []
}
};
state.clearAction();
ctx.logger.info("Tutorial state processed successfully");
return tutorialData;
}
state.clearAction();
}
break;
🤖 Prompt for AI Agents
In agent-docs/src/agents/agent-pulse/state/manager.ts around lines 23 to 44,
validate presence of action.tutorialId, action.currentStep and action.totalSteps
before calling getTutorialStep, clamp currentStep to Math.max(1,
Math.min(action.currentStep, action.totalSteps)) so the requested step is within
[1, totalSteps], avoid unsafe casting of tutorialStep.data.meta?.title by
checking its type (use a conditional string fallback like typeof title ===
"string" ? title : tutorialStep.data.slug or String(title) only if defined), and
ensure state.clearAction() is also called on failure/invalid inputs (not just on
success) so the action is always cleared when processing finishes.

Comment on lines +81 to +98
export async function getTutorialStep(tutorialId: string, stepNumber: number, ctx: AgentContext): Promise<ApiResponse<TutorialStepResponseData>> {
try {
const response = await fetch(`${TUTORIAL_API_BASE_URL}/api/tutorials/${tutorialId}/steps/${stepNumber}`);

if (!response.ok) {
ctx.logger.error('Failed to fetch tutorial step %d for tutorial %s: %s', stepNumber, tutorialId, response.statusText);
return { success: false, status: response.status, error: response.statusText };
}

const responseData = await response.json();
ctx.logger.info('Fetched step %d for tutorial %s', stepNumber, tutorialId);

return responseData as ApiResponse<TutorialStepResponseData>;
} catch (error) {
ctx.logger.error('Error fetching tutorial step %d for tutorial %s: %s', stepNumber, tutorialId, error);
throw error;
}
} No newline at end of file
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

🧩 Analysis chain

Verify API response structure consistency.

The function returns responseData as ApiResponse<TutorialStepResponseData> but doesn't validate the response structure. Consider adding runtime validation to ensure the API contract is met.


🌐 Web query:

What is the standard structure for tutorial step API responses in REST APIs? Should there be validation for the response format?

💡 Result:

Short answer: use a small, consistent JSON object (or a consistent wrapper) describing the step (id, title, body/content, order, metadata, navigation/links) and validate it with a schema (OpenAPI/JSON Schema) on the server — yes, validate.

Suggested minimal structure (one of these patterns — pick what fits your API style):

  • Simple body (common for small APIs)
    {
    "id": "step-123",
    "title": "Install the SDK",
    "content": "Markdown or HTML string",
    "order": 2,
    "estimatedSeconds": 120,
    "completed": false,
    "resources": [ { "type":"link", "title":"SDK", "url":"..." } ],
    "nextStepId": "step-124",
    "prevStepId": "step-121"
    }

  • Wrapped (consistent wrapper for success/errors)
    {
    "success": true,
    "data": { ...step object above... },
    "meta": { "version":"v1", "updatedAt":"2025-09-20T12:00:00Z" }
    }

What to include (best practices)

  • id, title, content (plain text / markdown / HTML) and order/index.
  • Metadata: estimated duration, tags, difficulty, locale, created/updated timestamps.
  • Navigation: next/previous or links (HATEOAS) if you want discoverability.
  • Resources/attachments array for links, image IDs, code snippets.
  • Use HTTP status codes and a consistent error object (code, message, details). (speakeasy.com)

Validation: yes — validate the response format with a machine-readable schema (OpenAPI + JSON Schema) and enforce it server-side (and optionally test/validate in CI). Reasons:

  • Keeps contract stable for clients, avoids accidental breaking changes.
  • Enables automatic client generation, documentation and runtime checks.
  • Reject or log responses that include unexpected fields or missing required fields. (restack.io)

Practical checklist

  • Define the step model in your OpenAPI spec (or JSON Schema).
  • Mark required vs optional fields and whether extra properties are allowed.
  • Return consistent error payloads and proper HTTP codes.
  • Add integration tests that assert responses conform to the schema. (restack.io)

If you want, I can draft an OpenAPI/JSON Schema for your exact tutorial-step model or show sample validation code (Node/Express or Python/FastAPI).

Citations:


Validate tutorial-step API response before casting.

The function casts remote JSON to ApiResponse without runtime checks — add runtime validation (define the tutorial-step model in OpenAPI/JSON Schema and validate client-side with AJV or a Zod schema) inside agent-docs/src/agents/agent-pulse/tutorial.ts (getTutorialStep). On validation failure log the mismatch and return a consistent error payload (or a typed error) and add an integration/CI test to enforce the contract.

Copy link
Contributor

coderabbitai bot commented Sep 20, 2025

✅ Actions performed

Reviews paused.

@afterrburn afterrburn closed this Sep 20, 2025
@afterrburn afterrburn deleted the devin/1758395173-empty-pr-4 branch September 20, 2025 20:45
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.

1 participant