feat(cron): add contextMessages param to control reminder context

This commit is contained in:
Michael Behr
2026-01-17 09:06:54 -05:00
committed by Peter Steinberger
parent 5fe8c4ab8c
commit 4642fae193
2 changed files with 33 additions and 3 deletions

View File

@@ -85,7 +85,7 @@ describe("cron tool", () => {
});
});
it("adds recent context for systemEvent reminders when session key is available", async () => {
it("adds recent context for systemEvent reminders when contextMessages > 0", async () => {
callGatewayMock
.mockResolvedValueOnce({
messages: [
@@ -102,6 +102,7 @@ describe("cron tool", () => {
const tool = createCronTool({ agentSessionKey: "main" });
await tool.execute("call3", {
action: "add",
contextMessages: 3,
job: {
name: "reminder",
schedule: { atMs: 123 },
@@ -127,4 +128,28 @@ describe("cron tool", () => {
expect(text).toContain("Assistant: We agreed to review on Tuesday.");
expect(text).toContain("User: Remind me about the thing at 2pm");
});
it("does not add context when contextMessages is 0 (default)", async () => {
callGatewayMock.mockResolvedValueOnce({ ok: true });
const tool = createCronTool({ agentSessionKey: "main" });
await tool.execute("call4", {
action: "add",
job: {
name: "reminder",
schedule: { atMs: 123 },
payload: { text: "Reminder: the thing." },
},
});
// Should only call cron.add, not chat.history
expect(callGatewayMock).toHaveBeenCalledTimes(1);
const cronCall = callGatewayMock.mock.calls[0]?.[0] as {
method?: string;
params?: { payload?: { text?: string } };
};
expect(cronCall.method).toBe("cron.add");
const text = cronCall.params?.payload?.text ?? "";
expect(text).not.toContain("Recent context:");
});
});

View File

@@ -16,7 +16,6 @@ const CRON_ACTIONS = ["status", "list", "add", "update", "remove", "run", "runs"
const CRON_WAKE_MODES = ["now", "next-heartbeat"] as const;
const REMINDER_CONTEXT_MESSAGES = 3;
const REMINDER_CONTEXT_PER_MESSAGE_MAX = 220;
const REMINDER_CONTEXT_TOTAL_MAX = 700;
const REMINDER_CONTEXT_MARKER = "\n\nRecent context:\n";
@@ -34,6 +33,7 @@ const CronToolSchema = Type.Object({
patch: Type.Optional(Type.Object({}, { additionalProperties: true })),
text: Type.Optional(Type.String()),
mode: optionalStringEnum(CRON_WAKE_MODES),
contextMessages: Type.Optional(Type.Number()),
});
type CronToolOptions = {
@@ -86,7 +86,9 @@ function extractMessageText(message: ChatMessage): { role: string; text: string
async function buildReminderContextLines(params: {
agentSessionKey?: string;
gatewayOpts: GatewayCallOptions;
contextMessages: number;
}) {
if (params.contextMessages <= 0) return [];
const sessionKey = params.agentSessionKey?.trim();
if (!sessionKey) return [];
const cfg = loadConfig();
@@ -101,7 +103,7 @@ async function buildReminderContextLines(params: {
const parsed = messages
.map((msg) => extractMessageText(msg as ChatMessage))
.filter((msg): msg is { role: string; text: string } => Boolean(msg));
const recent = parsed.slice(-REMINDER_CONTEXT_MESSAGES);
const recent = parsed.slice(-params.contextMessages);
if (recent.length === 0) return [];
const lines: string[] = [];
let total = 0;
@@ -149,6 +151,8 @@ export function createCronTool(opts?: CronToolOptions): AnyAgentTool {
throw new Error("job required");
}
const job = normalizeCronJobCreate(params.job) ?? params.job;
const contextMessages =
typeof params.contextMessages === "number" ? params.contextMessages : 0;
if (
job &&
typeof job === "object" &&
@@ -160,6 +164,7 @@ export function createCronTool(opts?: CronToolOptions): AnyAgentTool {
const contextLines = await buildReminderContextLines({
agentSessionKey: opts?.agentSessionKey,
gatewayOpts,
contextMessages,
});
if (contextLines.length > 0) {
const baseText = stripExistingContext(payload.text);