Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import js from '@eslint/js'
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from '@typescript-eslint/eslint-plugin'
import tsparser from '@typescript-eslint/parser'
import prettierConfig from 'eslint-config-prettier'
import { globalIgnores } from "eslint/config";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

style: Verify that globalIgnores export exists in eslint/config - this import path may not be valid for ESLint

Prompt To Fix With AI
This is a comment left during a code review.
Path: eslint.config.js
Line: 8:8

Comment:
**style:** Verify that `globalIgnores` export exists in `eslint/config` - this import path may not be valid for ESLint

How can I resolve this? If you propose a fix, please make it concise.

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The import { globalIgnores } from "eslint/config" appears to be incorrect. ESLint does not export a globalIgnores function from "eslint/config". The standard way to define ignores in flat config is to use the ignores property in configuration objects. The ignores array already defined on line 10 should be sufficient. This import and its usage on line 53 should be removed.

Suggested change
import { globalIgnores } from "eslint/config";

Copilot uses AI. Check for mistakes.

const ignores = [
'eslint.config.js',
Expand Down Expand Up @@ -46,7 +50,10 @@ const ignores = [

export default [
{ ignores },
globalIgnores(["dist", "node_modules"]),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The use of globalIgnores here seems redundant. The ignores array defined on line 10 already includes patterns for dist/** and node_modules/**, and it's applied globally on line 52. To avoid redundancy and keep the configuration clean, you can remove this line.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

style: This globalIgnores call is redundant - dist and node_modules are already in the ignores array above (lines 14-15). Consider removing this line.

Prompt To Fix With AI
This is a comment left during a code review.
Path: eslint.config.js
Line: 53:53

Comment:
**style:** This `globalIgnores` call is redundant - `dist` and `node_modules` are already in the `ignores` array above (lines 14-15). Consider removing this line.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines 52 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove redundant globalIgnores call.

The globalIgnores(["dist", "node_modules"]) duplicates entries already present in the ignores array (lines 13-14: 'node_modules/**' and 'dist/**'). This redundancy adds no value and may cause confusion.

     { ignores },
-    globalIgnores(["dist", "node_modules"]),
     js.configs.recommended,
📝 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
{ ignores },
globalIgnores(["dist", "node_modules"]),
{ ignores },
js.configs.recommended,
🤖 Prompt for AI Agents
In eslint.config.js around lines 52 to 53, remove the redundant
globalIgnores(["dist", "node_modules"]) call because those patterns are already
present in the ignores array (lines ~13-14 as 'node_modules/**' and 'dist/**');
simply delete the globalIgnores(...) entry so ignores are defined in a single
place to avoid duplication and confusion.

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

This usage of globalIgnores(["dist", "node_modules"]) is incorrect and will cause a runtime error. The globalIgnores function does not exist in ESLint's API. Additionally, these paths are already included in the ignores array defined earlier (lines 13-14). This line should be removed.

Copilot uses AI. Check for mistakes.
js.configs.recommended,
reactHooks.configs.flat.recommended,
reactRefresh.configs.next,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

style: Verify that reactRefresh.configs.next exists - the standard config is typically reactRefresh.configs.recommended or a custom flat config object

Prompt To Fix With AI
This is a comment left during a code review.
Path: eslint.config.js
Line: 56:56

Comment:
**style:** Verify that `reactRefresh.configs.next` exists - the standard config is typically `reactRefresh.configs.recommended` or a custom flat config object

How can I resolve this? If you propose a fix, please make it concise.

Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The configuration reference reactRefresh.configs.next appears to be incorrect. The eslint-plugin-react-refresh package typically exports configs.recommended or configs.vite, not configs.next. This should be verified against the actual plugin documentation and corrected to use a valid configuration export.

Suggested change
reactRefresh.configs.next,
reactRefresh.configs.recommended,

Copilot uses AI. Check for mistakes.
prettierConfig,
{
files: ['**/*.ts', '**/*.tsx'],
Expand All @@ -55,6 +62,7 @@ export default [
},
languageOptions: {
parser: tsparser,
globals: globals.browser,
Comment on lines 63 to +65
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): Applying globals.browser to all *.ts / *.tsx files may hide issues in Node-only code.

Because this config applies globals.browser to all TS/TSX files, browser globals like window will appear valid even in server-side/Node-only modules, potentially hiding real bugs. Consider limiting globals.browser to known client/React files (e.g., narrower files patterns or a separate override) and using appropriate Node globals for backend code instead.

parserOptions: {
ecmaVersion: 2022,
sourceType: 'module',
Expand Down
7 changes: 7 additions & 0 deletions lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { type ClassValue, clsx } from "clsx"
import { twMerge } from "tailwind-merge"
import { v4 as uuid } from "@lukeed/uuid";
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

syntax: Missing dependency: @lukeed/uuid is imported but not present in package.json

Suggested change
import { v4 as uuid } from "@lukeed/uuid";
import { v4 as uuid } from "uuid";
Prompt To Fix With AI
This is a comment left during a code review.
Path: lib/utils.ts
Line: 3:3

Comment:
**syntax:** Missing dependency: `@lukeed/uuid` is imported but not present in `package.json`

```suggestion
import { v4 as uuid } from "uuid";
```

How can I resolve this? If you propose a fix, please make it concise.


export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
export function newThreadLink(
rootPath: string,
agentId: string,
): `/${string}/${string}/chat/${string}?new=true` {
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The function newThreadLink has a return type that is too specific. The template literal type /${string}/${string}/chat/${string}?new=true doesn't accurately represent the actual return value since uuid() returns a specific string format, not just any string. Consider using a more appropriate return type like string or a branded type if type safety for URLs is important.

Suggested change
): `/${string}/${string}/chat/${string}?new=true` {
): string {

Copilot uses AI. Check for mistakes.
return `/${rootPath}/${agentId}/chat/${uuid()}?new=true`;
Comment on lines +8 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion: Consider normalizing rootPath and agentId to avoid accidental double slashes in the generated URL.

If callers pass rootPath with or without a leading slash, or an agentId that already includes slashes, this helper can generate malformed URLs like //agents//id/chat/.... Normalizing inputs (e.g., trimming leading/trailing slashes) within this function would make it safer and more predictable to use.

}
Comment on lines +8 to +13
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add TSDoc documentation and consider input validation.

The function lacks TSDoc documentation. Per coding guidelines, public APIs should include parameter descriptions, return types, and examples. Additionally, consider validating/sanitizing rootPath and agentId to prevent malformed URLs (e.g., if they contain slashes or special characters).

+/**
+ * Generates a new chat thread URL path with a unique identifier.
+ * @param rootPath - The root path segment (e.g., 'app', 'dashboard')
+ * @param agentId - The agent identifier
+ * @returns A URL path in the format `/{rootPath}/{agentId}/chat/{uuid}?new=true`
+ * @example
+ * newThreadLink('app', 'agent-123')
+ * // => '/app/agent-123/chat/550e8400-e29b-41d4-a716-446655440000?new=true'
+ */
 export function newThreadLink(
   rootPath: string,
   agentId: string,
 ): `/${string}/${string}/chat/${string}?new=true` {
   return `/${rootPath}/${agentId}/chat/${uuid()}?new=true`;
 }
🤖 Prompt for AI Agents
In lib/utils.ts around lines 8–13, add a TSDoc block for newThreadLink
describing parameters (rootPath, agentId), the template-literal return type, and
an example usage; then validate/sanitize inputs by trimming and removing
leading/trailing slashes from rootPath and agentId, ensure they are non-empty
(throw or return a clear error), and URL-encode each segment (e.g., with
encodeURIComponent) before composing the final
`/${rootPath}/${agentId}/chat/${uuid()}?new=true` string so the generated URL is
well-formed and safe.

114 changes: 57 additions & 57 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
"diff": "^8.0.2",
"dotenv": "^17.2.3",
"embla-carousel-react": "^8.6.0",
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.4.23",
Comment on lines +122 to +123
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Move ESLint plugins to devDependencies.

ESLint plugins are development-time tools used for linting, not runtime dependencies. They should be in devDependencies to avoid unnecessary installation in production environments.

Move these entries from dependencies to devDependencies:

 "devDependencies": {
     "@mdx-js/loader": "^3.1.1",
     ...
+    "eslint-plugin-react-hooks": "^7.0.1",
+    "eslint-plugin-react-refresh": "^0.4.23",
     ...
 }

And remove from dependencies.

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

🤖 Prompt for AI Agents
In package.json around lines 122-123, the ESLint plugins
"eslint-plugin-react-hooks" and "eslint-plugin-react-refresh" are incorrectly
listed under dependencies; move these two entries into devDependencies and
remove them from dependencies (update package.json accordingly or run npm
install --save-dev eslint-plugin-react-hooks@^7.0.1
eslint-plugin-react-refresh@^0.4.23 and remove the entries from dependencies),
then run an install to update lockfile.

"excalidraw-to-svg": "^3.1.0",
"fast-glob": "^3.3.3",
"fast-xml-parser": "^5.3.3",
Expand Down Expand Up @@ -216,4 +218,4 @@
"body-parser": "^2.2.1",
"jws": "^4.0.1"
}
}
}
11 changes: 6 additions & 5 deletions src/mastra/workflows/document-processing-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ const convertPdfToMarkdownStep = createStep({
data: {
status: 'in-progress',
message: 'Converting PDF to markdown `...',
stage: "documentProcessingAgent",
stage: "convert-pdf-to-markdown",
Comment on lines 271 to +272
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo: stray backtick in message string.

The message contains an errant backtick that appears unintentional.

           status: 'in-progress',
-          message: 'Converting PDF to markdown `...',
+          message: 'Converting PDF to markdown...',
           stage: "convert-pdf-to-markdown",
📝 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
message: 'Converting PDF to markdown `...',
stage: "documentProcessingAgent",
stage: "convert-pdf-to-markdown",
status: 'in-progress',
message: 'Converting PDF to markdown...',
stage: "convert-pdf-to-markdown",
🤖 Prompt for AI Agents
In src/mastra/workflows/document-processing-workflow.ts around lines 271-272,
the log/message string "Converting PDF to markdown `..." contains a stray
backtick; remove the backtick so the message reads "Converting PDF to
markdown..." (ensure punctuation and string quoting remain valid after edit).

},
id: "convert-pdf-to-markdown",
});
Expand Down Expand Up @@ -318,7 +318,7 @@ const convertPdfToMarkdownStep = createStep({
data: {
status: "in-progress",
message: 'Markdown conversion complete (90%)...',
stage: "documentProcessingAgent",
stage: "convert-pdf-to-markdown",
},
id: "convert-pdf-to-markdown",
});
Expand All @@ -345,7 +345,7 @@ const convertPdfToMarkdownStep = createStep({
data: {
status: "done",
message: "PDF converted to markdown successfully",
stepId: 'convert-pdf-to-markdown',
stage: 'convert-pdf-to-markdown',
},
id: "convert-pdf-to-markdown",
});
Expand All @@ -360,9 +360,10 @@ const convertPdfToMarkdownStep = createStep({
logError('convert-pdf-to-markdown', error, { contentType: inputData.contentType });

await writer?.custom({
type: 'data-workflow-step-error',
type: 'data-tool-progress',
data: {
stepId: 'convert-pdf-to-markdown',
stage: 'convert-pdf-to-markdown',
status: "done",
error: error instanceof Error ? error.message : 'Unknown error',
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

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

The error data structure is inconsistent with other progress updates. Similar to other error handlers in this file, consider adding a 'message' field to provide better context. The current structure only includes 'stage', 'status', and 'error', while success cases include descriptive messages.

Suggested change
error: error instanceof Error ? error.message : 'Unknown error',
error: error instanceof Error ? error.message : 'Unknown error',
message: `Error in convert-pdf-to-markdown: ${error instanceof Error ? error.message : 'Unknown error'}`,

Copilot uses AI. Check for mistakes.
},
id: "convert-pdf-to-markdown",
Expand Down
37 changes: 22 additions & 15 deletions src/mastra/workflows/repo-ingestion-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,29 +177,36 @@ const ingestStep = createStep({

const result = await mdocumentChunker.execute({
documentContent: content,
documentMetadata: { filePath, source: githubRepo ? 'github' : 'local' },
documentMetadata: { filePath, source: (githubRepo) ? 'github' : 'local' },
chunkingStrategy: strategy,
indexName: 'memory_messages_3072',
generateEmbeddings: true,
embeddingModel: 'google/gemini-embedding-001',
embeddingBatchSize: 50,
Comment on lines +184 to +185
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider making embedding configuration parameters configurable.

The embedding model and batch size are hardcoded. For flexibility, consider adding these as optional inputs to the workflow schema with sensible defaults.

// In scanInputSchema, add optional embedding config:
embeddingModel: z.string().default('google/gemini-embedding-001'),
embeddingBatchSize: z.number().default(50),
🤖 Prompt for AI Agents
In src/mastra/workflows/repo-ingestion-workflow.ts around lines 184-185, the
embeddingModel and embeddingBatchSize are hardcoded; update the scan input
schema to accept optional embeddingModel (default 'google/gemini-embedding-001')
and embeddingBatchSize (default 50), then replace the hardcoded values by
reading those fields from the validated input (falling back to the defaults for
backward compatibility) so the workflow can be configured per-run without
breaking existing callers.

chunkSize: 512,
chunkOverlap: 50,
chunkSeparator: '\n'
}, { writer, requestContext });

if (result && typeof result === 'object' && 'success' in result && result.success) {
processedFiles++;
totalChunks += result.chunkCount;
}
else if (result instanceof ZodError) {
throw new Error(result.message);
}
else if (result instanceof Error) {
throw result;
} else if (result && typeof result === 'object' && 'error' in result) {
throw new Error(typeof result.error === 'string' ? result.error : 'Unknown error in chunking');
} else {
throw new Error('Unknown error in chunking');
}
const isSuccessResult = (r: unknown): r is { success: true; chunkCount?: number } =>
typeof r === 'object' && r !== null && 'success' in r && (r as { success: unknown }).success === true;

const isErrorObject = (r: unknown): r is { error: unknown } =>
typeof r === 'object' && r !== null && 'error' in r;

if (isSuccessResult(result)) {
processedFiles++;
totalChunks += result.chunkCount ?? 0;
} else if (result instanceof ZodError) {
throw new Error(result.message);
} else if (result instanceof Error) {
throw result;
} else if (isErrorObject(result)) {
const err = result.error;
throw new Error(typeof err === 'string' ? err : 'Unknown error in chunking');
} else {
throw new Error('Unknown error in chunking');
}

} catch (error) {
const errorObj = error instanceof Error ? error : new Error(String(error));
Expand Down
40 changes: 23 additions & 17 deletions src/mastra/workflows/research-synthesis-workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ const initializeResearchStep = createStep({
await writer?.custom({
type: 'data-tool-progress',
data: {
status: "in-progress",
message: `Researching topic: ${inputData.topics}...`,
stage: "researchAgent",
status: "in-progress",
message: `Researching topic: ${inputData.topics}...`,
stage: "researchAgent",
},
id: 'initialize-research',
});
Comment on lines 72 to 80
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Inconsistent stage naming in initializeResearchStep.

This step uses stage: "researchAgent" while other steps in this file use their step IDs (e.g., 'research-topic-item', 'synthesize-research'). For consistency, consider updating to stage: 'initialize-research'.

       await writer?.custom({
         type: 'data-tool-progress',
         data: {
           status: "in-progress",
           message: `Researching topic: ${inputData.topics}...`,
-          stage: "researchAgent",
+          stage: "initialize-research",
         },
         id: 'initialize-research',
       });
🤖 Prompt for AI Agents
In src/mastra/workflows/research-synthesis-workflow.ts around lines 72 to 80,
the progress event sets stage: "researchAgent" which is inconsistent with other
steps; update the stage value to 'initialize-research' so it matches the step ID
(and keeps stage naming consistent across the file), leaving the rest of the
writer.custom payload unchanged.

Expand All @@ -84,7 +84,7 @@ const initializeResearchStep = createStep({
maxSources: inputData.maxSourcesPerTopic,
}));


logStepEnd('initialize-research', { topicsCount: topics.length }, Date.now() - startTime);

return topics;
Expand All @@ -109,7 +109,7 @@ const researchTopicStep = createStep({
data: {
status: 'in-progress',
message: `Researching topic: ${inputData.topic}...`,
stage: 'workflow',
stage: 'research-topic-item',
},
id: 'research-topic-item',
});
Expand All @@ -128,7 +128,7 @@ const researchTopicStep = createStep({
data: {
status: 'in-progress',
message: `Researching topic: ${inputData.topic}...`,
stage: 'workflow',
stage: 'research-topic-item',
},
id: 'research-topic-item',
});
Expand Down Expand Up @@ -189,7 +189,7 @@ const researchTopicStep = createStep({
data: {
status: 'done',
message: `Research completed for ${inputData.topic}...`,
stage: 'workflow',
stage: 'research-topic-item',
},
id: 'research-topic-item',
});
Expand All @@ -211,9 +211,9 @@ const researchTopicStep = createStep({
await writer?.custom({
type: 'data-tool-progress',
data: {
status: 'in-progress',
status: 'done',
message: `Research error for ${inputData.topic}...`,
stage: 'researchAgent',
stage: 'research-topic-item',
},
id: 'research-topic-item',
});
Expand Down Expand Up @@ -255,6 +255,7 @@ const synthesizeResearchStep = createStep({
data: {
status: 'in-progress',
message: 'Synthesizing research across topics...',
stage: 'synthesize-research',
},
id: 'synthesize-research',
});
Expand All @@ -269,6 +270,7 @@ const synthesizeResearchStep = createStep({
data: {
status: 'in-progress',
message: 'AI synthesizing findings...',
stage: 'synthesize-research',
},
id: 'synthesize-research',
});
Expand Down Expand Up @@ -352,6 +354,7 @@ Provide:
data: {
status: 'done',
message: 'Synthesis complete...',
stage: 'synthesize-research',
},
id: 'synthesize-research',
});
Expand All @@ -376,7 +379,7 @@ Provide:
span.setAttribute('responseTimeMs', Date.now() - startTime);
span.end();


logStepEnd('synthesize-research', { themesCount: synthesis.commonThemes.length }, Date.now() - startTime);
return result;
} catch (error) {
Expand All @@ -386,10 +389,11 @@ Provide:
logError('synthesize-research', error);

await writer?.custom({
type: 'data-workflow-step-error',
type: 'data-tool-progress',
data: {
stepId: 'synthesize-research',
stage: 'synthesize-research',
error: error instanceof Error ? error.message : 'Unknown error',
status: 'done',
},
Comment on lines 391 to 397
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

style: Setting status: 'done' in error case may be misleading - consider status: 'error' to better indicate failure state

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/workflows/research-synthesis-workflow.ts
Line: 391:397

Comment:
**style:** Setting `status: 'done'` in error case may be misleading - consider `status: 'error'` to better indicate failure state

How can I resolve this? If you propose a fix, please make it concise.

id: 'synthesize-research',
});
Expand Down Expand Up @@ -421,6 +425,7 @@ const generateResearchReportStep = createStep({
data: {
status: 'in-progress',
message: 'Generating research report...',
stage: 'generate-research-report',
},
id: 'generate-research-report',
});
Expand All @@ -434,6 +439,7 @@ const generateResearchReportStep = createStep({
data: {
status: 'in-progress',
message: 'AI generating comprehensive report...',
stage: 'generate-research-report',
},
id: 'generate-research-report',
});
Expand Down Expand Up @@ -502,7 +508,7 @@ ${inputData.synthesis.gaps?.map(g => `- ${g}`).join('\n') ?? 'No significant gap
data: {
status: 'done',
message: 'Report generation complete...',
stage: 'workflow',
stage: 'generate-research-report',
},
id: 'generate-research-report',
});
Expand All @@ -523,7 +529,7 @@ ${inputData.synthesis.gaps?.map(g => `- ${g}`).join('\n') ?? 'No significant gap
span.setAttribute('responseTimeMs', Date.now() - startTime);
span.end();


logStepEnd('generate-research-report', { reportLength: report.length }, Date.now() - startTime);
return result;
} catch (error) {
Expand All @@ -533,11 +539,11 @@ ${inputData.synthesis.gaps?.map(g => `- ${g}`).join('\n') ?? 'No significant gap
logError('generate-research-report', error);

await writer?.custom({
type: 'data-workflow-step-error',
type: 'data-tool-progress',
data: {
stepId: 'generate-research-report',
stage: 'generate-research-report',
error: error instanceof Error ? error.message : 'Unknown error',
stage: 'workflow',
status: 'done'
},
id: 'generate-research-report',
});
Expand Down
Loading
Loading