perf: stabilize system prompt time
This commit is contained in:
@@ -5,6 +5,7 @@ Docs: https://docs.clawd.bot
|
|||||||
## 2026.1.23 (Unreleased)
|
## 2026.1.23 (Unreleased)
|
||||||
|
|
||||||
### Changes
|
### Changes
|
||||||
|
- Agents: keep system prompt time zone-only and move current time to `session_status` for better cache hits.
|
||||||
- Browser: add node-host proxy auto-routing for remote gateways (configurable per gateway/node).
|
- 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.
|
- 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.
|
- CLI: restart the gateway by default after `clawdbot update`; add `--no-restart` to skip it.
|
||||||
|
|||||||
@@ -66,12 +66,12 @@ To inspect how much each injected file contributes (raw vs injected, truncation,
|
|||||||
|
|
||||||
## Time handling
|
## Time handling
|
||||||
|
|
||||||
The system prompt includes a dedicated **Current Date & Time** section when user
|
The system prompt includes a dedicated **Current Date & Time** section when the
|
||||||
time or timezone is known. It is explicit about:
|
user timezone is known. To keep the prompt cache-stable, it now only includes
|
||||||
|
the **time zone** (no dynamic clock or time format).
|
||||||
|
|
||||||
- The user’s **local time** (already converted).
|
Use `session_status` when the agent needs the current time; the status card
|
||||||
- The **time zone** used for the conversion.
|
includes a timestamp line.
|
||||||
- The **time format** (12-hour / 24-hour).
|
|
||||||
|
|
||||||
Configure with:
|
Configure with:
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ read_when:
|
|||||||
|
|
||||||
# Date & Time
|
# Date & Time
|
||||||
|
|
||||||
Clawdbot defaults to **host-local time for transport timestamps** and **user-local time only in the system prompt**.
|
Clawdbot defaults to **host-local time for transport timestamps** and **user timezone only in the system prompt**.
|
||||||
Provider timestamps are preserved so tools keep their native semantics.
|
Provider timestamps are preserved so tools keep their native semantics (current time is available via `session_status`).
|
||||||
|
|
||||||
## Message envelopes (local by default)
|
## Message envelopes (local by default)
|
||||||
|
|
||||||
@@ -63,16 +63,16 @@ You can override this behavior:
|
|||||||
|
|
||||||
## System prompt: Current Date & Time
|
## System prompt: Current Date & Time
|
||||||
|
|
||||||
If the user timezone or local time is known, the system prompt includes a dedicated
|
If the user timezone is known, the system prompt includes a dedicated
|
||||||
**Current Date & Time** section:
|
**Current Date & Time** section with the **time zone only** (no clock/time format)
|
||||||
|
to keep prompt caching stable:
|
||||||
|
|
||||||
```
|
```
|
||||||
Thursday, January 15th, 2026 — 3:07 PM (America/Chicago)
|
Time zone: America/Chicago
|
||||||
Time format: 12-hour
|
|
||||||
```
|
```
|
||||||
|
|
||||||
If only the timezone is known, we still include the section and instruct the model
|
When the agent needs the current time, use the `session_status` tool; the status
|
||||||
to assume UTC for unknown time references.
|
card includes a timestamp line.
|
||||||
|
|
||||||
## System event lines (local by default)
|
## System event lines (local by default)
|
||||||
|
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ describe("buildAgentSystemPrompt", () => {
|
|||||||
expect(prompt).toContain("Reminder: commit your changes in this workspace after edits.");
|
expect(prompt).toContain("Reminder: commit your changes in this workspace after edits.");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes user time when provided (12-hour)", () => {
|
it("includes user timezone when provided (12-hour)", () => {
|
||||||
const prompt = buildAgentSystemPrompt({
|
const prompt = buildAgentSystemPrompt({
|
||||||
workspaceDir: "/tmp/clawd",
|
workspaceDir: "/tmp/clawd",
|
||||||
userTimezone: "America/Chicago",
|
userTimezone: "America/Chicago",
|
||||||
@@ -133,11 +133,10 @@ describe("buildAgentSystemPrompt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(prompt).toContain("## Current Date & Time");
|
expect(prompt).toContain("## Current Date & Time");
|
||||||
expect(prompt).toContain("Monday, January 5th, 2026 — 3:26 PM (America/Chicago)");
|
expect(prompt).toContain("Time zone: America/Chicago");
|
||||||
expect(prompt).toContain("Time format: 12-hour");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes user time when provided (24-hour)", () => {
|
it("includes user timezone when provided (24-hour)", () => {
|
||||||
const prompt = buildAgentSystemPrompt({
|
const prompt = buildAgentSystemPrompt({
|
||||||
workspaceDir: "/tmp/clawd",
|
workspaceDir: "/tmp/clawd",
|
||||||
userTimezone: "America/Chicago",
|
userTimezone: "America/Chicago",
|
||||||
@@ -146,11 +145,10 @@ describe("buildAgentSystemPrompt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(prompt).toContain("## Current Date & Time");
|
expect(prompt).toContain("## Current Date & Time");
|
||||||
expect(prompt).toContain("Monday, January 5th, 2026 — 15:26 (America/Chicago)");
|
expect(prompt).toContain("Time zone: America/Chicago");
|
||||||
expect(prompt).toContain("Time format: 24-hour");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("shows UTC fallback when only timezone is provided", () => {
|
it("shows timezone when only timezone is provided", () => {
|
||||||
const prompt = buildAgentSystemPrompt({
|
const prompt = buildAgentSystemPrompt({
|
||||||
workspaceDir: "/tmp/clawd",
|
workspaceDir: "/tmp/clawd",
|
||||||
userTimezone: "America/Chicago",
|
userTimezone: "America/Chicago",
|
||||||
@@ -158,9 +156,7 @@ describe("buildAgentSystemPrompt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(prompt).toContain("## Current Date & Time");
|
expect(prompt).toContain("## Current Date & Time");
|
||||||
expect(prompt).toContain(
|
expect(prompt).toContain("Time zone: America/Chicago");
|
||||||
"Time zone: America/Chicago. Current time unknown; assume UTC for date/time references.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("includes model alias guidance when aliases are provided", () => {
|
it("includes model alias guidance when aliases are provided", () => {
|
||||||
|
|||||||
@@ -49,22 +49,9 @@ function buildUserIdentitySection(ownerLine: string | undefined, isMinimal: bool
|
|||||||
return ["## User Identity", ownerLine, ""];
|
return ["## User Identity", ownerLine, ""];
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTimeSection(params: {
|
function buildTimeSection(params: { userTimezone?: string }) {
|
||||||
userTimezone?: string;
|
if (!params.userTimezone) return [];
|
||||||
userTime?: string;
|
return ["## Current Date & Time", `Time zone: ${params.userTimezone}`, ""];
|
||||||
userTimeFormat?: ResolvedTimeFormat;
|
|
||||||
}) {
|
|
||||||
if (!params.userTimezone && !params.userTime) return [];
|
|
||||||
return [
|
|
||||||
"## Current Date & Time",
|
|
||||||
params.userTime
|
|
||||||
? `${params.userTime} (${params.userTimezone ?? "unknown"})`
|
|
||||||
: `Time zone: ${params.userTimezone}. Current time unknown; assume UTC for date/time references.`,
|
|
||||||
params.userTimeFormat
|
|
||||||
? `Time format: ${params.userTimeFormat === "24" ? "24-hour" : "12-hour"}`
|
|
||||||
: "",
|
|
||||||
"",
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildReplyTagsSection(isMinimal: boolean) {
|
function buildReplyTagsSection(isMinimal: boolean) {
|
||||||
@@ -212,7 +199,7 @@ export function buildAgentSystemPrompt(params: {
|
|||||||
sessions_send: "Send a message to another session/sub-agent",
|
sessions_send: "Send a message to another session/sub-agent",
|
||||||
sessions_spawn: "Spawn a sub-agent session",
|
sessions_spawn: "Spawn a sub-agent session",
|
||||||
session_status:
|
session_status:
|
||||||
"Show a /status-equivalent status card (usage + Reasoning/Verbose/Elevated); use for model-use questions (📊 session_status); optional per-session model override",
|
"Show a /status-equivalent status card (usage + time + Reasoning/Verbose/Elevated); use for model-use questions (📊 session_status); optional per-session model override",
|
||||||
image: "Analyze an image with the configured image model",
|
image: "Analyze an image with the configured image model",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,7 +289,6 @@ 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
|
||||||
@@ -465,8 +451,6 @@ export function buildAgentSystemPrompt(params: {
|
|||||||
...buildUserIdentitySection(ownerLine, isMinimal),
|
...buildUserIdentitySection(ownerLine, isMinimal),
|
||||||
...buildTimeSection({
|
...buildTimeSection({
|
||||||
userTimezone,
|
userTimezone,
|
||||||
userTime,
|
|
||||||
userTimeFormat: params.userTimeFormat,
|
|
||||||
}),
|
}),
|
||||||
"## Workspace Files (injected)",
|
"## Workspace Files (injected)",
|
||||||
"These user-editable files are loaded by Clawdbot and included below in Project Context.",
|
"These user-editable files are loaded by Clawdbot and included below in Project Context.",
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
resolveDefaultModelForAgent,
|
resolveDefaultModelForAgent,
|
||||||
resolveModelRefFromString,
|
resolveModelRefFromString,
|
||||||
} from "../../agents/model-selection.js";
|
} from "../../agents/model-selection.js";
|
||||||
|
import { formatUserTime, resolveUserTimeFormat, resolveUserTimezone } from "../date-time.js";
|
||||||
import { normalizeGroupActivation } from "../../auto-reply/group-activation.js";
|
import { normalizeGroupActivation } from "../../auto-reply/group-activation.js";
|
||||||
import { getFollowupQueueDepth, resolveQueueSettings } from "../../auto-reply/reply/queue.js";
|
import { getFollowupQueueDepth, resolveQueueSettings } from "../../auto-reply/reply/queue.js";
|
||||||
import { buildStatusMessage } from "../../auto-reply/status.js";
|
import { buildStatusMessage } from "../../auto-reply/status.js";
|
||||||
@@ -215,7 +216,7 @@ export function createSessionStatusTool(opts?: {
|
|||||||
label: "Session Status",
|
label: "Session Status",
|
||||||
name: "session_status",
|
name: "session_status",
|
||||||
description:
|
description:
|
||||||
"Show a /status-equivalent session status card (usage + cost when available). Use for model-use questions (📊 session_status). Optional: set per-session model override (model=default resets overrides).",
|
"Show a /status-equivalent session status card (usage + time + cost when available). Use for model-use questions (📊 session_status). Optional: set per-session model override (model=default resets overrides).",
|
||||||
parameters: SessionStatusToolSchema,
|
parameters: SessionStatusToolSchema,
|
||||||
execute: async (_toolCallId, args) => {
|
execute: async (_toolCallId, args) => {
|
||||||
const params = args as Record<string, unknown>;
|
const params = args as Record<string, unknown>;
|
||||||
@@ -324,6 +325,13 @@ export function createSessionStatusTool(opts?: {
|
|||||||
resolved.entry.queueDebounceMs ?? resolved.entry.queueCap ?? resolved.entry.queueDrop,
|
resolved.entry.queueDebounceMs ?? resolved.entry.queueCap ?? resolved.entry.queueDrop,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const userTimezone = resolveUserTimezone(cfg.agents?.defaults?.userTimezone);
|
||||||
|
const userTimeFormat = resolveUserTimeFormat(cfg.agents?.defaults?.timeFormat);
|
||||||
|
const userTime = formatUserTime(new Date(), userTimezone, userTimeFormat);
|
||||||
|
const timeLine = userTime
|
||||||
|
? `🕒 Time: ${userTime} (${userTimezone})`
|
||||||
|
: `🕒 Time zone: ${userTimezone}`;
|
||||||
|
|
||||||
const agentDefaults = cfg.agents?.defaults ?? {};
|
const agentDefaults = cfg.agents?.defaults ?? {};
|
||||||
const defaultLabel = `${configured.provider}/${configured.model}`;
|
const defaultLabel = `${configured.provider}/${configured.model}`;
|
||||||
const agentModel =
|
const agentModel =
|
||||||
@@ -346,6 +354,7 @@ export function createSessionStatusTool(opts?: {
|
|||||||
agentDir,
|
agentDir,
|
||||||
}),
|
}),
|
||||||
usageLine,
|
usageLine,
|
||||||
|
timeLine,
|
||||||
queue: {
|
queue: {
|
||||||
mode: queueSettings.mode,
|
mode: queueSettings.mode,
|
||||||
depth: queueDepth,
|
depth: queueDepth,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ type StatusArgs = {
|
|||||||
resolvedElevated?: ElevatedLevel;
|
resolvedElevated?: ElevatedLevel;
|
||||||
modelAuth?: string;
|
modelAuth?: string;
|
||||||
usageLine?: string;
|
usageLine?: string;
|
||||||
|
timeLine?: string;
|
||||||
queue?: QueueStatus;
|
queue?: QueueStatus;
|
||||||
mediaDecisions?: MediaUnderstandingDecision[];
|
mediaDecisions?: MediaUnderstandingDecision[];
|
||||||
subagentsLine?: string;
|
subagentsLine?: string;
|
||||||
@@ -381,6 +382,7 @@ export function buildStatusMessage(args: StatusArgs): string {
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
versionLine,
|
versionLine,
|
||||||
|
args.timeLine,
|
||||||
modelLine,
|
modelLine,
|
||||||
usageCostLine,
|
usageCostLine,
|
||||||
`📚 ${contextLine}`,
|
`📚 ${contextLine}`,
|
||||||
|
|||||||
Reference in New Issue
Block a user