darkfire514's picture
Upload 2526 files
fb4d8fe verified
import type { OpenClawConfig } from "../../config/types.js";
import { resolveUserTimezone } from "../../agents/date-time.js";
import { formatZonedTimestamp } from "../../auto-reply/envelope.js";
/**
* Cron jobs inject "Current time: ..." into their messages.
* Skip injection for those.
*/
const CRON_TIME_PATTERN = /Current time: /;
/**
* Matches a leading `[... YYYY-MM-DD HH:MM ...]` envelope — either from
* channel plugins or from a previous injection. Uses the same YYYY-MM-DD
* HH:MM format as {@link formatZonedTimestamp}, so detection stays in sync
* with the formatting.
*/
const TIMESTAMP_ENVELOPE_PATTERN = /^\[.*\d{4}-\d{2}-\d{2} \d{2}:\d{2}/;
export interface TimestampInjectionOptions {
timezone?: string;
now?: Date;
}
/**
* Injects a compact timestamp prefix into a message if one isn't already
* present. Uses the same `YYYY-MM-DD HH:MM TZ` format as channel envelope
* timestamps ({@link formatZonedTimestamp}), keeping token cost low (~7
* tokens) and format consistent across all agent contexts.
*
* Used by the gateway `agent` and `chat.send` handlers to give TUI, web,
* spawned subagents, `sessions_send`, and heartbeat wake events date/time
* awareness — without modifying the system prompt (which is cached).
*
* Channel messages (Discord, Telegram, etc.) already have timestamps via
* envelope formatting and take a separate code path — they never reach
* these handlers, so there is no double-stamping risk. The detection
* pattern is a safety net for edge cases.
*
* @see https://github.com/moltbot/moltbot/issues/3658
*/
export function injectTimestamp(message: string, opts?: TimestampInjectionOptions): string {
if (!message.trim()) {
return message;
}
// Already has an envelope or injected timestamp
if (TIMESTAMP_ENVELOPE_PATTERN.test(message)) {
return message;
}
// Already has a cron-injected timestamp
if (CRON_TIME_PATTERN.test(message)) {
return message;
}
const now = opts?.now ?? new Date();
const timezone = opts?.timezone ?? "UTC";
const formatted = formatZonedTimestamp(now, timezone);
if (!formatted) {
return message;
}
// 3-letter DOW: small models (8B) can't reliably derive day-of-week from
// a date, and may treat a bare "Wed" as a typo. Costs ~1 token.
const dow = new Intl.DateTimeFormat("en-US", { timeZone: timezone, weekday: "short" }).format(
now,
);
return `[${dow} ${formatted}] ${message}`;
}
/**
* Build TimestampInjectionOptions from an OpenClawConfig.
*/
export function timestampOptsFromConfig(cfg: OpenClawConfig): TimestampInjectionOptions {
return {
timezone: resolveUserTimezone(cfg.agents?.defaults?.userTimezone),
};
}