/** * The datetime contract, stated once for the whole tool surface. * * A datetime that names a clock time but not a zone does not name an instant. * The API used to read such a value as UTC, so a birth time of 09:01 in Chicago * was computed as 09:01 UTC — five hours early. Every planet keeps its sign at * that error scale, so the answer looked right while the Ascendant was two signs * off and every house placement was wrong. * * The rules, identical here and in the Go engine * (apps/api/go-sidecar/internal/api/handlers/datetime_contract.go): * * 1. A datetime WITH a clock time must carry its zone — either a `Z`/`±HH:MM` * suffix on the string, or a companion `timezone` argument. * 2. A date-only value ("1987-07-15") has no clock time to be ambiguous about * and resolves to 12:00 UTC. * 3. Nothing in this layer ever appends a `Z` to a zone-less value. Doing so * asserts a fact the caller never stated. * * Violations are rejected here rather than at the API so the model gets the * correction immediately and no credit is spent on a chart that is wrong. */ /** True when `value` states a clock time but not the zone that clock is in. */ export declare function isNaiveClockTime(value: string): boolean; /** True when `value` already carries its own zone. */ export declare function hasZoneSuffix(value: string): boolean; /** The canonical description for a birth / chart-moment datetime parameter. */ export declare const DATETIME_DESC: string; /** The canonical description for the companion `timezone` parameter. */ export declare const TIMEZONE_DESC = "IANA timezone (e.g. `America/Chicago`); required when the datetime is naive, ignored otherwise."; /** The canonical description for a search-window date parameter (date or datetime). */ export declare const WINDOW_DATE_DESC = "ISO 8601 date or zoned datetime; a naive clock time is rejected. Date-only resolves to 12:00 UTC."; /** The `timezone` property to spread into a tool's inputSchema. */ export declare const TIMEZONE_PROPERTY: { readonly type: "string"; readonly description: "IANA timezone (e.g. `America/Chicago`); required when the datetime is naive, ignored otherwise."; }; /** Build a `_timezone` property with a tailored example. */ export declare function timezoneProperty(label: string, example?: string): { readonly type: "string"; readonly description: `IANA timezone for ${string} (e.g. \`${string}\`); required when the datetime is naive.`; }; /** * The full statement of the datetime contract, for the server `instructions` * field. Every datetime-accepting tool's parameter description points here. * * Setting this on the server (both stdio and HTTP transports) surfaces the * contract to the model once, up front, instead of re-sending it on every * tool description. Clients that fail to propagate `instructions` still get * the rule enforced by the 400 rejection, whose detail rewrites the caller's * own value into each remedy — the model learns from the first violation. */ export declare const DATETIME_CONTRACT_INSTRUCTIONS: string; /** * The rejection message. Names both remedies, rewriting the caller's own value * into each — the commonest failure is not realising the value was ambiguous. */ export declare function ambiguousDatetimeMessage(field: string, value: string, timezoneField?: string): string; /** * Throws unless `value` names an unambiguous instant. * * Call this in every tool handler before building a request body. Passing a * `timezone` satisfies the contract for a zone-less value; the zone itself is * resolved server-side (or by `localToUtcIso` for the `*_utc` endpoints). */ export declare function assertZonedDatetime(field: string, value: unknown, timezone?: unknown, timezoneField?: string): void; /** * Body shape for a `DateTimeInput`-typed request field (`date_time`, * `birth_datetime`, `target_datetime`, `transit_datetime`, …). The Go type * carries an optional `timezone` companion, so a naive local wall-clock time * plus its IANA name is a legitimate value the server resolves itself. */ export type DateTimeInputBody = { iso: string; timezone?: { iana_name: string; }; }; /** * Build a `DateTimeInput` body that PRESERVES zone provenance. * * Use this in place of `localToUtcIso` for any endpoint whose body field is a * `DateTimeInput` (i.e. `{ iso, timezone?, components?, julian_day? }`). Those * endpoints already resolve a naive datetime plus an inner `timezone.iana_name` * server-side and stamp `datetime_zone_source: "timezone"` on the response. * Pre-converting to UTC on the client makes the server believe the caller * supplied a Z-suffixed offset — the metadata then reports * `datetime_zone: "UTC", datetime_zone_source: "offset"` for a call that * actually named "America/Chicago", and `house_cusps` and `natal_chart` * disagree about the same fact (NEW-8). * * - Zone-suffixed input passes through (with `±HHMM` normalised to `±HH:MM`); * server records `source: "offset"`. * - Naive input + `tz` returns `{ iso, timezone: { iana_name: tz } }`; server * records `source: "timezone", zone: tz`. * - Naive input with no `tz` throws (same behaviour as `localToUtcIso`). * - Date-only or unrecognised strings pass through untouched. */ export declare function toDateTimeInputBody(field: string, dt: string, tz?: string, timezoneField?: string): DateTimeInputBody; /** * Convert a datetime to a UTC ISO 8601 string for the endpoints whose field is * named `*_utc` and whose Go type is a strict RFC 3339 `time.Time`. * * Unlike the four ad-hoc `ensureTimezone` helpers this replaces, it never * appends a bare "Z" to a zone-less value — it throws instead. Appending Z * asserts the input was UTC, which is exactly the silent assumption that made * the original defect invisible. * * For endpoints that take a `DateTimeInput` body field (not `*_utc`), prefer * `toDateTimeInputBody` — pre-converting to UTC erases the zone name the * caller supplied, and the response metadata then lies about how the moment * was named. */ export declare function localToUtcIso(field: string, dt: string, tz?: string, timezoneField?: string): string;