Conversation
- Created `governed-rag-answer.workflow.ts` to handle multi-agent governed retrieval-augmented generation (RAG) process including authentication, document retrieval, answer generation, and verification. - Added detailed logging and error handling throughout the workflow steps to enhance observability and debugging. - Implemented `governed-rag-index.workflow.ts` for indexing documents with security tags and classifications, ensuring proper handling of document indexing results. - Introduced new types in `types.ts` for better type safety and clarity, including `Principal`, `AccessFilter`, `Document`, and `Chunk`. - Added utility functions in `streamUtils.ts` for handling server-sent events (SSE) and streaming text data, including `createSSEStream`, `streamProgressUpdate`, and `handleTextStream`. - Developed comprehensive tests for streaming utilities in `streamUtils.test.ts` to ensure functionality and reliability. - Updated `tsconfig.cli.json` for improved TypeScript configuration in CLI context. - Documented the utilities directory and its purpose in `AGENTS.md` for better understanding and maintenance.
|
Keep this PR in a mergeable state → Learn moreAll Green is an AI agent that automatically: ✅ Addresses code review comments ✅ Fixes failing CI checks ✅ Resolves merge conflicts |
|
🤖 Hi @ssdeanx, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
Summary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughThis change introduces a comprehensive backend infrastructure for governed RAG with multi-tenant support, role-based access control, and tier-based quota management. New CLI commands enable document indexing and querying. UI enhancements include task parsing from assistant content, file attachment previews, and code sandbox support. Streaming utilities provide SSE handling for real-time updates. Changes
Sequence DiagramsequenceDiagram
participant Client
participant AuthService as Authentication<br/>Service
participant Retriever as Document<br/>Retriever
participant AnswerAgent as Answer<br/>Generator Agent
participant VerifierAgent as Verifier<br/>Agent
participant Response
Client->>AuthService: Authentication (JWT)
activate AuthService
AuthService-->>Client: Access Filter + Question
deactivate AuthService
Client->>Retriever: Retrieve Documents<br/>(with Access Filter)
activate Retriever
Retriever-->>Client: Contexts
deactivate Retriever
Client->>AnswerAgent: Generate Answer<br/>(Contexts + Question)
activate AnswerAgent
AnswerAgent-->>Client: Answer + Citations
deactivate AnswerAgent
Client->>VerifierAgent: Verify Answer<br/>(Security Policies)
activate VerifierAgent
alt Verification Success
VerifierAgent-->>Response: Return Answer + Citations
else Insufficient Evidence
VerifierAgent-->>Response: Return Fallback Answer
end
deactivate VerifierAgent
Estimated code review effort🎯 4 (Complex) | ⏱️ ~70 minutes Areas requiring extra attention:
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary of ChangesHello @ssdeanx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the application's Retrieval-Augmented Generation (RAG) capabilities by introducing governed, multi-agent workflows for both answering queries and indexing documents. It integrates robust role-based access control and subscription tier management to ensure data security and resource allocation. Furthermore, it provides a command-line interface for streamlined RAG operations and improves the chat UI with new features for displaying agent-generated tasks, file previews, and code snippets. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
🤖 I'm sorry @ssdeanx, but I was unable to process your request. Please see the logs for more details. |
Greptile OverviewGreptile SummaryThis PR implements a comprehensive governed RAG (Retrieval-Augmented Generation) system with multi-agent workflows, role-based access control, and subscription tier management. Key Changes:
Architecture Integration: Issues Found:
Confidence Score: 3/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant Client
participant Workflow as governedRagAnswer
participant AuthService as AuthenticationService
participant RetrieveAgent
participant VectorStore as PgVector
participant RerankAgent
participant AnswererAgent
participant VerifierAgent
Client->>Workflow: execute({jwt, question})
Note over Workflow: Step 1: Authentication
Workflow->>AuthService: authenticateAndAuthorize(jwt)
AuthService-->>Workflow: {accessFilter, principal}
Note over Workflow: Step 2: Retrieval & Rerank
Workflow->>RetrieveAgent: generate(question, accessFilter)
RetrieveAgent->>VectorStore: query with security filters
VectorStore-->>RetrieveAgent: contexts with securityTags
RetrieveAgent-->>Workflow: retrieved contexts
Workflow->>RerankAgent: generate(question, contexts)
RerankAgent-->>Workflow: reranked contexts
Note over Workflow: Step 3: Answer Generation
Workflow->>AnswererAgent: generate(question, contexts)
AnswererAgent-->>Workflow: {answer, citations}
Note over Workflow: Step 4: Verification
Workflow->>VerifierAgent: generate(answer, question, contexts)
VerifierAgent-->>Workflow: {ok, reason}
alt Verification Failed
Workflow-->>Client: Error: Security violation
else Verification Passed
Workflow-->>Client: {answer, citations}
end
|
| }), | ||
| execute: async ({ inputData, mastra }) => { | ||
| const startTime = Date.now() | ||
| const requestId = `REQ-${Date.now()}-${Math.random().toString(36).substr(2, 5)}` |
There was a problem hiding this comment.
style: use crypto module's randomBytes instead of Math.random() for better uniqueness
| const requestId = `REQ-${Date.now()}-${Math.random().toString(36).substr(2, 5)}` | |
| const requestId = `REQ-${Date.now()}-${crypto.randomBytes(3).toString('hex')}` |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/workflows/governed-rag-answer.workflow.ts
Line: 94:94
Comment:
**style:** use crypto module's `randomBytes` instead of `Math.random()` for better uniqueness
```suggestion
const requestId = `REQ-${Date.now()}-${crypto.randomBytes(3).toString('hex')}`
```
How can I resolve this? If you propose a fix, please make it concise.| } from '../schemas/agent-schemas' | ||
| import { AuthenticationService } from '../services/AuthenticationService' | ||
| import { log } from '../config/logger' | ||
| import { ChunkType } from '@mastra/core/stream'; |
There was a problem hiding this comment.
syntax: unused import - ChunkType is imported but never used in this file
| import { ChunkType } from '@mastra/core/stream'; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/workflows/governed-rag-answer.workflow.ts
Line: 23:23
Comment:
**syntax:** unused import - `ChunkType` is imported but never used in this file
```suggestion
```
How can I resolve this? If you propose a fix, please make it concise.| (typeof ctx.text === 'string' && | ||
| !ctx.text.includes( | ||
| 'The Termination Procedures are as follows' | ||
| )) && | ||
| (typeof ctx.text === 'string' && | ||
| !ctx.text.includes( | ||
| '# Git Workflow at ACME' | ||
| )) && | ||
| // Score must be realistic (0-1 range) | ||
| ctx.score >= 0 && |
There was a problem hiding this comment.
style: hardcoded strings like "The Termination Procedures" and "# Git Workflow at ACME" create brittle validation that breaks if these specific examples change - consider validating based on structural properties only (docId, score range, etc) without content-specific checks
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/workflows/governed-rag-answer.workflow.ts
Line: 310:319
Comment:
**style:** hardcoded strings like "The Termination Procedures" and "# Git Workflow at ACME" create brittle validation that breaks if these specific examples change - consider validating based on structural properties only (docId, score range, etc) without content-specific checks
How can I resolve this? If you propose a fix, please make it concise.| const indexName: string = | ||
| process.env.QDRANT_COLLECTION ?? 'governed_rag' |
There was a problem hiding this comment.
syntax: mismatch between environment variable name and comment - reads QDRANT_COLLECTION but comment says "PgVector"
| const indexName: string = | |
| process.env.QDRANT_COLLECTION ?? 'governed_rag' | |
| const indexName: string = | |
| process.env.PG_INDEX_NAME ?? 'governed_rag' |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/workflows/governed-rag-index.workflow.ts
Line: 50:51
Comment:
**syntax:** mismatch between environment variable name and comment - reads `QDRANT_COLLECTION` but comment says "PgVector"
```suggestion
const indexName: string =
process.env.PG_INDEX_NAME ?? 'governed_rag'
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Code Review
This pull request introduces governed RAG workflows, including indexing and answering processes. It adds new agents, services for authentication and tier management, and CLI commands to interact with these workflows. The changes also include significant updates to the chat UI to support new features like inline previews, code sandboxes, and task lists. My review focuses on improving type safety, fixing potential runtime errors, ensuring consistency, and addressing brittle implementation details. I've identified a critical issue with inconsistent embedding dimensions, several high-severity issues related to type safety and unsafe data handling, and medium-severity issues concerning code maintainability and correctness in streaming logic.
| try { | ||
| await vectorStore.createIndex({ | ||
| indexName, | ||
| dimension: 1568, // gemini-embedding-001 dimension (1568) |
There was a problem hiding this comment.
There is a critical inconsistency in the embedding dimension used. Here, it's hardcoded to 1568 for gemini-embedding-001. However, in src/mastra/config/pg-storage.ts, the dimension for the same model is specified as 3072. This mismatch will cause vector storage and query operations to fail. This value should be consistent across the codebase, ideally defined as a shared constant.
|
|
||
| const researchResult = | ||
| researchResponse.result.status.message?.parts?.[0]?.kind === 'text' | ||
| ? researchResponse.result.status.message.parts?.[0]?.text |
There was a problem hiding this comment.
There's a potential null pointer exception here. researchResponse.result.status.message can be undefined due to the optional chaining on line 111. However, on line 112, you are accessing message.parts without optional chaining (?). If message is undefined, this will throw a runtime error. You should use optional chaining here as well.
| ? researchResponse.result.status.message.parts?.[0]?.text | |
| ? researchResponse.result.status.message?.parts?.[0]?.text |
| log.info(`Current usage: ${usage.documentsIndexed} documents indexed`) | ||
| } | ||
|
|
||
| const sampleDocs: any[] = [ |
There was a problem hiding this comment.
Using any[] for sampleDocs and later for validDocs bypasses TypeScript's type safety. This can lead to hard-to-debug issues if the object structure is incorrect. It's highly recommended to define a specific interface or type for the document objects and use it here to ensure type safety and improve code clarity.
| const sampleDocs: any[] = [ | |
| interface SampleDoc { | |
| filePath: string; | |
| docId: string; | |
| classification: 'public' | 'internal' | 'confidential'; | |
| allowedRoles: string[]; | |
| tenant: string; | |
| source: string; | |
| } | |
| const sampleDocs: SampleDoc[] = [ |
| Date.now() - startTime | ||
| ) | ||
| // Log document results | ||
| const resultData = (result as any).result ?? result |
There was a problem hiding this comment.
Casting result to any and then accessing properties ((result as any).result) is not type-safe. The workflow's output schema is defined, so you should use a proper type assertion or, even better, ensure the workflow execution function returns a strongly-typed result. This will prevent potential runtime errors and improve maintainability.
| } catch (validationError) { | ||
| const validationErrorInfo = | ||
| validationError instanceof Error | ||
| ? { | ||
| message: validationError.message, | ||
| stack: validationError.stack, | ||
| } | ||
| : (() => { | ||
| try { | ||
| return { error: JSON.parse(JSON.stringify(validationError)) } | ||
| } catch { | ||
| return { error: String(validationError) } | ||
| } | ||
| })() | ||
| log.warn( | ||
| `[${requestId}] ⚠️ Schema validation failed, using raw contexts`, | ||
| validationErrorInfo | ||
| ) | ||
| contexts = toolResultData.contexts | ||
| } |
There was a problem hiding this comment.
In the catch block for Zod validation, the code falls back to using the unvalidated toolResultData.contexts. This is risky as it bypasses the schema validation and could pass malformed data to subsequent steps, potentially causing runtime errors or unexpected behavior. If validation fails, it should ideally throw an error or the data should be sanitized, not used as is.
| className="h-6 px-2 text-xs" | ||
| onClick={() => | ||
| setInlinePreview({ | ||
| id: (file as { id?: string }).id ?? `file-${idx}`, |
There was a problem hiding this comment.
The type assertion (file as { id?: string }) is not type-safe and might lead to runtime errors if the file object shape changes. The FileUIPart type does not guarantee an id property. A safer approach would be to use a type guard or optional chaining with a check to ensure the property exists before accessing it.
id: 'id' in file && typeof (file as any).id === 'string' ? (file as any).id : `file-${idx}`,
| if ('error' in response) { | ||
| throw new Error(response.error.message); | ||
| } else if ('messageId' in response.result) { | ||
| // Mastra's current A2A implementation will always return a task, rather than a message | ||
| return; | ||
| } |
There was a problem hiding this comment.
The pattern for checking for an error and then for a messageId in the response is repeated multiple times in this file (lines 49-54, 103-108, 130-135). This duplicated logic increases maintenance overhead. Consider extracting this into a reusable helper function to handle the A2A response, which would make the main logic cleaner and more robust.
| private async loadUsageStats( | ||
| tenant: string, | ||
| tier: SubscriptionTier | ||
| ): Promise<UsageStats> { | ||
| // Mock implementation - replace with database query | ||
| return { | ||
| tenant, | ||
| tier, | ||
| documentsIndexed: 0, | ||
| apiRequestsToday: 0, | ||
| totalUsers: 1, | ||
| lastReset: new Date(), | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Persist usage stats to storage | ||
| * TODO: Implement database integration | ||
| */ | ||
| private async persistUsageStats(usage: UsageStats): Promise<void> { | ||
| // Mock implementation - replace with database update | ||
| log.debug('Persisting usage stats', { usage }) | ||
| } |
There was a problem hiding this comment.
The loadUsageStats and persistUsageStats methods have mock implementations and TODO comments indicating they are not yet integrated with a database. While this is acceptable for development, it's a critical missing piece for production functionality. This should be addressed before this feature is considered complete.
| const indexName: string = | ||
| process.env.QDRANT_COLLECTION ?? 'governed_rag' |
There was a problem hiding this comment.
The variable indexName is being initialized using an environment variable named QDRANT_COLLECTION, but the code is using pgVector. This is misleading and could cause configuration issues. The environment variable should be renamed to reflect its use with PostgreSQL/PgVector, for example, PG_VECTOR_INDEX_NAME.
| const lines = chunk.split('\n') | ||
| for (const line of lines) { | ||
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | ||
| } | ||
| streamController.enqueue(encoder.encode('\n\n')) |
There was a problem hiding this comment.
The way SSE events are terminated here is inconsistent with streamJSONEvent and technically incorrect. This implementation sends data: ...\n followed by \n\n, resulting in an extra newline (...\n\n\n). While many clients might tolerate this, it creates an extra empty comment line after each event. To be correct and consistent, you should send data: ...\n for each line and then a single \n to terminate the event, resulting in the standard ...\n\n terminator.
const lines = chunk.split('\n')
for (const line of lines) {
streamController.enqueue(encoder.encode(`data: ${line}\n`))
}
// Terminate the event with a single extra newline to form a blank line.
streamController.enqueue(encoder.encode(`\n`))There was a problem hiding this comment.
Actionable comments posted: 36
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
app/chat/components/chat-messages.tsx (3)
36-106: Task import andextractTasksFromTexthelper look correct and robustThe new
AgentTask/TaskStepimports andextractTasksFromTextimplementation correctly parse checklist-style sections, infer status from[x]/[-]markers, and generate stable IDs per section/step. The regex and sanitation steps are reasonable for expected assistant output.If you expect very large responses, you might gate
extractTasksFromTextbehindisAssistantat the call site (or add a cheap early check for keywords) to avoid unnecessary parsing on user messages. Otherwise this is good as-is.
202-271: Message-level content parsing, previews, sandbox, and parsed tasks wiring are sound
- Using
rawContent = textPart?.text ?? ""as a single source for artifact extraction, plan extraction, and task extraction simplifies downstream logic and keeps the toolbarCopyButtonbehavior consistent.- Inline preview (
inlinePreview) and sandbox (sandboxPreview) state are kept local toMessageItem, and all usages are guarded (inlinePreview && …,sandboxPreview && …), so there are no hook order issues here.- Non-image attachment UI with “Preview” (using
AgentWebPreview) and “Download” (viawindow.open) is straightforward and respects the existing file-part model.- The mutually exclusive reasoning/chain-of-thought display (
hasChainOfThoughtStepsvsshouldShowReasoningFallback) is clear and matches the intended behavior.- Sandbox integration for the first code block and subsequent
AgentCodeSandboxrendering are correctly guarded bycodeBlocks.length > 0andsandboxPreview.You could slightly optimize by memoizing
extractedTasksonly whenisAssistant && rawContent(e.g.,useMemo(() => isAssistant ? extractTasksFromText(rawContent) : [], [isAssistant, rawContent])) since tasks are only rendered for assistant messages, but this is a minor perf nit.Also applies to: 318-370, 373-379, 411-440, 451-458
515-575: Move hooks above the early return to fix rules-of-hooks violation inWebPreviewPanel
WebPreviewPanelcallsuseChatContext()at the top level, then has an early return before callinguseCallbackhooks. This means the hooks only execute on some renders (whenpreviewis non-null), violating React's rules-of-hooks: the number and order of hooks changes between renders.Move the early return to after all hooks, and move the
previewcheck inside thehandleCodeChangecallback:function WebPreviewPanel({ preview }: { preview: WebPreviewData | null }) { const { setWebPreview, agentConfig } = useChatContext() - - if (!preview || !agentConfig?.features.webPreview) {return null} - const handleCodeChange = useCallback( (newCode: string) => { - if (preview) { + if (!preview) { return } setWebPreview({ ...preview, code: newCode, }) - } }, [preview, setWebPreview], ) const handleClose = useCallback(() => { setWebPreview(null) }, [setWebPreview]) + + if (!preview || !agentConfig?.features.webPreview) { + return null + }This keeps all hooks executing unconditionally in the same order on every render while preserving the existing behavior.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (16)
app/chat/components/chat-messages.tsx(14 hunks)lib/a2a.ts(1 hunks)package.json(1 hunks)src/AGENTS.md(1 hunks)src/cli/AGENTS.md(1 hunks)src/cli/index.ts(1 hunks)src/index.ts(1 hunks)src/mastra/config/role-service.ts(1 hunks)src/mastra/config/tier-management-service.ts(1 hunks)src/mastra/workflows/governed-rag-answer.workflow.ts(1 hunks)src/mastra/workflows/governed-rag-index.workflow.ts(1 hunks)src/types.ts(1 hunks)src/utils/AGENTS.md(1 hunks)src/utils/streamUtils.test.ts(1 hunks)src/utils/streamUtils.ts(1 hunks)tsconfig.cli.json(1 hunks)
🧰 Additional context used
📓 Path-based instructions (21)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
**/*.{js,jsx,ts,tsx}: Usenext/dynamicfor dynamic imports to load components only when needed, improving initial load time.
Usenext/imagecomponent for automatic image optimization, including lazy loading and responsive images.
Use React.memo to prevent unnecessary re-renders of components.
Use the<Link prefetch>tag to prefetch pages that are likely to be visited.
Use getServerSideProps, getStaticProps, or server components for fetching data on the server-side.
Use SWR or React Query for client-side data fetching and caching.
Use CSS Modules, Styled Components, or Tailwind CSS for component-level styling. Prefer Tailwind CSS for rapid development.
Use React Context, Zustand, Jotai, or Recoil for managing global state. Avoid Redux unless necessary.
Usereact-hook-formfor managing forms and validation.
Only fetch the data that is needed by the component to avoid over-fetching.
Avoid long-running synchronous operations in the main thread to prevent blocking.
Always usesetStateor hooks to update state instead of mutating state directly.
Include a complete dependency array inuseEffecthooks to prevent unexpected behavior.
Avoid writing server-side code in client components to prevent exposing secrets or causing unexpected behavior.
Usetry...catchblocks for handling errors in asynchronous operations.
Implement error boundary components usinggetDerivedStateFromErrororcomponentDidCatchlifecycle methods.
Sanitize user input to prevent Cross-Site Scripting (XSS) attacks. Be especially careful when rendering HTML directly from user input.
Store authentication tokens in HTTP-only cookies or local storage securely.
Implement role-based access control to restrict access to sensitive resources.
Clean up event listeners and timers inuseEffecthooks to avoid memory leaks.
Only update state when necessary to reduce the number of re-renders and improve performance.
Use immutable data structures and avoid mutating data directly to prevent unexpected...
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tslib/a2a.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.tsapp/chat/components/chat-messages.tsx
**/*.{js,ts}
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
Use parameterized queries or an ORM to prevent SQL injection attacks.
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tslib/a2a.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Never commit API keys or secrets to the repository; use maskSensitiveMessageData() helper from src/mastra/config/pg-storage.ts when logging
**/*.{ts,tsx}: Document interface and type definitions with TSDoc comments explaining their purpose and usage context
Document interface properties with /** */ comments explaining each field's purpose and constraints
Document generic type parameters with @template tags explaining what each type parameter represents
Use type guards with comments explaining the runtime validation logic being performed
Document advanced/complex TypeScript types with explanatory comments about their purpose and use cases
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tslib/a2a.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.tsapp/chat/components/chat-messages.tsx
src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Run eslint with --max-warnings=0 on src/**/*.{ts,tsx} to enforce linting standards
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.ts
**/*.{ts,tsx,js,jsx,py,java,cs,rb,go,rs,cpp,c,h,hpp,swift,kotlin,php,scala,clj,groovy,lua,sh,bash}
📄 CodeRabbit inference engine (.github/instructions/self-explanatory-code-commenting.instructions.md)
**/*.{ts,tsx,js,jsx,py,java,cs,rb,go,rs,cpp,c,h,hpp,swift,kotlin,php,scala,clj,groovy,lua,sh,bash}: Write code that speaks for itself. Comment only when necessary to explain WHY, not WHAT. Avoid obvious comments that state what the code literally does.
Avoid redundant comments that simply repeat what the code is doing
Keep comments accurate and up-to-date with code changes. Remove or update outdated comments that no longer match the implementation.
Write comments for complex business logic that explain the WHY behind specific calculations or business rules
Document non-obvious algorithms with comments explaining the algorithm choice and its reasoning
Add comments explaining what regex patterns match, especially for complex patterns
Document API constraints, rate limits, gotchas, and external dependencies with explanatory comments
Avoid commenting out dead code. Use version control instead of maintaining commented code blocks.
Do not maintain code change history or modification logs as comments. Rely on git history and commit messages instead.
Avoid decorative divider comments (e.g., lines of equals signs or asterisks) for section separation
Ensure comments are placed appropriately above or adjacent to the code they describe
Write comments using proper grammar, spelling, and professional language
Prefer self-documenting code with clear variable/function names over adding comments to explain unclear code
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tslib/a2a.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.tsapp/chat/components/chat-messages.tsx
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/instructions/self-explanatory-code-commenting.instructions.md)
**/*.{ts,tsx,js,jsx}: Document public APIs with TSDoc/JSDoc comments including parameter descriptions, return types, examples, and thrown exceptions
Add TSDoc comments to configuration constants and environment variables explaining their source, reasoning, or constraints
Use TSDoc annotation tags (TODO, FIXME, HACK, NOTE, WARNING, PERF, SECURITY, BUG, REFACTOR, DEPRECATED) to mark special comments
Include file headers with @fileoverview, @author, @copyright, and @license tags to document file purpose and ownership
Document function parameters with @param tags, return values with @returns tags, and exceptions with @throws tags in TSDoc comments
Use @see tags in TSDoc comments to reference related functions, methods, or documentation
Include @example tags in public API documentation with code examples showing typical usage
Files:
src/cli/index.tssrc/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/config/role-service.tssrc/utils/streamUtils.tslib/a2a.tssrc/index.tssrc/types.tssrc/utils/streamUtils.test.tssrc/mastra/workflows/governed-rag-answer.workflow.tssrc/mastra/config/tier-management-service.tsapp/chat/components/chat-messages.tsx
**/*.md
📄 CodeRabbit inference engine (.github/instructions/markdown.instructions.md)
**/*.md: Use appropriate heading levels (H2, H3, etc.) to structure markdown content. Do not use H1 headings, as these will be generated from the title. Use##for H2 and###for H3 in a hierarchical manner. Recommend restructuring if content includes H4 or higher levels.
Use bullet points (with-) or numbered lists (with1.) for lists in markdown. Indent nested lists with two spaces and ensure proper indentation and spacing.
Use fenced code blocks (triple backticks) for code snippets in markdown. Specify the language after the opening backticks for syntax highlighting (e.g.,csharp).
Use proper markdown syntax for links:[link text](URL). Ensure that link text is descriptive and URLs are valid and accessible.
Use proper markdown syntax for images:. Include a brief description of the image in the alt text for accessibility.
Use markdown tables (with|delimiters) for tabular data. Ensure proper formatting, alignment, and inclusion of headers.
Limit line length to 80 characters in markdown for readability. Use soft line breaks for long paragraphs.
Use appropriate whitespace in markdown to separate sections and improve readability. Use blank lines between sections and avoid excessive whitespace.
Include YAML front matter at the beginning of markdown files with required metadata fields:post_title,author1,post_slug,microsoft_alias,featured_image,categories(from /categories.txt),tags,ai_note,summary, andpost_date.
Files:
src/AGENTS.mdsrc/cli/AGENTS.mdsrc/utils/AGENTS.md
src/mastra/{tools,workflows}/**/*.ts
📄 CodeRabbit inference engine (src/mastra/AGENTS.md)
Use
RuntimeContextto enforce access control in tools and workflows
Files:
src/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/workflows/governed-rag-answer.workflow.ts
src/mastra/workflows/**/*.ts
📄 CodeRabbit inference engine (src/mastra/AGENTS.md)
Add new workflow definitions under
src/mastra/workflowsto orchestrate multi-step flowsUse Mastra DSL for multi-step workflow definitions in src/mastra/workflows
Files:
src/mastra/workflows/governed-rag-index.workflow.tssrc/mastra/workflows/governed-rag-answer.workflow.ts
src/mastra/config/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
src/mastra/config/**/*.ts: Centralize provider clients (Google, OpenAI, Anthropic, OpenRouter) and storage configuration in src/mastra/config
Use pg-storage with PgVector for vector store functionality in the configuration
Use src/mastra/config for role hierarchy and provider client configuration
Files:
src/mastra/config/role-service.tssrc/mastra/config/tier-management-service.ts
**/types.{ts,tsx}
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
Organize TypeScript type definitions and interfaces in
types.tsorinterfaces.tsfiles.
Files:
src/types.ts
**/*.test.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
**/*.test.{js,jsx,ts,tsx}: Write unit tests for individual components to ensure they are working correctly.
Use React Testing Library for component testing to encourage testing from a user perspective.
Mock external dependencies to isolate components during testing.
Use Jest or Mocha as a testing framework for unit and integration tests.
Use Mock Service Worker (msw) to intercept and mock API calls during testing.
Co-locate test files with components using a consistent naming convention (e.g.,ComponentName.test.js).
Test edge cases and error conditions to ensure components are robust.
Files:
src/utils/streamUtils.test.ts
**/*.test.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
Use Vitest for all unit testing in the Mastra project
Files:
src/utils/streamUtils.test.ts
**/app/**
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
Use the
app/directory structure for route handlers, server components, and client components (Next.js 13+). Prefer this over thepages/directory for new projects.
Files:
app/chat/components/chat-messages.tsx
**/components/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.github/instructions/next-js.instructions.md)
Use PascalCase for component file names (e.g.,
ComponentName.jsxorComponentName.tsx).
Files:
app/chat/components/chat-messages.tsx
app/**/*.{tsx,ts}
📄 CodeRabbit inference engine (app/AGENTS.md)
app/**/*.{tsx,ts}: Use Tailwind CSS 4 with oklch color variables for styling in Next.js App Router pages and layouts
Use React 19 latest features in component implementations within the app directory
Files:
app/chat/components/chat-messages.tsx
app/chat/components/**/*.{ts,tsx}
📄 CodeRabbit inference engine (app/chat/AGENTS.md)
app/chat/components/**/*.{ts,tsx}: Use AI SDK v5 types and patterns in chat components: import types like UIMessage, DynamicToolUIPart, TextUIPart, ReasoningUIPart from 'ai' and use type guard functions like isTextUIPart, isReasoningUIPart, isToolOrDynamicToolUIPart to filter message parts
Access message content through message.parts array using type guards rather than message.content, extracting specific parts like text using const textPart = message.parts?.find(isTextUIPart)
Handle Mastra stream chunk types correctly: use 'text-delta' for streaming text, 'reasoning-delta' for streaming reasoning (NOT 'reasoning'), 'tool-call' for tool invocation, 'tool-result' for tool completion, 'source' for research sources, and 'finish' for completion with usage data
Use AI Elements components (Conversation, Message, PromptInput, ModelSelector, Reasoning, Tool, Sources, Artifact) from the AI Elements library in their respective chat component files as specified in the architecture
Files:
app/chat/components/chat-messages.tsx
app/chat/components/chat-messages.tsx
📄 CodeRabbit inference engine (app/chat/AGENTS.md)
Implement ChatMessages component to display message list with streaming support, using Conversation and Message AI Elements components with proper handling of different message types
Files:
app/chat/components/chat-messages.tsx
app/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Next.js 16 App Router with React 19 for frontend development in the app/ directory
Files:
app/chat/components/chat-messages.tsx
{app,ui,src/components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Tailwind CSS 4 with oklch color variables for styling in UI components
Files:
app/chat/components/chat-messages.tsx
{app,src/components}/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
{app,src/components}/**/*.{ts,tsx}: Use shadcn/ui base components (34 components) located in ui/ directory for common UI elements
Use AI Elements library (30 components) from src/components/ai-elements/ for chat, reasoning, and canvas UIs
Files:
app/chat/components/chat-messages.tsx
🧬 Code graph analysis (5)
src/mastra/workflows/governed-rag-index.workflow.ts (2)
src/mastra/config/logger.ts (4)
logStepStart(72-88)logProgress(176-197)logStepEnd(90-109)logError(153-174)src/mastra/config/pg-storage.ts (1)
pgVector(81-87)
src/mastra/config/role-service.ts (2)
src/mastra/config/role-hierarchy.ts (4)
isValidRole(80-82)ROLE_HIERARCHY(16-36)getRoleLevel(73-75)getInheritorRoles(103-113)src/mastra/config/logger.ts (1)
log(14-18)
src/utils/streamUtils.test.ts (1)
src/utils/streamUtils.ts (4)
createSSEStream(13-48)streamProgressUpdate(57-69)streamJSONEvent(78-84)handleTextStream(100-120)
src/mastra/workflows/governed-rag-answer.workflow.ts (1)
src/mastra/config/logger.ts (5)
logStepStart(72-88)logStepEnd(90-109)logError(153-174)log(14-18)logAgentActivity(132-151)
src/mastra/config/tier-management-service.ts (2)
src/mastra/config/role-hierarchy.ts (1)
SubscriptionTier(8-8)src/mastra/config/logger.ts (1)
log(14-18)
🪛 Biome (2.1.2)
app/chat/components/chat-messages.tsx
[error] 520-520: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
[error] 529-529: This hook is being called conditionally, but all hooks must be called in the exact same order in every component render.
Hooks should not be called after an early return.
For React to preserve state between calls, hooks needs to be called unconditionally and always in the same order.
See https://reactjs.org/docs/hooks-rules.html#only-call-hooks-at-the-top-level
(lint/correctness/useHookAtTopLevel)
⏰ 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: Agent
- GitHub Check: Codacy Security Scan
🔇 Additional comments (23)
src/AGENTS.md (1)
1-59: Documentation structure looks good.The AGENTS.md file provides clear architectural guidance with well-organized sections covering persona, scope, key files, data flow, and dependency rules. The dependency rules table (lines 48-53) effectively documents module boundaries.
package.json (1)
22-22: LGTM on the format script update.Adding
dotenvx runprefix to the format script aligns with other scripts that use environment variables.lib/a2a.ts (1)
1-152: Overall structure is good for a demo file.The A2A example demonstrates a clear multi-step workflow with proper error handling via try/catch. The eslint-disable for console is appropriate for this demonstration code.
src/mastra/config/role-service.ts (3)
9-44: Well-implemented role expansion with proper fallback handling.The
expandRolesmethod correctly handles the role hierarchy lookup with nullish coalescing (line 29), preserves unknown roles for backward compatibility, and sorts by privilege level. The warning log for unknown roles is appropriate.
103-112: Good defensive handling of tenant parameter.The explicit check for string type and trimmed empty string handling with a warning log is thorough and prevents edge cases from creating invalid tags.
1-197: RoleService implementation is well-structured.The service provides a comprehensive API for role-based access control with proper TSDoc documentation, logging for edge cases, and consistent return types. The static method pattern is appropriate for stateless utility operations.
src/mastra/config/tier-management-service.ts (4)
1-16: LGTM!The file header documentation clearly describes the module's purpose. Imports are correctly sourced from the local config modules.
17-33: LGTM!The
UsageStatsandTierValidationResultinterfaces are well-defined with clear property names and appropriate optional fields.
215-225: Edge case:lastResetin the future causes immediate reset.If
lastResetis accidentally set to a future date,needsDailyResetreturnstrueimmediately (sincelastReset > resetTimeis false but the comparisonlastReset < resetTimewill be true). This is likely correct behavior, but consider adding a guard or logging for anomalous dates.
258-280: LGTM!The percentage calculation correctly handles unlimited quotas (
-1) by returning0.src/index.ts (1)
1-2: LGTM!Clean barrel exports for the package entry point, properly exposing the Mastra module and types.
src/mastra/workflows/governed-rag-index.workflow.ts (1)
1-14: LGTM!Imports are well-organized and correctly sourced from local config and services.
src/cli/index.ts (1)
281-286: LGTM!Entry point correctly guards with
require.main === moduleand handles fatal errors withprocess.exit(1).src/mastra/workflows/governed-rag-answer.workflow.ts (5)
31-77: LGTM!The authentication step is well-structured with proper schema validation, logging, and error handling.
359-407: Good fallback behavior on rerank failure.The reranking step properly falls back to original contexts if reranking fails, with appropriate logging. This is a robust pattern.
420-492: LGTM!The answer generation step has proper schema validation, sensible defaults for empty contexts, and comprehensive error handling.
543-567: Good handling of "insufficient evidence" edge case.The verification step correctly identifies and passes through answers that indicate no authorized documents were found, rather than failing verification.
601-624: LGTM!The workflow composition is clean and follows the expected pattern with proper chaining of steps.
src/utils/streamUtils.test.ts (1)
1-371: Comprehensive test coverage for streaming utilities.The test suite is well-structured and covers all exported functions with good edge case handling (empty streams, special characters, multi-line content). The use of Vitest aligns with the coding guidelines, and the integration test validates a realistic end-to-end workflow.
src/utils/streamUtils.ts (3)
13-48: LGTM!The SSE stream creation is well-implemented with proper error handling, completion signaling, and correct headers. The try-catch-finally pattern ensures the controller is always closed.
57-69: LGTM!The progress update event is correctly formatted as SSE data with proper JSON payload structure.
78-84: LGTM!Generic typing provides good flexibility. Note that
JSON.stringifycould throw on circular references, but this would be caught bycreateSSEStream's error handler.app/chat/components/chat-messages.tsx (1)
619-630: Streaming reasoning / chain-of-thought and WebPreviewPanel wiring look correct
streamingReasoningStepsis memoized fromstreamingReasoningand reusesparseReasoningToSteps, mirroring the per-message behavior.hasStreamingChainOfThoughtandshouldShowStreamingReasoningFallbackare correctly derived and ensure only one ofAgentChainOfThoughtorAgentReasoningis shown during streaming, aligned with the static rendering logic.- Passing
webPreviewdown as apreviewprop intoWebPreviewPanelcentralizes web preview handling cleanly in one place.Also applies to: 661-661, 687-693
| } else if ('messageId' in response.result) { | ||
| // Mastra's current A2A implementation will always return a task, rather than a message | ||
| return; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Silent early return may confuse users.
When the A2A implementation returns a task instead of a message, the script exits silently without any indication of what happened. Consider adding a log message before returning to improve debugging experience.
} else if ('messageId' in response.result) {
// Mastra's current A2A implementation will always return a task, rather than a message
+ console.log('ℹ️ Received task response (not a message). A2A task-based flow not demonstrated in this example.');
return;
}📝 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.
| } else if ('messageId' in response.result) { | |
| // Mastra's current A2A implementation will always return a task, rather than a message | |
| return; | |
| } | |
| } else if ('messageId' in response.result) { | |
| // Mastra's current A2A implementation will always return a task, rather than a message | |
| console.log('ℹ️ Received task response (not a message). A2A task-based flow not demonstrated in this example.'); | |
| return; | |
| } |
🤖 Prompt for AI Agents
In lib/a2a.ts around lines 51–54, the function silently returns when
response.result contains messageId (Mastra returns a task not a message); add a
log statement right before the return (e.g., processLogger.warn or console.warn
depending on existing logger) that clearly states we received a task instead of
a message and include relevant details from response.result (like messageId and
any task metadata) to aid debugging, then keep the early return behavior.
| } else if ('messageId' in researchResponse.result) { | ||
| // Mastra's current A2A implementation will always return a task, rather than a message | ||
| return; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add logging for silent early return.
Same issue as above - the silent return provides no feedback to the user.
} else if ('messageId' in researchResponse.result) {
// Mastra's current A2A implementation will always return a task, rather than a message
+ console.log('ℹ️ Research step returned task response. Exiting example.');
return;
}🤖 Prompt for AI Agents
In lib/a2a.ts around lines 105-108, the branch that returns early when
'messageId' exists in researchResponse.result is silent; add a log statement
before the return to surface this condition (e.g., use the module's logger or
console.warn/info) including relevant context such as messageId and any request
or task identifiers so callers can see why processing stopped; keep the log
level appropriate (warn/info) and include enough details to correlate to the
originating request.
| } else if ('messageId' in contentResponse.result) { | ||
| // Mastra's current A2A implementation will always return a task, rather than a message | ||
| return; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Add logging for silent early return.
Same issue - add user feedback before exiting.
} else if ('messageId' in contentResponse.result) {
// Mastra's current A2A implementation will always return a task, rather than a message
+ console.log('ℹ️ Content step returned task response. Exiting example.');
return;
}📝 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.
| } else if ('messageId' in contentResponse.result) { | |
| // Mastra's current A2A implementation will always return a task, rather than a message | |
| return; | |
| } | |
| } else if ('messageId' in contentResponse.result) { | |
| // Mastra's current A2A implementation will always return a task, rather than a message | |
| console.log('ℹ️ Content step returned task response. Exiting example.'); | |
| return; | |
| } |
🤖 Prompt for AI Agents
In lib/a2a.ts around lines 132–135 the function returns silently when
contentResponse.result contains a messageId; add a log statement immediately
before the return to surface this early-exit (include the fact of an A2A
messageId-based response and any non-sensitive identifiers such as messageId,
taskId or requestId available on contentResponse/result), using the module's
existing logger (or console.warn if none) and avoid printing full sensitive
payloads so callers get feedback instead of a silent no-op.
| "build-cli": "tsc -p tsconfig.cli.json", | ||
| "cli": "node dist/index.js", | ||
| "generate-index": "node dist/cli/index.js" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the actual CLI entrypoint structure
# Find all index.ts files
echo "=== All index.ts files ==="
find . -name "index.ts" -type f 2>/dev/null | sort
# Check src/cli/index.ts if it exists
echo -e "\n=== src/cli/index.ts (first 40 lines) ==="
if [ -f "src/cli/index.ts" ]; then
cat -n src/cli/index.ts | head -40
else
echo "File not found"
fi
# Check src/index.ts
echo -e "\n=== src/index.ts (first 40 lines) ==="
if [ -f "src/index.ts" ]; then
cat -n src/index.ts | head -40
else
echo "File not found"
fi
# Check if dist/cli/index.js exists
echo -e "\n=== Checking dist structure (relevant to runtime) ==="
if [ -d "dist/cli" ]; then
echo "dist/cli/ exists"
ls -la dist/cli/ 2>/dev/null | head -10
fi
if [ -f "dist/index.js" ]; then
echo "dist/index.js exists"
fiRepository: ssdeanx/AgentStack
Length of output: 2234
Fix the cli script to point to the correct CLI entry point.
The cli script points to dist/index.js, which is the library export file containing only re-exports. The actual CLI entry point is src/cli/index.ts with CLI-specific logic (document indexing, logging, tier validation). Change cli to "node dist/cli/index.js" to match the generate-index script and execute the correct entry point.
🤖 Prompt for AI Agents
In package.json around lines 23 to 25, the "cli" npm script incorrectly points
to "dist/index.js" (the library re-exports) instead of the actual CLI bundle;
update the "cli" script to use "node dist/cli/index.js" so it executes the CLI
entry point (matching the "generate-index" script) and runs the CLI-specific
logic.
| @@ -0,0 +1,60 @@ | |||
| <!-- AGENTS-META {"title":"Backend Source Root","version":"1.1.0","last_updated":"2025-10-08T08:00:26Z","applies_to":"/src","tags":["layer:backend","domain:rag","type:source","status:stable"],"status":"stable"} --> | |||
|
|
|||
| # Source Root (`/src`) | |||
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use H2 instead of H1 heading.
Per coding guidelines, H1 headings should not be used as they will be generated from the title. Change this to an H2 heading.
-# Source Root (`/src`)
+## Source Root (`/src`)📝 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.
| # Source Root (`/src`) | |
| ## Source Root (`/src`) |
🤖 Prompt for AI Agents
In src/AGENTS.md around line 3, the document uses an H1 heading ("# Source Root
(`/src`)") but coding guidelines require H2 headings; change the leading '#' to
'##' so the heading becomes H2 ("## Source Root (`/src`)") and ensure no other
H1 headings are present in this file.
| @@ -0,0 +1,35 @@ | |||
| <!-- AGENTS-META {"title":"Backend Utility Layer","version":"1.1.0","last_updated":"2025-10-08T08:00:26Z","applies_to":"/src/utils","tags":["layer:backend","domain:shared","type:utilities","status":"stable"],"status":"stable"} --> | |||
|
|
|||
| # Utilities Directory (`/src/utils`) | |||
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Use H2 instead of H1 heading.
Per coding guidelines, H1 headings should not be used. Change to H2.
-# Utilities Directory (`/src/utils`)
+## Utilities Directory (`/src/utils`)📝 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.
| # Utilities Directory (`/src/utils`) | |
| ## Utilities Directory (`/src/utils`) |
🤖 Prompt for AI Agents
In src/utils/AGENTS.md around line 3, the top-level H1 heading "# Utilities
Directory (`/src/utils`)" should be changed to an H2 per project guidelines;
edit the heading to use "## Utilities Directory (`/src/utils`)" (or equivalent
H2 syntax) and save the file so no H1 headings remain.
| **Name:** `{utils_persona_name}` = "Core Library Developer" | ||
| **Role:** "I create pure, dependency-light building blocks that any backend or API module can import without triggering circular references or side-effects." |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider replacing the template placeholder.
The {utils_persona_name} appears to be an unreplaced template variable. If this is intentional documentation of a pattern, consider adding a note clarifying this; otherwise, replace it with an actual value like the other AGENTS.md files.
🤖 Prompt for AI Agents
In src/utils/AGENTS.md around lines 7 to 8, the Name field uses an unreplaced
template variable `{utils_persona_name}`; replace the placeholder with the
actual persona name (e.g., Name: "Core Library Developer") so it matches the
Role line, or if the template is intentional add a short clarifying note
immediately below explaining this is a template variable and how to replace it
in derived files; ensure the final text uses plain literal names (no braces)
unless documenting the templating pattern.
| /** | ||
| * Emit any JSON object as a data event. | ||
| * Used for actions, tool responses, custom events, etc. | ||
| * | ||
| * @param controller - The stream controller to enqueue the event to | ||
| * @param eventData - The event data to serialize and send | ||
| */ | ||
| export function streamJSONEvent<T>( | ||
| controller: ReadableStreamDefaultController<Uint8Array>, | ||
| eventData: T | ||
| ): void { | ||
| const encoder = new TextEncoder() | ||
| controller.enqueue(encoder.encode(`data: ${JSON.stringify(eventData)}\n\n`)) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding @template tag for the generic type parameter.
Per coding guidelines, generic type parameters should be documented with @template tags explaining what each type parameter represents.
/**
* Emit any JSON object as a data event.
* Used for actions, tool responses, custom events, etc.
*
* @param controller - The stream controller to enqueue the event to
* @param eventData - The event data to serialize and send
+ * @template T - The type of event data to serialize (must be JSON-serializable)
*/
export function streamJSONEvent<T>(📝 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.
| /** | |
| * Emit any JSON object as a data event. | |
| * Used for actions, tool responses, custom events, etc. | |
| * | |
| * @param controller - The stream controller to enqueue the event to | |
| * @param eventData - The event data to serialize and send | |
| */ | |
| export function streamJSONEvent<T>( | |
| controller: ReadableStreamDefaultController<Uint8Array>, | |
| eventData: T | |
| ): void { | |
| const encoder = new TextEncoder() | |
| controller.enqueue(encoder.encode(`data: ${JSON.stringify(eventData)}\n\n`)) | |
| } | |
| /** | |
| * Emit any JSON object as a data event. | |
| * Used for actions, tool responses, custom events, etc. | |
| * | |
| * @param controller - The stream controller to enqueue the event to | |
| * @param eventData - The event data to serialize and send | |
| * @template T - The type of event data to serialize (must be JSON-serializable) | |
| */ | |
| export function streamJSONEvent<T>( | |
| controller: ReadableStreamDefaultController<Uint8Array>, | |
| eventData: T | |
| ): void { | |
| const encoder = new TextEncoder() | |
| controller.enqueue(encoder.encode(`data: ${JSON.stringify(eventData)}\n\n`)) | |
| } |
🤖 Prompt for AI Agents
In src/utils/streamUtils.ts around lines 71 to 84, the JSDoc for streamJSONEvent
is missing a @template tag for the generic type parameter T; add a @template T
line to the comment describing what T represents (e.g., "T - type of the event
data to be serialized and sent"), so the generic is documented per guidelines
and tools that consume JSDoc will show the generic's meaning.
| // Stream raw text chunks through data field | ||
| for await (const chunk of streamResult.textStream) { | ||
| chunks.push(chunk) | ||
| const lines = chunk.split('\n') | ||
| for (const line of lines) { | ||
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | ||
| } | ||
| streamController.enqueue(encoder.encode('\n\n')) | ||
| } |
There was a problem hiding this comment.
Extra newline in SSE event termination.
The current implementation produces an extra newline in the SSE output. Each data: line already ends with \n, so only one additional \n is needed to terminate the event (creating the required blank line), not two.
For a chunk like 'Hello':
- Current output:
data: Hello\n\n\n(3 newlines) - Correct SSE format:
data: Hello\n\n(2 newlines)
Most SSE parsers tolerate this, but it's technically non-compliant with the SSE specification.
for await (const chunk of streamResult.textStream) {
chunks.push(chunk)
const lines = chunk.split('\n')
for (const line of lines) {
streamController.enqueue(encoder.encode(`data: ${line}\n`))
}
- streamController.enqueue(encoder.encode('\n\n'))
+ streamController.enqueue(encoder.encode('\n'))
}📝 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.
| // Stream raw text chunks through data field | |
| for await (const chunk of streamResult.textStream) { | |
| chunks.push(chunk) | |
| const lines = chunk.split('\n') | |
| for (const line of lines) { | |
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | |
| } | |
| streamController.enqueue(encoder.encode('\n\n')) | |
| } | |
| // Stream raw text chunks through data field | |
| for await (const chunk of streamResult.textStream) { | |
| chunks.push(chunk) | |
| const lines = chunk.split('\n') | |
| for (const line of lines) { | |
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | |
| } | |
| streamController.enqueue(encoder.encode('\n')) | |
| } |
🤖 Prompt for AI Agents
In src/utils/streamUtils.ts around lines 109 to 117, the SSE event termination
writes an extra newline: each `data:` line already ends with `\n`, but the code
enqueues an additional `\n\n` after lines producing a total of three newlines
per event; change the final enqueue to a single `\n` (so the event ends with one
blank line total) by removing one of the newlines when enqueueing the event
terminator to produce `data: ...\n\n` instead of `data: ...\n\n\n`.
| { | ||
| "extends": "./tsconfig.json", | ||
| "compilerOptions": { | ||
| "noEmit": false, | ||
| "outDir": "dist", | ||
| "jsx": "react", | ||
| "module": "commonjs", | ||
| "moduleResolution": "node" | ||
| }, | ||
| "include": ["src/**/*"], | ||
| "exclude": ["node_modules", "app/**/*", "components/**/*", ".next/**/*"] | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider removing unnecessary jsx setting for CLI builds.
The CLI code in src/cli/index.ts doesn't appear to use JSX. Setting "jsx": "react" is harmless but adds unnecessary configuration. If the CLI will never use JSX, consider removing this option.
"compilerOptions": {
"noEmit": false,
"outDir": "dist",
- "jsx": "react",
"module": "commonjs",
"moduleResolution": "node"
},📝 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.
| { | |
| "extends": "./tsconfig.json", | |
| "compilerOptions": { | |
| "noEmit": false, | |
| "outDir": "dist", | |
| "jsx": "react", | |
| "module": "commonjs", | |
| "moduleResolution": "node" | |
| }, | |
| "include": ["src/**/*"], | |
| "exclude": ["node_modules", "app/**/*", "components/**/*", ".next/**/*"] | |
| } | |
| { | |
| "extends": "./tsconfig.json", | |
| "compilerOptions": { | |
| "noEmit": false, | |
| "outDir": "dist", | |
| "module": "commonjs", | |
| "moduleResolution": "node" | |
| }, | |
| "include": ["src/**/*"], | |
| "exclude": ["node_modules", "app/**/*", "components/**/*", ".next/**/*"] | |
| } |
🤖 Prompt for AI Agents
In tsconfig.cli.json lines 1-12, the "jsx": "react" compilerOption is
unnecessary for a CLI build; remove the "jsx" entry from the "compilerOptions"
object (or omit it entirely) so the CLI tsconfig uses the default non-JSX
behavior, then run the CLI TypeScript build to verify no breakages.
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive governed RAG (Retrieval-Augmented Generation) system with multi-agent workflows, role-based access control, subscription tier management, and document indexing capabilities. The implementation includes server-sent event streaming utilities, CLI tooling for operations, and comprehensive test coverage for streaming functionality.
Key Changes
- Multi-agent RAG workflows: Implements authentication → retrieval+rerank → answer generation → verification pipeline with security-first design
- Document indexing system: Adds workflow for indexing documents with security tags, classifications, and role-based access controls using PgVector
- Streaming utilities: Provides reusable SSE streaming helpers for progress updates, JSON events, and text streaming with comprehensive test coverage
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 24 comments.
Show a summary per file
| File | Description |
|---|---|
tsconfig.cli.json |
New TypeScript configuration for CLI builds with CommonJS output |
package.json |
Added CLI build and execution scripts |
src/types.ts |
Defines core type interfaces for Principal, AccessFilter, Document, and Chunk |
src/index.ts |
Exports public API for mastra instance and types |
src/utils/streamUtils.ts |
SSE streaming utilities for data-only format with progress updates and JSON events |
src/utils/streamUtils.test.ts |
Comprehensive test suite covering SSE stream creation, progress updates, JSON events, and text streaming |
src/utils/AGENTS.md |
Documentation for utilities directory structure and streaming patterns |
src/mastra/workflows/governed-rag-index.workflow.ts |
Document indexing workflow with security tags and PgVector integration |
src/mastra/workflows/governed-rag-answer.workflow.ts |
Multi-step RAG answer workflow with authentication, retrieval, reranking, answer generation, and verification |
src/mastra/config/tier-management-service.ts |
Subscription tier validation, quota enforcement, and usage tracking service |
src/mastra/config/role-service.ts |
Role hierarchy management with role expansion and access control logic |
src/cli/index.ts |
CLI entry point with commands for indexing, querying, and usage monitoring |
src/cli/AGENTS.md |
CLI documentation describing command surface and execution flow |
src/AGENTS.md |
Source root documentation outlining modular boundaries and dependency rules |
lib/a2a.ts |
Example implementation of agent-to-agent communication protocol |
app/chat/components/chat-messages.tsx |
UI enhancements for task parsing, file previews, and reasoning display |
Comments suppressed due to low confidence (9)
src/cli/index.ts:47
- Unexpected any. Specify a different type.
const sampleDocs: any[] = [
src/cli/index.ts:77
- Unexpected any. Specify a different type.
const validDocs: any[] = []
src/cli/index.ts:108
- Unexpected any. Specify a different type.
result as any,
src/cli/index.ts:112
- Unexpected any. Specify a different type.
const resultData = (result as any).result ?? result
src/cli/index.ts:115
- Unexpected any. Specify a different type.
resultData.documents.forEach((doc: any) => {
src/cli/index.ts:176
- Unexpected any. Specify a different type.
const resultData = (result as any).result ?? result
src/cli/index.ts:196
- Unexpected any. Specify a different type.
resultData.citations.forEach((citation: any) => {
src/mastra/workflows/governed-rag-answer.workflow.ts:20
- Unused imports jwtClaimsSchema, verificationResultSchema.
import {
jwtClaimsSchema,
accessFilterSchema,
documentContextSchema,
ragAnswerSchema,
verificationResultSchema,
} from '../schemas/agent-schemas'
src/mastra/workflows/governed-rag-answer.workflow.ts:23
- Unused import ChunkType.
import { ChunkType } from '@mastra/core/stream';
| // Stream raw text chunks through data field | ||
| for await (const chunk of streamResult.textStream) { | ||
| chunks.push(chunk) | ||
| const lines = chunk.split('\n') | ||
| for (const line of lines) { | ||
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | ||
| } | ||
| streamController.enqueue(encoder.encode('\n\n')) |
There was a problem hiding this comment.
The code splits chunks by newlines and sends each line as a separate SSE data event without any sanitization. If the chunk contains content that could be interpreted as SSE control sequences (e.g., lines starting with "event:", "data:", or "id:"), this could cause protocol confusion or injection issues. Consider sanitizing or escaping the content before sending it through the SSE stream, or use a more robust encoding method.
| // Stream raw text chunks through data field | |
| for await (const chunk of streamResult.textStream) { | |
| chunks.push(chunk) | |
| const lines = chunk.split('\n') | |
| for (const line of lines) { | |
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | |
| } | |
| streamController.enqueue(encoder.encode('\n\n')) | |
| // Stream raw text chunks through data field, JSON-encoded to prevent SSE injection | |
| for await (const chunk of streamResult.textStream) { | |
| chunks.push(chunk) | |
| streamController.enqueue(encoder.encode(`data: ${JSON.stringify(chunk)}\n\n`)) |
| const lines = chunk.split('\n') | ||
| for (const line of lines) { | ||
| streamController.enqueue(encoder.encode(`data: ${line}\n`)) | ||
| } | ||
| streamController.enqueue(encoder.encode('\n\n')) |
There was a problem hiding this comment.
The comment "Stream raw text chunks through data field" doesn't fully explain the unusual behavior of splitting chunks by newlines and sending each line separately, followed by an empty line. This non-standard approach to streaming text could be confusing to maintainers. Consider adding more detailed documentation explaining why this split-by-newline approach is used and how clients should reassemble the text.
| const possibleToolNames = [ | ||
| 'pgQuery', | ||
| 'pgQueryTool', | ||
| 'vectorQueryTool', | ||
| 'vector-query', | ||
| 'vectorQuery', | ||
| 'pgQueryTool', | ||
| 'pg-query', | ||
| 'graphQueryTool', | ||
| 'graph-query', | ||
| 'graphQuery', | ||
| 'textQueryTool', | ||
| 'text-query', | ||
| 'textQuery', | ||
| ] |
There was a problem hiding this comment.
The list of possible tool names (lines 165-179) contains duplicate entries: 'pgQueryTool' appears twice (lines 167 and 171). This duplication is unnecessary and should be removed to improve code clarity and maintainability.
| title: preview.title, | ||
| }} | ||
| onClose={handleClose} | ||
| // onCodeChange={handleCodeChange} |
There was a problem hiding this comment.
Another instance of commented-out code (// onCodeChange={handleCodeChange}). This should be removed to keep the codebase clean and maintainable.
| // onCodeChange={handleCodeChange} |
| // Use nullish coalescing to avoid treating empty arrays as falsy | ||
| // and to satisfy linters that warn about object values in boolean conditionals. |
There was a problem hiding this comment.
The comment explaining the use of nullish coalescing (lines 27-28) is overly detailed for a straightforward operation. While it's good to document why you're using ?? instead of ||, this level of explanation is excessive for standard TypeScript patterns. Consider simplifying to just "// Get inherited roles, defaulting to empty array" or remove the comment entirely as the code is self-explanatory.
| // Use nullish coalescing to avoid treating empty arrays as falsy | |
| // and to satisfy linters that warn about object values in boolean conditionals. |
| } from '../schemas/agent-schemas' | ||
| import { AuthenticationService } from '../services/AuthenticationService' | ||
| import { log } from '../config/logger' | ||
| import { ChunkType } from '@mastra/core/stream'; |
There was a problem hiding this comment.
'ChunkType' is defined but never used.
| import { ChunkType } from '@mastra/core/stream'; |
| tenant: string, | ||
| tier: SubscriptionTier, | ||
| operationType: 'document' | 'api_request' | 'user' | ||
| ): Promise<TierValidationResult> { |
There was a problem hiding this comment.
Callee is not a function: it has type undefined.
| ): Promise<TierValidationResult> { | |
| ): Promise<TierValidationResult> { | |
| if (typeof getTierQuota !== 'function') { | |
| throw new Error('getTierQuota is not a function. Check the import from ../config/role-hierarchy.'); | |
| } |
| tier: SubscriptionTier, | ||
| feature: string | ||
| ): TierValidationResult { | ||
| const allowed = isTierFeatureEnabled(tier, feature) |
There was a problem hiding this comment.
Callee is not a function: it has type undefined.
| const allowed = isTierFeatureEnabled(tier, feature) | |
| let allowed: boolean | |
| if (typeof isTierFeatureEnabled === 'function') { | |
| allowed = isTierFeatureEnabled(tier, feature) | |
| } else { | |
| log.error?.( | |
| "isTierFeatureEnabled is not a function (got: %s). Feature access validation failed for tier: %s, feature: %s", | |
| typeof isTierFeatureEnabled, | |
| tier, | |
| feature | |
| ) | |
| return { | |
| allowed: false, | |
| tier, | |
| reason: "Feature access validation is not available due to a configuration error.", | |
| upgradeRequired: false, | |
| } | |
| } |
| tier: SubscriptionTier, | ||
| classification: 'public' | 'internal' | 'confidential' | ||
| ): TierValidationResult { | ||
| const minimumTier = getMinimumTierForClassification(classification) |
There was a problem hiding this comment.
Callee is not a function: it has type undefined.
| documents: number | ||
| apiRequests: number | ||
| users: number | ||
| } { |
There was a problem hiding this comment.
Callee is not a function: it has type undefined.
| } { | |
| } { | |
| if (typeof getTierQuota !== 'function') { | |
| throw new Error('getTierQuota is not a function. Check the import from ../config/role-hierarchy.'); | |
| } |
governed-rag-answer.workflow.tsto handle multi-agent governed retrieval-augmented generation (RAG) process including authentication, document retrieval, answer generation, and verification.governed-rag-index.workflow.tsfor indexing documents with security tags and classifications, ensuring proper handling of document indexing results.types.tsfor better type safety and clarity, includingPrincipal,AccessFilter,Document, andChunk.streamUtils.tsfor handling server-sent events (SSE) and streaming text data, includingcreateSSEStream,streamProgressUpdate, andhandleTextStream.streamUtils.test.tsto ensure functionality and reliability.tsconfig.cli.jsonfor improved TypeScript configuration in CLI context.AGENTS.mdfor better understanding and maintenance.