From b1482957f582634071f559f05924866a0968a753 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 24 Jan 2026 07:08:20 +0000 Subject: [PATCH] feat: add cron time context --- CHANGELOG.md | 1 + ....uses-last-non-empty-agent-text-as.test.ts | 37 +++++++++++++++++++ src/cron/isolated-agent/run.ts | 12 +++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 37a2b1a99..8e4e5ba85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Docs: https://docs.clawd.bot ### Changes - Agents: keep system prompt time zone-only and move current time to `session_status` for better cache hits. +- Cron: append current time to isolated agent prompt context for scheduled runs. - Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node). - Plugins: add optional llm-task JSON-only tool for workflows. (#1498) Thanks @vignesh07. - CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it. diff --git a/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts b/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts index c796580be..90a4e64b8 100644 --- a/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts +++ b/src/cron/isolated-agent.uses-last-non-empty-agent-text-as.test.ts @@ -125,6 +125,43 @@ describe("runCronIsolatedAgentTurn", () => { }); }); + it("appends current time after the cron header line", async () => { + await withTempHome(async (home) => { + const storePath = await writeSessionStore(home); + const deps: CliDeps = { + sendMessageWhatsApp: vi.fn(), + sendMessageTelegram: vi.fn(), + sendMessageDiscord: vi.fn(), + sendMessageSignal: vi.fn(), + sendMessageIMessage: vi.fn(), + }; + vi.mocked(runEmbeddedPiAgent).mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { + durationMs: 5, + agentMeta: { sessionId: "s", provider: "p", model: "m" }, + }, + }); + + await runCronIsolatedAgentTurn({ + cfg: makeCfg(home, storePath), + deps, + job: makeJob({ kind: "agentTurn", message: "do it", deliver: false }), + message: "do it", + sessionKey: "cron:job-1", + lane: "cron", + }); + + const call = vi.mocked(runEmbeddedPiAgent).mock.calls.at(-1)?.[0] as { + prompt?: string; + }; + const lines = call?.prompt?.split("\n") ?? []; + expect(lines[0]).toContain("[cron:job-1"); + expect(lines[0]).toContain("do it"); + expect(lines[1]).toMatch(/^Current time: .+ \(.+\)$/); + }); + }); + it("uses agentId for workspace, session key, and store paths", async () => { await withTempHome(async (home) => { const deps: CliDeps = { diff --git a/src/cron/isolated-agent/run.ts b/src/cron/isolated-agent/run.ts index 4f8f4deb3..bab060438 100644 --- a/src/cron/isolated-agent/run.ts +++ b/src/cron/isolated-agent/run.ts @@ -25,6 +25,11 @@ import { getSkillsSnapshotVersion } from "../../agents/skills/refresh.js"; import { resolveAgentTimeoutMs } from "../../agents/timeout.js"; import { hasNonzeroUsage } from "../../agents/usage.js"; import { ensureAgentWorkspace } from "../../agents/workspace.js"; +import { + formatUserTime, + resolveUserTimeFormat, + resolveUserTimezone, +} from "../../agents/date-time.js"; import { formatXHighModelHint, normalizeThinkLevel, @@ -226,7 +231,12 @@ export async function runCronIsolatedAgentTurn(params: { }); const base = `[cron:${params.job.id} ${params.job.name}] ${params.message}`.trim(); - const commandBody = base; + const userTimezone = resolveUserTimezone(params.cfg.agents?.defaults?.userTimezone); + const userTimeFormat = resolveUserTimeFormat(params.cfg.agents?.defaults?.timeFormat); + const formattedTime = + formatUserTime(new Date(now), userTimezone, userTimeFormat) ?? new Date(now).toISOString(); + const timeLine = `Current time: ${formattedTime} (${userTimezone})`; + const commandBody = `${base}\n${timeLine}`.trim(); const existingSnapshot = cronSession.sessionEntry.skillsSnapshot; const skillsSnapshotVersion = getSkillsSnapshotVersion(workspaceDir);