feat(gateway): inject timestamps into agent handler messages

Messages arriving through the gateway agent method (TUI, web, spawned
subagents, sessions_send, heartbeats) now get a timestamp prefix
automatically. This gives all agent contexts date/time awareness
without modifying the system prompt (which is cached for stability).

Channel messages (Discord, Telegram, etc.) already have timestamps
via envelope formatting in a separate code path and never reach
the agent handler, so there is no double-stamping risk.

Cron jobs also inject their own 'Current time:' prefix and are
detected and skipped.

Extracted as a pure function (injectTimestamp) with 12 unit tests
covering: timezone handling, 12/24h format, midnight boundaries,
envelope detection, cron detection, and empty messages.

Integration test verifies the agent handler wires it in correctly.

Closes #3658
Refs: #1897, #1928, #2108
This commit is contained in:
Conroy Whitney
2026-01-28 21:31:08 -05:00
committed by Tak Hoffman
parent 83e64c1ac9
commit 582a4e261a
4 changed files with 270 additions and 1 deletions

View File

@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
import { agentCommand } from "../../commands/agent.js";
import { listAgentIds } from "../../agents/agent-scope.js";
import { loadConfig } from "../../config/config.js";
import { injectTimestamp, timestampOptsFromConfig } from "./agent-timestamp.js";
import {
resolveAgentIdFromSessionKey,
resolveExplicitAgentSessionKey,
@@ -136,6 +137,13 @@ export const agentHandlers: GatewayRequestHandlers = {
return;
}
}
// Inject timestamp into messages that don't already have one.
// Channel messages (Discord, Telegram, etc.) get timestamps via envelope
// formatting in a separate code path — they never reach this handler.
// See: https://github.com/moltbot/moltbot/issues/3658
message = injectTimestamp(message, timestampOptsFromConfig(cfg));
const isKnownGatewayChannel = (value: string): boolean => isGatewayMessageChannel(value);
const channelHints = [request.channel, request.replyChannel]
.filter((value): value is string => typeof value === "string")