Skip to content

Commit 1579228

Browse files
committed
feat: enhance agent functionality and add new tools
- Updated access control enforcement in tools and workflows to use `RequestContext` and agent-specific runtime context types. - Added optional fields `userId` and `researchPhase` to `ResearchRuntimeContext` for improved context handling. - Imported new agents: `calendarAgent`, `bgColorAgent`, and `danePackagePublisher` into the main index. - Implemented a new tool `colorChangeTool` to change the background color with a defined input schema.
1 parent 89b9eb4 commit 1579228

4 files changed

Lines changed: 93 additions & 34 deletions

File tree

src/mastra/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ This folder contains the middleware and high-level components that define Mastra
4545

4646
- Use explicit Zod schemas for every tool input/output.
4747
- Keep tools small and side-effect-free when possible; agents orchestrate tools and handle context.
48-
- Use `RuntimeContext` to enforce access control in tools and workflows.
48+
- Use `RequestContext` (per-request) or agent-specific runtime context types (e.g. `EditorRuntimeContext`) to enforce access control in tools and workflows.
4949

5050
---
5151
Last updated: 2025-11-26

src/mastra/agents/researchAgent.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ export type UserTier = 'free' | 'pro' | 'enterprise'
3434
export interface ResearchRuntimeContext {
3535
'user-tier': UserTier
3636
language: 'en' | 'es' | 'ja' | 'fr'
37+
// Optional runtime fields the server middleware may populate
38+
userId?: string
39+
researchPhase?: 'initial' | 'followup' | 'validation' | string
3740
}
3841
log.info('Initializing Research Agent...')
3942

@@ -46,13 +49,18 @@ export const researchAgent = new Agent({
4649
// runtimeContext is read at invocation time
4750
const userTier = requestContext.get('user-tier') ?? 'free'
4851
const language = requestContext.get('language') ?? 'en'
52+
const userId = requestContext.get('userId') ?? 'anonymous'
53+
const researchPhase = requestContext.get('researchPhase') ?? 'initial'
54+
4955
return {
5056
role: 'system',
5157
content: `
5258
<role>
5359
5460
Tier: ${userTier}
5561
Language: ${language}
62+
UserId: ${userId}
63+
Research Phase: ${researchPhase}
5664
5765
You are a Senior Research Analyst. Your goal is to research topics thoroughly by following a precise, multi-phase process.
5866
</role>
@@ -160,8 +168,8 @@ export const researchAgent = new Agent({
160168
// higher quality (chat style) for enterprise
161169
return google.chat('gemini-3-pro-preview')
162170
} else if (userTier === 'pro') {
163-
// Chat bison for pro as well
164-
return googleAI
171+
// cheaper/faster model for pro tier
172+
return 'google/gemini-2.5-flash-preview-09-2025'
165173
}
166174
// cheaper/faster model for free tier
167175
return google.chat('gemini-2.5-flash-preview-09-2025')

src/mastra/index.ts

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ import { researchPaperAgent } from './agents/researchPaperAgent';
5151

5252
// Utility Agents
5353
import { daneNewContributor } from './workflows/new-contributor';
54+
import { calendarAgent } from './agents/calendarAgent';
55+
import { bgColorAgent } from './agents/bgColorAgent';
56+
import { danePackagePublisher } from './agents/package-publisher';
5457

5558
// Financial Chart Agents
5659
import {
@@ -72,6 +75,7 @@ import { codingA2ACoordinator } from './a2a/codingA2ACoordinator';
7275

7376
// Workflows
7477
import { trace } from "@opentelemetry/api";
78+
import type { RequestContext } from '@mastra/core/request-context';
7579
import { acpAgent } from './agents/acpAgent';
7680
import { businessStrategyAgent, complianceMonitoringAgent, contractAnalysisAgent, legalResearchAgent } from "./agents/businessLegalAgents";
7781
import { dane, daneChangeLog, daneCommitMessage, daneIssueLabeler, daneLinkChecker } from './agents/dane';
@@ -140,6 +144,11 @@ export const mastra = new Mastra({
140144
daneLinkChecker,
141145
daneChangeLog,
142146
dane,
147+
// Calendar and misc
148+
calendarAgent,
149+
bgColorAgent,
150+
// Package publisher
151+
danePackagePublisher,
143152
// Financial Chart Agents
144153
chartTypeAdvisorAgent,
145154
chartDataProcessorAgent,
@@ -453,37 +462,63 @@ export const mastra = new Mastra({
453462
sendSources: true,
454463
}),
455464
],
456-
// cors: {
457-
// origin: ["*"], // Allow specific origins or '*' for all
458-
// allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
459-
// allowHeaders: ["Content-Type", "Authorization"],
460-
// exposeHeaders: ["Content-Length", "X-Requested-With"],
461-
// credentials: false,
462-
// },
463-
// middleware: [
464-
// Middleware to extract data from the request body
465-
// async (c, next) => {
466-
// const runtimeContext = c.get("runtimeContext");
467-
//
468-
// if (c.req.method === "POST") {
469-
// try {
470-
// const clonedReq = c.req.raw.clone();
471-
/// const body = await clonedReq.json();
472-
//
473-
// Ensure body and body.data are objects before using them
474-
// if (body !== null && typeof body === 'object' && body.data !== null && typeof body.data === 'object') {
475-
// const data = body.data as Record<string, unknown>;
476-
// for (const [key, value] of Object.entries(data)) {
477-
// runtimeContext.set(key, value);
478-
// }
479-
// }
480-
// } catch {
481-
// log.error("Failed to parse request body for middleware");
482-
// }
483-
// }
484-
// await next();
485-
// },
486-
//0 ],
465+
middleware: [
466+
// Populate RequestContext with real runtime values derived from headers (used by agents/tools)
467+
async (c, next) => {
468+
const country = c.req.header('CF-IPCountry') ?? ''
469+
const authHeader = c.req.header('Authorization') ?? ''
470+
const headerUserId = c.req.header('x-user-id')
471+
const headerUserTier = c.req.header('x-user-tier')
472+
const acceptLanguage = c.req.header('accept-language') ?? ''
473+
const researchPhaseHeader = c.req.header('x-research-phase')
474+
475+
const requestContext = c.get('requestContext') as RequestContext | undefined
476+
if (requestContext?.set) {
477+
// Temperature unit (from Cloudflare geo header)
478+
const unit = country === 'US' ? 'fahrenheit' : 'celsius'
479+
requestContext.set('temperature-unit', unit)
480+
481+
// userId: prefer explicit header, otherwise try to parse from a bearer token (format: "Bearer user:<id>")
482+
let userId = headerUserId
483+
if (!userId && authHeader !== null && authHeader !== '' && authHeader.startsWith('Bearer ')) {
484+
const token = authHeader.slice('Bearer '.length)
485+
const exec = /user:([^;\s]+)/.exec(token)
486+
if (exec) { userId = exec[1] }
487+
}
488+
if (userId !== null && userId !== '') { requestContext.set('userId', userId) }
489+
490+
// user-tier: prefer explicit header, otherwise derive from token hints
491+
let userTier = headerUserTier
492+
if (!userTier && authHeader !== null && authHeader !== '' && authHeader.startsWith('Bearer ')) {
493+
const token = authHeader.slice('Bearer '.length)
494+
if (token.includes('enterprise')) { userTier = 'enterprise' }
495+
else if (token.includes('pro')) { userTier = 'pro' }
496+
else { userTier = 'free' }
497+
}
498+
if (userTier !== null && userTier !== '') { requestContext.set('user-tier', userTier) }
499+
500+
// language: prefer Accept-Language header (primary language subtag), fallback to 'en'
501+
const language = acceptLanguage.split(',')[0]?.split('-')[0] ?? 'en'
502+
const supported = ['en', 'es', 'ja', 'fr']
503+
requestContext.set('language', supported.includes(language) ? language : 'en')
504+
505+
// research phase
506+
requestContext.set('researchPhase', researchPhaseHeader ?? 'initial')
507+
508+
// runtime API key (for tools that may accept runtimeContext.apiKey)
509+
// if (apiKeyHeader !== null && apiKeyHeader !== '') { requestContext.set('apiKey', apiKeyHeader) }
510+
}
511+
512+
await next()
513+
},
514+
// Request timing logger
515+
async (c, next) => {
516+
const start = Date.now()
517+
await next()
518+
const duration = Date.now() - start
519+
log.info(`${c.req.method} ${c.req.url} - ${duration}ms`)
520+
},
521+
]
487522
}
488523
});
489524

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createTool } from "@mastra/client-js";
2+
import z from "zod";
3+
4+
export function changeBgColor(color: string) {
5+
if (typeof window !== "undefined") {
6+
document.body.style.setProperty("--background", color);
7+
}
8+
}
9+
10+
export const colorChangeTool = createTool({
11+
id: "changeColor",
12+
description: "Changes the background color",
13+
inputSchema: z.object({
14+
color: z.string(),
15+
}),
16+
});

0 commit comments

Comments
 (0)