🐛 fix: inject timezone and cron jobs list into cron tool system prompt (#14012)

* 🐛 fix: inject timezone and cron jobs list into cron tool system prompt

Add {{timezone}} to cron systemRole session_context so the model knows
the user's local timezone when creating scheduled tasks. Wire up the
{{CRON_JOBS_LIST}} placeholder that was already referenced in the
systemRole but never populated — now fetches the agent's existing cron
jobs via tRPC and injects them, following the same pattern as CREDS_LIST.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* 🐛 fix: limit cron jobs context to 4 items to save context window

Only inject a preview of up to 4 cron jobs into the system prompt.
When there are more, append a hint directing the model to call
listCronJobs API for the full list. This avoids bloating the context
window for agents with many scheduled tasks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
LiJian 2026-04-21 15:25:55 +08:00 committed by GitHub
parent ca47d972a4
commit 665b482390
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 70 additions and 0 deletions

View file

@ -0,0 +1,25 @@
import type { CronJobSummaryForContext } from './types';
const formatCronJob = (job: CronJobSummaryForContext): string => {
const status = job.enabled ? 'enabled' : 'disabled';
const execInfo =
job.remainingExecutions != null ? `${job.remainingExecutions} remaining` : 'unlimited';
const lastRun = job.lastExecutedAt ?? 'never';
const desc = job.description ? ` - ${job.description}` : '';
return ` - ${job.name || 'Unnamed'} (id: ${job.id}): ${job.cronPattern} [${job.timezone}] [${status}, ${execInfo}, ${job.totalExecutions} completed, last run: ${lastRun}]${desc}`;
};
export const generateCronJobsList = (jobs: CronJobSummaryForContext[], total?: number): string => {
if (jobs.length === 0) {
return 'No scheduled tasks configured for this agent.';
}
const lines = jobs.map(formatCronJob);
if (total && total > jobs.length) {
lines.push(` (showing ${jobs.length} of ${total} tasks — use listCronJobs to see all)`);
}
return lines.join('\n');
};

View file

@ -1,4 +1,5 @@
export { CronExecutionRuntime, type ICronService } from './ExecutionRuntime';
export { generateCronJobsList } from './helpers';
export { CronIdentifier, CronManifest } from './manifest';
export { systemPrompt } from './systemRole';
export {

View file

@ -4,6 +4,7 @@ export const systemPrompt = `You have access to a LobeHub Scheduled Tasks Tool.
Current user: {{username}}
Session date: {{date}}
Current agent: {{agent_id}}
User timezone: {{timezone}}
</session_context>
<existing_scheduled_tasks>

View file

@ -2,6 +2,11 @@ import { LobeActivatorIdentifier } from '@lobechat/builtin-tool-activator';
import { AgentBuilderIdentifier } from '@lobechat/builtin-tool-agent-builder';
import { AgentManagementIdentifier } from '@lobechat/builtin-tool-agent-management';
import { CredsIdentifier, type CredSummary, generateCredsList } from '@lobechat/builtin-tool-creds';
import {
CronIdentifier,
type CronJobSummaryForContext,
generateCronJobsList,
} from '@lobechat/builtin-tool-cron';
import { GroupAgentBuilderIdentifier } from '@lobechat/builtin-tool-group-agent-builder';
import { GTDIdentifier } from '@lobechat/builtin-tool-gtd';
import { WebOnboardingIdentifier } from '@lobechat/builtin-tool-web-onboarding';
@ -377,6 +382,42 @@ export const contextEngineering = async ({
}
}
// Resolve cron jobs context for cron tool
// Only inject a small preview (up to 4) to save context window;
// the model can call listCronJobs API for the full list.
const isCronEnabled = tools?.includes(CronIdentifier) ?? false;
let cronJobsList: CronJobSummaryForContext[] | undefined;
let cronJobsTotal = 0;
if (isCronEnabled && agentId) {
try {
const cronResult = await lambdaClient.agentCronJob.list.query({ agentId, limit: 4 });
const jobs = (cronResult as any)?.data ?? [];
cronJobsTotal = (cronResult as any)?.pagination?.total ?? jobs.length;
cronJobsList = jobs.map(
(job: any): CronJobSummaryForContext => ({
cronPattern: job.cronPattern,
description: job.description,
enabled: job.enabled,
id: job.id,
lastExecutedAt: job.lastExecutedAt,
name: job.name,
remainingExecutions: job.remainingExecutions,
timezone: job.timezone ?? 'UTC',
totalExecutions: job.totalExecutions ?? 0,
}),
);
log(
'Cron jobs context resolved: count=%d, total=%d',
cronJobsList?.length ?? 0,
cronJobsTotal,
);
} catch (error) {
// Silently fail - cron context is optional
log('Failed to resolve cron jobs context:', error);
}
}
const userMemoryConfig =
enableUserMemories && userMemoryData
? {
@ -694,6 +735,8 @@ export const contextEngineering = async ({
...VARIABLE_GENERATORS,
// NOTICE: required by builtin-tool-creds/src/systemRole.ts
CREDS_LIST: () => (credsList ? generateCredsList(credsList) : ''),
// NOTICE: required by builtin-tool-cron/src/systemRole.ts
CRON_JOBS_LIST: () => (cronJobsList ? generateCronJobsList(cronJobsList, cronJobsTotal) : ''),
// NOTICE(@nekomeowww): required by builtin-tool-memory/src/systemRole.ts
memory_effort: () => (userMemoryConfig ? (memoryContext?.effort ?? '') : ''),
// Current agent + topic identity — referenced by the LobeHub builtin