Skip to content

refactor(agents): remove datetime from system prompts, add to user messages#8

Merged
hudrazine merged 2 commits intomainfrom
develop
Sep 15, 2025
Merged

refactor(agents): remove datetime from system prompts, add to user messages#8
hudrazine merged 2 commits intomainfrom
develop

Conversation

@hudrazine
Copy link
Owner

@hudrazine hudrazine commented Sep 15, 2025

Overview

This refactoring removes static temporal context from agent system prompts and implements dynamic environment details that are injected into user messages via a new UserMessageWrapper processor.

Changes

  • Agent Configuration: Added inputProcessors: [new UserMessageWrapper()] to all agent definitions
  • New Utilities:
    • src/mastra/utils/context.ts: Functions to generate and wrap environment details with current datetime
    • src/mastra/utils/date.ts: Date/time formatting utilities
    • src/mastra/utils/processor.ts: UserMessageWrapper class that processes input messages
  • Cleanup: Removed deprecated src/mastra/utils/date-context.ts

Benefits

  • Freshness: Current datetime information is injected per interaction rather than being embedded statically
  • Maintainability: Dynamic context generation reduces prompt maintenance overhead
  • Consistency: Centralized datetime handling across all agents

Technical Details

The UserMessageWrapper implements the Processor interface and wraps user message text parts with environment details including current date/time and timezone information.

Summary by CodeRabbit

  • New Features

    • Agents now wrap user messages with environment details (current local time and timezone) and preprocess inputs to standardize messages.
    • Agents will never reproduce internal or <environment_details> blocks in outputs.
  • Refactor

    • Web Research agent instructions simplified; temporal context is provided via input preprocessing rather than dynamic prompts.
  • Chores

    • Utility exports consolidated to support consistent date-time formatting and message-wrapping across agents.

…ssages

Removed static temporal context from agent system prompts and implemented dynamic environment details in user messages via UserMessageWrapper processor. This improves prompt freshness by injecting current datetime per interaction rather than embedding fixed datetime in system context.
Copilot AI review requested due to automatic review settings September 15, 2025 14:13
@coderabbitai
Copy link

coderabbitai bot commented Sep 15, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a UserMessageWrapper input processor and new date/context utilities, removes the legacy getDateContext helper, and updates three agents to use the processor and static instructions; re-exports new utilities from utils index.

Changes

Cohort / File(s) Summary of changes
Agents — add input processor
src/mastra/agents/context7-agent.ts, src/mastra/agents/deepwiki-agent.ts
Import UserMessageWrapper and add inputProcessors: [new UserMessageWrapper()] to agent configs; added constraint to prompts forbidding reproduction of <message> or <environment_details> blocks.
WebResearchAgent — instructions & processor
src/mastra/agents/web-research-agent.ts
Replace async instructions that appended date context with a static SYSTEM_PROMPT; remove getDateContext usage; import and add inputProcessors: [new UserMessageWrapper()]; update imports.
Utils — new date & context helpers
src/mastra/utils/date.ts, src/mastra/utils/context.ts
Add formatDateTimeWithOffset and getCurrentDateTimeZone; add getEnvironmentDetails(now?) and wrapMessage(message, now?) which produce XML-like <environment_details> and <message> blocks.
Utils — new input processor
src/mastra/utils/processor.ts
Add UserMessageWrapper class implementing Processor that wraps user text parts with wrapMessage; idempotency guard skips already-wrapped text; mutates and returns message array.
Utils index — re-exports
src/mastra/utils/index.ts
Remove export of getDateContext; add exports for getEnvironmentDetails, wrapMessage, formatDateTimeWithOffset, getCurrentDateTimeZone, and UserMessageWrapper.
Legacy removal
src/mastra/utils/date-context.ts
Remove getDateContext() export and implementation.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Agent as Agent (Web/DeepWiki/Context7)
  participant Proc as UserMessageWrapper
  participant LLM as LLM

  User->>Agent: send messages
  Agent->>Proc: processInput(messages)
  Note right of Proc: For each user text part:\n- if not wrapped → wrapMessage(text)\n- append <environment_details>
  Proc-->>Agent: processed messages (mutated)
  Agent->>LLM: submit SYSTEM_PROMPT + processed messages
  LLM-->>Agent: response
  Agent-->>User: return response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

I tuck your words in tidy tags,
With clocks and zones in gentle flags.
Hop—agents read, but never show
The secret blocks we keep below.
Rabbit cheers: wrapped, safe, and slow. 🐇

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title concisely and accurately summarizes the primary change: removing datetime from agent system prompts and adding it to user messages. It uses the conventional commit-style scope "refactor(agents)" and communicates intent without noise. The title matches the PR objectives and the changed files (UserMessageWrapper and moving date logic out of system prompts), so a reviewer scanning history will understand the main change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

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

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This refactoring modernizes temporal context handling in AI agents by removing static datetime information from system prompts and implementing dynamic environment details that are injected per-interaction through user message processing.

  • Introduces a new UserMessageWrapper processor that dynamically injects current datetime and timezone information into user messages
  • Replaces static date context generation with on-demand environment details wrapping
  • Updates all agent configurations to use the new input processor pattern

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/mastra/utils/processor.ts Implements UserMessageWrapper class that processes user messages to add environment context
src/mastra/utils/date.ts Provides date/time formatting utilities for generating current timestamps with timezone
src/mastra/utils/context.ts Contains functions for generating environment details and wrapping messages with context
src/mastra/utils/index.ts Updates exports to include new utilities and remove deprecated ones
src/mastra/agents/*.ts Updates agent configurations to use UserMessageWrapper instead of static date context

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

export function getEnvironmentDetails(): string {
const localTime = getCurrentDateTimeZone();

return `<environment_details>\n# Current Time\n${localTime}\n</environment_details>`;
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

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

[nitpick] The hardcoded XML-like tag name environment_details should be extracted as a constant to improve maintainability and consistency across the codebase.

Copilot uses AI. Check for mistakes.
* @returns A combined string containing the message and environment details.
*/
export function wrapMessage(message: string): string {
const query = `<message>\n${message}\n</message>`;
Copy link

Copilot AI Sep 15, 2025

Choose a reason for hiding this comment

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

[nitpick] The hardcoded XML-like tag name message should be extracted as a constant to improve maintainability and consistency with other XML-like structures in this module.

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (7)
src/mastra/utils/date.ts (2)

2-6: Make getCurrentDateTimeZone testable and resilient to Intl failures

Allow an injectable Date for determinism in tests and guard Intl access. This also avoids repeatedly constructing new Dates in callers.

-export function getCurrentDateTimeZone(): string {
-	const now = new Date();
-	const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
-	return `${formatDateToISOString(now)} (${timeZone})`;
-}
+export function getCurrentDateTimeZone(now: Date = new Date()): string {
+	let timeZone = "UTC";
+	try {
+		timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
+	} catch {
+		// Fallback: keep UTC label
+	}
+	return `${formatDateToISOString(now)} (${timeZone})`;
+}

8-22: Naming nit: function emits RFC3339 with offset, not canonical toISOString

The output includes a numeric offset (valid), whereas JS toISOString uses Zulu. Consider a clearer name (e.g., formatDateTimeWithOffset) to avoid confusion. No functional change required.

src/mastra/agents/deepwiki-agent.ts (1)

14-21: Shield model from echoing injected environment blocks

Since user inputs are wrapped with <message> and <environment_details>, make the constraint explicit so the model never reproduces them.

 # CONSTRAINTS
 - Operate autonomously with minimal user interaction.
 - Output strictly in Markdown.
 - Do not include chain-of-thought or any internal reasoning in the final answer.
 - Respect DeepWiki/GitHub API limitations and response times.
 - Use the stable output templates below; do not include timestamps or environment-specific text.
+ - If user inputs contain `<message>` or `<environment_details>` blocks, treat them as internal context only; never copy or reference them in the output.
src/mastra/agents/context7-agent.ts (1)

13-18: Prevent accidental leakage of environment blocks

Mirror the DeepWiki constraint to avoid echoing the injected context.

 # CONSTRAINTS
 - Operate autonomously with minimal user interaction.
 - Output strictly in Markdown.
 - Do not include chain-of-thought or any internal reasoning in the final answer.
 - Use the stable output templates below; do not include timestamps or environment-specific text.
+ - If user inputs contain `<message>` or `<environment_details>` blocks, treat them as internal context only; never copy or reference them in the output.
src/mastra/agents/web-research-agent.ts (1)

22-29: Add a no‑echo rule for injected environment context

Make explicit that any <message>/<environment_details> in user inputs must not appear in outputs.

 OPERATIONAL STANDARDS:
 
+Do not reproduce any `<message>` or `<environment_details>` blocks from user inputs in the final output; treat them strictly as internal context.
+
 **Search Execution**:
src/mastra/utils/context.ts (1)

12-16: Avoid Markdown inside XML-like tags; support deterministic time injection

Drop the # header inside the XML-ish block to reduce echo risk and allow an optional Date param for testability.

-export function getEnvironmentDetails(): string {
-	const localTime = getCurrentDateTimeZone();
-
-	return `<environment_details>\n# Current Time\n${localTime}\n</environment_details>`;
-}
+export function getEnvironmentDetails(now: Date = new Date()): string {
+	const localTime = getCurrentDateTimeZone(now);
+	return `<environment_details>\n<current_time>${localTime}</current_time>\n</environment_details>`;
+}
src/mastra/utils/processor.ts (1)

17-19: Minor: consider a more specific processor name

If names are surfaced in logs/metrics, user-message-wrapper is more explicit.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae019bd and fa761b8.

📒 Files selected for processing (8)
  • src/mastra/agents/context7-agent.ts (2 hunks)
  • src/mastra/agents/deepwiki-agent.ts (2 hunks)
  • src/mastra/agents/web-research-agent.ts (2 hunks)
  • src/mastra/utils/context.ts (1 hunks)
  • src/mastra/utils/date-context.ts (0 hunks)
  • src/mastra/utils/date.ts (1 hunks)
  • src/mastra/utils/index.ts (1 hunks)
  • src/mastra/utils/processor.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/mastra/utils/date-context.ts
🧰 Additional context used
🧬 Code graph analysis (6)
src/mastra/utils/context.ts (1)
src/mastra/utils/date.ts (1)
  • getCurrentDateTimeZone (2-6)
src/mastra/agents/deepwiki-agent.ts (1)
src/mastra/utils/processor.ts (1)
  • UserMessageWrapper (17-43)
src/mastra/utils/date.ts (1)
src/mastra/utils/index.ts (2)
  • getCurrentDateTimeZone (2-2)
  • formatDateToISOString (2-2)
src/mastra/agents/context7-agent.ts (1)
src/mastra/utils/processor.ts (1)
  • UserMessageWrapper (17-43)
src/mastra/utils/processor.ts (2)
src/mastra/utils/index.ts (2)
  • UserMessageWrapper (3-3)
  • wrapMessage (1-1)
src/mastra/utils/context.ts (1)
  • wrapMessage (28-32)
src/mastra/agents/web-research-agent.ts (2)
src/mastra/utils/index.ts (1)
  • UserMessageWrapper (3-3)
src/mastra/utils/processor.ts (1)
  • UserMessageWrapper (17-43)
🔇 Additional comments (6)
src/mastra/agents/context7-agent.ts (1)

190-190: Wrapper wiring LGTM

Consistent with other agents; no issues spotted.

src/mastra/agents/web-research-agent.ts (1)

79-79: Wrapper wiring LGTM

Consistent with the refactor intent.

src/mastra/utils/index.ts (1)

1-3: Re-exports look good

Public surface is cohesive and minimal.

src/mastra/utils/processor.ts (2)

5-16: Processor purpose and docs are clear

Nice, concise JSDoc and correct intent.


31-42: Add a quick regression check — ensure we don't double-wrap user messages; only wrap the latest user turn

processInput currently wraps every user message (src/mastra/utils/processor.ts — processInput). Your grep found <environment_details> in src/mastra/utils/context.ts, but that doesn't confirm/deny double-wrapping. Run these checks and add a regression test:

  • Inspect code: rg -nP --type=ts -C3 'processInput(|wrapMessage('
  • Search prompts/snapshots/logs for repeated wrappers: rg -nP '<environment_details>|wrapMessage' -C2 path/to/prompt_snapshots || true
  • Add a unit/regression: feed a history with an already-wrapped earlier user message and assert only the newest user message is wrapped (or make wrapMessage idempotent).
src/mastra/agents/deepwiki-agent.ts (1)

210-210: Confirm inputProcessors run before prompt assembly and only once per turn

UserMessageWrapper is implemented at src/mastra/utils/processor.ts and is configured in src/mastra/agents/deepwiki-agent.ts, src/mastra/agents/web-research-agent.ts, and src/mastra/agents/context7-agent.ts. No local Agent runner was found in the repo (likely handled by @mastra/core) — verify the Agent invokes inputProcessors prior to prompt assembly/tool execution and that each processor is executed exactly once per user turn.

Comment on lines +28 to +32
export function wrapMessage(message: string): string {
const query = `<message>\n${message}\n</message>`;
const environmentDetails = getEnvironmentDetails();
return `${query}\n\n${environmentDetails}`;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Make wrapMessage idempotent to prevent prompt bloat

If the processor runs more than once or processes history, don’t re-wrap already wrapped text.

-export function wrapMessage(message: string): string {
-	const query = `<message>\n${message}\n</message>`;
-	const environmentDetails = getEnvironmentDetails();
-	return `${query}\n\n${environmentDetails}`;
-}
+const WRAPPED_MARKER = "<environment_details>";
+
+export function wrapMessage(message: string): string {
+	// Idempotency: if already wrapped, return as-is
+	if (typeof message === "string" && message.includes(WRAPPED_MARKER)) {
+		return message;
+	}
+	const query = `<message>\n${message}\n</message>`;
+	const environmentDetails = getEnvironmentDetails();
+	return `${query}\n\n${environmentDetails}`;
+}
📝 Committable suggestion

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

Suggested change
export function wrapMessage(message: string): string {
const query = `<message>\n${message}\n</message>`;
const environmentDetails = getEnvironmentDetails();
return `${query}\n\n${environmentDetails}`;
}
const WRAPPED_MARKER = "<environment_details>";
export function wrapMessage(message: string): string {
// Idempotency: if already wrapped, return as-is
if (typeof message === "string" && message.includes(WRAPPED_MARKER)) {
return message;
}
const query = `<message>\n${message}\n</message>`;
const environmentDetails = getEnvironmentDetails();
return `${query}\n\n${environmentDetails}`;
}
🤖 Prompt for AI Agents
In src/mastra/utils/context.ts around lines 28-32, wrapMessage currently blindly
wraps input with <message> tags causing nested wrappers and prompt bloat when
re-run; change it to be idempotent by detecting if the input is already wrapped
(e.g. /^\s*<message>[\s\S]*<\/message>\s*$/) and, if so, extract and reuse the
inner content instead of adding another wrapper, and only append
environmentDetails if it is not already present at the end; return a single
properly wrapped message with environment details appended exactly once.

Comment on lines +31 to +42
processInput({ messages }: { messages: MastraMessageV2[] }): MastraMessageV2[] {
messages.forEach((msg) => {
if (msg.role !== "user") return;
msg.content.parts.forEach((part) => {
if (part.type === "text") {
part.text = wrapMessage(part.text);
}
});
});

return messages;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Prevent re-wrapping history; add basic guards

Current logic wraps every user message each run, which can re-wrap history and inflate tokens. Wrap only the latest user message, skip if already wrapped, and guard against missing content.parts.

-export class UserMessageWrapper implements Processor {
+export class UserMessageWrapper implements Processor {
 	readonly name = "message-wrapper";
@@
-	processInput({ messages }: { messages: MastraMessageV2[] }): MastraMessageV2[] {
-		messages.forEach((msg) => {
-			if (msg.role !== "user") return;
-			msg.content.parts.forEach((part) => {
-				if (part.type === "text") {
-					part.text = wrapMessage(part.text);
-				}
-			});
-		});
-
-		return messages;
-	}
+	processInput({ messages }: { messages: MastraMessageV2[] }): MastraMessageV2[] {
+		const WRAPPED_MARKER = "<environment_details>";
+		// Find most recent user message
+		for (let i = messages.length - 1; i >= 0; i--) {
+			const msg = messages[i];
+			if (msg.role !== "user") continue;
+			const parts = (msg as any)?.content?.parts;
+			if (!Array.isArray(parts)) break;
+			parts.forEach((part: any) => {
+				if (part?.type === "text" && typeof part.text === "string" && !part.text.includes(WRAPPED_MARKER)) {
+					part.text = wrapMessage(part.text);
+				}
+			});
+			break; // only the latest user message
+		}
+		return messages;
+	}
 }
🤖 Prompt for AI Agents
In src/mastra/utils/processor.ts around lines 31 to 42, the current loop wraps
every user message each run which can re-wrap history and inflate tokens; change
the logic to find only the latest user message (e.g. iterate messages from the
end and pick the first with role === "user"), ensure message.content &&
Array.isArray(message.content.parts) before touching parts, and for each part
only wrap when part.type === "text" AND the text is not already wrapped (detect
existing wrapper via a simple check, e.g. a prefix/suffix or a marker that
wrapMessage would add). Update the function to modify just that single latest
user message and return messages.

Add safety instructions to prevent potential information leakage in agent outputs.
Improve date handling functions with timezone fallback and injectable now parameter for better consistency.
Enhance user message processor with input validation and double-wrap prevention.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/mastra/utils/date.ts (1)

2-10: Avoid UTC label/local-offset mismatch when Intl is unavailable; also guard empty timeZone.

If Intl is missing (or returns a falsy/empty timeZone), we currently emit something like "…-07:00 (UTC)". Format in UTC in that case and treat empty strings as missing.

Apply this diff to the function:

 export function getCurrentDateTimeZone(now: Date = new Date()): string {
-	let timeZone = "UTC";
-	try {
-		timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
-	} catch {
-		// Fallback to UTC label if Intl is unavailable or fails
-	}
-	return `${formatDateTimeWithOffset(now)} (${timeZone})`;
+  let detectedTZ: string | undefined;
+  try {
+    detectedTZ = Intl.DateTimeFormat().resolvedOptions().timeZone;
+  } catch {
+    // ignore
+  }
+  const timeZone = detectedTZ && detectedTZ.length ? detectedTZ : "UTC";
+  const formatted = detectedTZ ? formatDateTimeWithOffset(now) : formatDateTimeUTC(now);
+  return `${formatted} (${timeZone})`;
 }

Add this helper (outside the changed block):

function formatDateTimeUTC(date: Date): string {
  const year = date.getUTCFullYear();
  const month = String(date.getUTCMonth() + 1).padStart(2, "0");
  const day = String(date.getUTCDate()).padStart(2, "0");
  const hour = String(date.getUTCHours()).padStart(2, "0");
  const minute = String(date.getUTCMinutes()).padStart(2, "0");
  const second = String(date.getUTCSeconds()).padStart(2, "0");
  return `${year}-${month}-${day}T${hour}:${minute}:${second}+00:00`;
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa761b8 and 455ef23.

📒 Files selected for processing (7)
  • src/mastra/agents/context7-agent.ts (3 hunks)
  • src/mastra/agents/deepwiki-agent.ts (3 hunks)
  • src/mastra/agents/web-research-agent.ts (3 hunks)
  • src/mastra/utils/context.ts (1 hunks)
  • src/mastra/utils/date.ts (1 hunks)
  • src/mastra/utils/index.ts (1 hunks)
  • src/mastra/utils/processor.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/mastra/agents/deepwiki-agent.ts
  • src/mastra/agents/web-research-agent.ts
  • src/mastra/utils/context.ts
  • src/mastra/utils/index.ts
  • src/mastra/agents/context7-agent.ts
  • src/mastra/utils/processor.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/mastra/utils/date.ts (1)
src/mastra/utils/index.ts (2)
  • getCurrentDateTimeZone (2-2)
  • formatDateTimeWithOffset (2-2)
🔇 Additional comments (1)
src/mastra/utils/date.ts (1)

13-26: Formatting logic looks solid.

Local offset sign, hours, and minutes are correct. Consider adding a DST-boundary test to lock behavior.

@hudrazine hudrazine merged commit 17b47a4 into main Sep 15, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants