Merge c2e9bfb864 into 4583f88626
This commit is contained in:
commit
eca19bc068
@ -49,9 +49,14 @@ function buildUserIdentitySection(ownerLine: string | undefined, isMinimal: bool
|
|||||||
return ["## User Identity", ownerLine, ""];
|
return ["## User Identity", ownerLine, ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTimeSection(params: { userTimezone?: string }) {
|
function buildTimeSection(params: { userTimezone?: string; userTime?: string }) {
|
||||||
if (!params.userTimezone) return [];
|
if (!params.userTimezone) return [];
|
||||||
return ["## Current Date & Time", `Time zone: ${params.userTimezone}`, ""];
|
const lines = ["## Current Date & Time"];
|
||||||
|
if (params.userTime) {
|
||||||
|
lines.push(params.userTime);
|
||||||
|
}
|
||||||
|
lines.push(`Time zone: ${params.userTimezone}`, "");
|
||||||
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildReplyTagsSection(isMinimal: boolean) {
|
function buildReplyTagsSection(isMinimal: boolean) {
|
||||||
@ -294,6 +299,7 @@ export function buildAgentSystemPrompt(params: {
|
|||||||
: undefined;
|
: undefined;
|
||||||
const reasoningLevel = params.reasoningLevel ?? "off";
|
const reasoningLevel = params.reasoningLevel ?? "off";
|
||||||
const userTimezone = params.userTimezone?.trim();
|
const userTimezone = params.userTimezone?.trim();
|
||||||
|
const userTime = params.userTime?.trim();
|
||||||
const skillsPrompt = params.skillsPrompt?.trim();
|
const skillsPrompt = params.skillsPrompt?.trim();
|
||||||
const heartbeatPrompt = params.heartbeatPrompt?.trim();
|
const heartbeatPrompt = params.heartbeatPrompt?.trim();
|
||||||
const heartbeatPromptLine = heartbeatPrompt
|
const heartbeatPromptLine = heartbeatPrompt
|
||||||
@ -445,6 +451,7 @@ export function buildAgentSystemPrompt(params: {
|
|||||||
...buildUserIdentitySection(ownerLine, isMinimal),
|
...buildUserIdentitySection(ownerLine, isMinimal),
|
||||||
...buildTimeSection({
|
...buildTimeSection({
|
||||||
userTimezone,
|
userTimezone,
|
||||||
|
userTime,
|
||||||
}),
|
}),
|
||||||
"## Workspace Files (injected)",
|
"## Workspace Files (injected)",
|
||||||
"These user-editable files are loaded by Moltbot and included below in Project Context.",
|
"These user-editable files are loaded by Moltbot and included below in Project Context.",
|
||||||
|
|||||||
73
src/cron/service/jobs.test.ts
Normal file
73
src/cron/service/jobs.test.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
|
||||||
|
import type { CronJob } from "../types.js";
|
||||||
|
import { computeJobNextRunAtMs } from "./jobs.js";
|
||||||
|
|
||||||
|
function makeEveryJob(overrides: Partial<CronJob> = {}): CronJob {
|
||||||
|
return {
|
||||||
|
id: "test-job",
|
||||||
|
enabled: true,
|
||||||
|
createdAtMs: 1000,
|
||||||
|
updatedAtMs: 1000,
|
||||||
|
schedule: { kind: "every", everyMs: 3600_000 }, // 1 hour
|
||||||
|
sessionTarget: "main",
|
||||||
|
wakeMode: "next-heartbeat",
|
||||||
|
payload: { kind: "systemEvent", text: "test" },
|
||||||
|
state: {},
|
||||||
|
...overrides,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("computeJobNextRunAtMs", () => {
|
||||||
|
it("anchors to lastRunAtMs for every jobs that have run", () => {
|
||||||
|
const job = makeEveryJob({
|
||||||
|
state: { lastRunAtMs: 1000 },
|
||||||
|
});
|
||||||
|
const nowMs = 5000;
|
||||||
|
const next = computeJobNextRunAtMs(job, nowMs);
|
||||||
|
// Should be lastRunAtMs + everyMs, NOT nowMs + everyMs
|
||||||
|
expect(next).toBe(1000 + 3600_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to anchorMs when no lastRunAtMs", () => {
|
||||||
|
const job = makeEveryJob({
|
||||||
|
schedule: { kind: "every", everyMs: 3600_000, anchorMs: 500 },
|
||||||
|
state: {}, // No lastRunAtMs
|
||||||
|
});
|
||||||
|
const next = computeJobNextRunAtMs(job, 1000);
|
||||||
|
expect(next).toBe(500 + 3600_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to nowMs when neither lastRunAtMs nor anchorMs exist", () => {
|
||||||
|
const job = makeEveryJob({
|
||||||
|
schedule: { kind: "every", everyMs: 3600_000 }, // No anchorMs
|
||||||
|
state: {}, // No lastRunAtMs
|
||||||
|
});
|
||||||
|
const next = computeJobNextRunAtMs(job, 1000);
|
||||||
|
expect(next).toBe(1000 + 3600_000);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not affect cron expression schedules", () => {
|
||||||
|
const job: CronJob = {
|
||||||
|
id: "test-job",
|
||||||
|
enabled: true,
|
||||||
|
createdAtMs: 1000,
|
||||||
|
updatedAtMs: 1000,
|
||||||
|
schedule: { kind: "cron", expr: "0 9 * * *", tz: "UTC" }, // Daily at 9am UTC
|
||||||
|
sessionTarget: "main",
|
||||||
|
wakeMode: "next-heartbeat",
|
||||||
|
payload: { kind: "systemEvent", text: "test" },
|
||||||
|
state: { lastRunAtMs: 500 }, // Should be ignored for cron expr
|
||||||
|
};
|
||||||
|
const nowMs = Date.parse("2025-12-13T00:00:00.000Z");
|
||||||
|
const next = computeJobNextRunAtMs(job, nowMs);
|
||||||
|
// Should be next 9am UTC, not anchored to lastRunAtMs
|
||||||
|
expect(next).toBe(Date.parse("2025-12-13T09:00:00.000Z"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns undefined for disabled jobs", () => {
|
||||||
|
const job = makeEveryJob({ enabled: false });
|
||||||
|
const next = computeJobNextRunAtMs(job, 1000);
|
||||||
|
expect(next).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -40,6 +40,12 @@ export function computeJobNextRunAtMs(job: CronJob, nowMs: number): number | und
|
|||||||
if (job.state.lastStatus === "ok" && job.state.lastRunAtMs) return undefined;
|
if (job.state.lastStatus === "ok" && job.state.lastRunAtMs) return undefined;
|
||||||
return job.schedule.atMs;
|
return job.schedule.atMs;
|
||||||
}
|
}
|
||||||
|
// For "every" jobs, anchor to lastRunAtMs to prevent drift on updates/restarts
|
||||||
|
if (job.schedule.kind === "every") {
|
||||||
|
const anchor = job.state.lastRunAtMs ?? job.schedule.anchorMs ?? nowMs;
|
||||||
|
return computeNextRunAtMs({ ...job.schedule, anchorMs: anchor }, nowMs);
|
||||||
|
}
|
||||||
|
// "cron" expressions are clock-based
|
||||||
return computeNextRunAtMs(job.schedule, nowMs);
|
return computeNextRunAtMs(job.schedule, nowMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user