UNPKG

@mnemoverse/mcp-memory-server

Version:

Hosted AI agent memory over MCP that learns from outcomes — feedback re-ranks recall — with shared rooms for multi-agent work; one key or OAuth across Claude Code, Cursor, VS Code & ChatGPT, no infra to run

66 lines • 3.5 kB
/** * The one place that decides what an offset-less timestamp MEANS. * * This module exists because the answer was written down twice and implemented * once. Both `since` parameter descriptions and core's own schema say a naive * ISO-8601 value is UTC; `Date.parse("2026-08-08T13:00:00")` says it is local * time. The gap was closed for the future-watermark note in 0.8.1 (src/scope.ts) * and left open in the renderer (src/render.ts), so the same wire value decided * one thing on the way in and another on the way out — a convention two modules * read differently is not a convention. * * It lives on its own rather than in scope.ts because render.ts must not depend * on scope.ts: the dependency deliberately runs the other way (scope.ts takes * `safeInline` as an injected parameter precisely to avoid importing the * renderer), and names.ts is about printing names, not about reading clocks. */ /** * Parse an ISO-8601 instant the way the SERVER does: an offset-less value is * UTC, not local time. Returns epoch milliseconds, or `null` for anything that * cannot be read as an instant. * * The mismatch this fixes is not cosmetic — it moved dates. West of UTC a * perfectly sane watermark was declared to be in the future and every clause of * the future-watermark note was false; east of UTC a genuinely future watermark * was shifted into the past and the note stayed silent, missing the one case it * exists for (review, 2026-08-08). On a rendered result line the same reading * printed `2026-08-01T23:30:00` as `2026-08-02 06:30Z` in America/Los_Angeles: * a memory dated to a day it was not written, with a `Z` asserting it was UTC. * * Date-only values ("2026-08-08") are already parsed as UTC by spec, so only * date-TIME values without an offset need the Z. * * `value` is `unknown` because both call sites read it off the wire, where the * response types are aspirational: `created_at` is typed as a string and can * arrive as a number. A value that is not a string is not an ISO-8601 instant, * so it is `null` here rather than a guess — `new Date(1754082281605)` is a * perfectly good date for a field whose contract says it is text. */ /** An explicit UTC marker or numeric offset at the end of an ISO-8601 value. */ const OFFSET_RE = /(?:Z|[+-]\d{2}:?\d{2})$/i; export function parseAsUtc(value) { if (typeof value !== "string") return null; const s = value.trim(); const hasOffset = OFFSET_RE.test(s); const isDateTime = /\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(s); const t = Date.parse(isDateTime && !hasOffset ? `${s.replace(" ", "T")}Z` : s); return Number.isNaN(t) ? null : t; } /** * The instant `parseAsUtc` reads, as a string a structured consumer can trust: * a value that states its own offset (`Z` or `+hh:mm`) is returned exactly as * sent, so the common core value stays byte-identical; an offset-less value, * which this package reads as UTC by contract, is re-emitted as the UTC * ISO-8601 instant the text renders, because a consumer parsing the naive * string by the ISO-8601 rule would read it as LOCAL time and land on a * different instant than the text shows (review, 2026-09-23). `null` for * anything `parseAsUtc` cannot read. */ export function utcInstant(value) { const t = parseAsUtc(value); if (t === null || typeof value !== "string") return null; return OFFSET_RE.test(value.trim()) ? value : new Date(t).toISOString(); } //# sourceMappingURL=time.js.map