mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 12:26:38 -06:00
refactor(infra): canonicalize session usage timestamps (#100687)
Co-authored-by: sheyanmin <44184140+sheyanmin@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c1cbc15e60
commit
f6f3423ce9
@@ -33,6 +33,7 @@ Docs: https://docs.openclaw.ai
|
||||
- **Small-context compaction:** cap the effective reserve against the known model context window so small local models do not enter compaction from the first token. (#100621) Thanks @vincentkoc.
|
||||
- **Plugin install diagnostics:** suppress the misleading hook-pack fallback after plugin install failures only when the hook manifest is absent, while preserving actionable malformed hook-pack errors. (#100554) Thanks @vincentkoc.
|
||||
- **Config validation diagnostics:** emit each unchanged sanitized validation-warning payload once per config path, reset deduplication after a clean validation, and preserve the warning fingerprint across transient invalid reads and failed refreshes. (#100569, #25574) Thanks @vincentkoc.
|
||||
- **Session usage logs:** normalize malformed transcript timestamps before sorting and Gateway serialization so invalid dates cannot surface as null usage-log times. (#99418) Thanks @sheyanmin.
|
||||
- **Config size-drop guard:** compare writes against canonical bytes for parseable object configs instead of raw BOM and indentation overhead, while preserving raw audit telemetry and the conservative malformed-input fallback. (#100591, #71865) Thanks @vincentkoc.
|
||||
- **Control UI coalesced updates:** show a clear queued-restart completion banner when an update joins an already-running Gateway restart. (#93082) Thanks @goutamadwant.
|
||||
- **Control UI connection errors:** preserve structured pairing and authentication failures for pending RPC callers while keeping generic disconnect behavior unchanged. (#54758) Thanks @ruanrrn.
|
||||
|
||||
@@ -2919,6 +2919,45 @@ example
|
||||
expect(logs?.[0]?.content).toBe("hello there");
|
||||
});
|
||||
|
||||
it("normalizes malformed log timestamps with the transcript timestamp rules", async () => {
|
||||
const root = await makeSessionCostRoot("logs-malformed-timestamp");
|
||||
const sessionsDir = path.join(root, "agents", "main", "sessions");
|
||||
await fs.mkdir(sessionsDir, { recursive: true });
|
||||
const sessionFile = path.join(sessionsDir, "sess-malformed.jsonl");
|
||||
|
||||
await fs.writeFile(
|
||||
sessionFile,
|
||||
[
|
||||
JSON.stringify({
|
||||
type: "message",
|
||||
timestamp: "not-a-valid-date-string",
|
||||
message: { role: "user", content: "bad timestamp entry" },
|
||||
}),
|
||||
JSON.stringify({
|
||||
type: "message",
|
||||
timestamp: "still-not-a-valid-date-string",
|
||||
message: {
|
||||
role: "assistant",
|
||||
content: "nested timestamp entry",
|
||||
timestamp: Date.parse("2026-02-21T17:46:00.000Z"),
|
||||
},
|
||||
}),
|
||||
JSON.stringify({
|
||||
type: "message",
|
||||
timestamp: "2026-02-21T17:47:00.000Z",
|
||||
message: { role: "assistant", content: "valid timestamp entry" },
|
||||
}),
|
||||
].join("\n"),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
const logs = await loadSessionLogs({ sessionFile });
|
||||
expect(logs).toHaveLength(3);
|
||||
expect(logs?.[0]?.timestamp).toBe(0);
|
||||
expect(logs?.[1]?.timestamp).toBe(Date.parse("2026-02-21T17:46:00.000Z"));
|
||||
expect(logs?.[2]?.timestamp).toBe(Date.parse("2026-02-21T17:47:00.000Z"));
|
||||
});
|
||||
|
||||
it("buckets hourly message counts into UTC quarter-hour slots", async () => {
|
||||
const root = await makeSessionCostRoot("cost-quarter");
|
||||
const sessionFile = path.join(root, "session.jsonl");
|
||||
|
||||
@@ -2740,15 +2740,9 @@ export async function loadSessionLogs(params: {
|
||||
}
|
||||
|
||||
// Get timestamp
|
||||
let timestamp = 0;
|
||||
if (typeof parsed.timestamp === "string") {
|
||||
timestamp = new Date(parsed.timestamp).getTime();
|
||||
if (Number.isNaN(timestamp)) {
|
||||
timestamp = 0;
|
||||
}
|
||||
} else if (typeof message.timestamp === "number") {
|
||||
timestamp = message.timestamp;
|
||||
}
|
||||
// Keep detail logs on the usage-summary timestamp path, including nested
|
||||
// fallback; direct Date parsing can leak NaN as null through Gateway JSON.
|
||||
const timestamp = parseTimestamp(parsed)?.getTime() ?? 0;
|
||||
|
||||
// Get usage for assistant messages
|
||||
let tokens: number | undefined;
|
||||
|
||||
Reference in New Issue
Block a user