/** UTC+8 wall-clock as `YYYY-MM-DD HH:MM:SS` — matches SQLite's `datetime()` * output shape so cross-source comparisons are well-defined. */ export declare function nowSqliteLocal(): string; /** Convert an absolute Date (or ms) to the local-storage shape. */ export declare function toSqliteLocal(d: Date | number): string; /** Normalize whatever the agent passes for `when_at` into our storage shape. * Accepts: bare ISO (assumed already local), ISO with `Z` or `±HH:MM` offset, * or already-normalized "YYYY-MM-DD HH:MM:SS". Output: "YYYY-MM-DD HH:MM:SS" * in UTC+8. Falls through unchanged on parse failure rather than throwing * so an agent typo doesn't kill the save. */ export declare function normalizeWhenAt(s: string | null | undefined): string | null; export type MemoSource = 'manual' | 'agent' | 'geocoded' | 'browser'; export interface Memo { id: number; platform: string; channel_id: string; thread_id: string; user_id: string; what: string; who: string | null; when_at: string | null; when_text: string | null; where_lat: number | null; where_lng: number | null; where_label: string | null; how: string | null; why: string | null; /** Free-text full intent — preserves the user's complete phrasing when * `what` is an extracted short noun. Optional; null when unset. */ memo: string | null; expires_at: string | null; source: MemoSource; created_at: string; updated_at: string; } export interface OwnerOpts { /** Scope reads/writes/deletes to this user. Empty = unrestricted (tests * / non-IM callers only). Production calls always pass userId. */ userId?: string; } export interface CreateMemoInput { platform: string; channelId: string; threadId: string; userId?: string; what: string; who?: string | null; whenAt?: string | null; whenText?: string | null; whereLat?: number | null; whereLng?: number | null; whereLabel?: string | null; how?: string | null; why?: string | null; /** Free-text full intent — preserve user's complete phrasing when `what` * is a shorter extracted noun. Optional. */ memo?: string | null; expiresAt?: string | null; source?: MemoSource; } export declare function createMemo(input: CreateMemoInput): number; export interface UpdateMemoInput { what?: string; who?: string | null; whenAt?: string | null; whenText?: string | null; whereLat?: number | null; whereLng?: number | null; whereLabel?: string | null; how?: string | null; why?: string | null; memo?: string | null; expiresAt?: string | null; } /** Patch-style update — only fields explicitly present in the input are * written. Pass `null` to clear a field (vs `undefined` = leave alone). */ export declare function updateMemo(id: number, input: UpdateMemoInput, opts?: OwnerOpts): boolean; export declare function getMemo(id: number, opts?: OwnerOpts): Memo | null; export declare function deleteMemo(id: number, opts?: OwnerOpts): boolean; export interface SearchOpts extends OwnerOpts { /** Free-text — matched as `LIKE %query%` against what / who / where_label / * how / why / when_text. Single token; no AND splitting. */ query?: string; /** LIKE filter on `who` only. */ who?: string; /** LIKE filter on `what` only. */ what?: string; /** When true, filter to rows where where_lat IS NOT NULL. */ hasLocation?: boolean; /** ISO range on `when_at` (or fall back to `created_at` when when_at is null). */ whenAfter?: string; whenBefore?: string; /** When true, include expired rows in the result. Default: exclude. */ includeExpired?: boolean; limit?: number; } /** * Search memos. All filters are AND-combined. Empty filter set returns the * most recent memos (= same as listMemos). * * Sort: `created_at DESC` (newest first). Future: relevance score. * * Wildcard escaping: SQL LIKE `%` and `_` in the query become literal chars * via `LIKE ESCAPE '\\'` — the user's question-mark or percent doesn't blow * the search wide open. */ export declare function searchMemos(opts?: SearchOpts): Memo[]; /** Recent memos for this user. Convenience wrapper around searchMemos. */ export declare function listMemos(opts?: OwnerOpts & { limit?: number; }): Memo[]; export declare function sweepExpired(): number; export declare function startSweepTimer(): void; export declare function stopSweepTimer(): void; export declare function closeMemosDb(): void; export declare function parseCoordPair(input: string): { lat: number; lng: number; } | null; export declare function stripCoordPrefix(input: string): string; export type GeocodeOutcome = { ok: true; lat: number; lng: number; formattedAddress: string; } | { ok: false; reason: 'no_key' | 'quota' | 'signature' | 'not_found' | 'http' | 'unknown'; status?: number; message?: string; }; export declare function geocodeAddress(address: string): Promise; export declare function isGeocodingAvailable(): boolean; export declare function wgs84ToGcj02(lat: number, lng: number): { lat: number; lng: number; }; /** * Reverse of wgs84ToGcj02. Useful when an upstream gives us GCJ-02 coords * but our DB convention is WGS-84. * * Common case: iOS Core Location in China returns GCJ-02 by design (Apple * applies the offset to comply with PRC regulations). Telegram on a Chinese * iPhone forwards exactly those coords. Without this reverse, the values * get re-offset when we build amap/baidu URLs → marker lands 500m–1km off. * * Math: GCJ→WGS doesn't have a closed-form inverse, so we use the standard * Newton's-method-style refinement — compute the forward offset at the * GCJ point, subtract to get a candidate WGS point, then iterate. Three * passes converge to sub-meter precision for any reasonable input. */ export declare function gcj02ToWgs84(lat: number, lng: number): { lat: number; lng: number; }; export interface MapUrls { baidu: string; amap: string; google: string; } export declare function mapUrls(lat: number, lng: number, label?: string, addr?: string): MapUrls; export declare function mapUrl(lat: number, lng: number, label?: string, addr?: string): string; //# sourceMappingURL=memos.d.ts.map