@remnic/connector-omi 10.0.21 → 10.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -36,8 +36,8 @@ interface OmiTranscriptSegment {
36
36
  interface OmiConversation {
37
37
  id: string;
38
38
  created_at?: string;
39
- started_at?: string;
40
- finished_at?: string;
39
+ started_at?: string | null;
40
+ finished_at?: string | null;
41
41
  structured?: {
42
42
  title?: string;
43
43
  overview?: string;
@@ -47,7 +47,7 @@ interface OmiConversation {
47
47
  completed?: boolean;
48
48
  }>;
49
49
  };
50
- transcript_segments?: OmiTranscriptSegment[];
50
+ transcript_segments?: OmiTranscriptSegment[] | null;
51
51
  geolocation?: {
52
52
  address?: string | null;
53
53
  } | null;
package/dist/index.js CHANGED
@@ -301,8 +301,8 @@ function conversationToWearable(conversation) {
301
301
  const personId = typeof segment.person_id === "string" && segment.person_id.length > 0 ? segment.person_id : void 0;
302
302
  const speakerId = typeof segment.speaker_id === "string" || typeof segment.speaker_id === "number" ? String(segment.speaker_id) : void 0;
303
303
  const label = typeof segment.speaker_name === "string" && segment.speaker_name.trim().length > 0 ? segment.speaker_name.trim() : typeof segment.speaker === "string" && segment.speaker.trim().length > 0 ? segment.speaker.trim() : void 0;
304
- const startIso = !Number.isNaN(startedAtMs) && typeof segment.start === "number" ? new Date(startedAtMs + segment.start * 1e3).toISOString() : void 0;
305
- const endIso = !Number.isNaN(startedAtMs) && typeof segment.end === "number" ? new Date(startedAtMs + segment.end * 1e3).toISOString() : void 0;
304
+ const startIso = segmentTimestamp(startedAtMs, segment.start);
305
+ const endIso = segmentTimestamp(startedAtMs, segment.end);
306
306
  segments.push({
307
307
  text,
308
308
  // person_id is the most stable key when the user has tagged the
@@ -325,11 +325,16 @@ function conversationToWearable(conversation) {
325
325
  ...title && title.length > 0 ? { title } : {},
326
326
  ...overview && overview.length > 0 ? { summary: overview } : {},
327
327
  startIso: conversation.started_at ?? conversation.created_at ?? "",
328
- ...conversation.finished_at !== void 0 ? { endIso: conversation.finished_at } : {},
328
+ ...typeof conversation.finished_at === "string" ? { endIso: conversation.finished_at } : {},
329
329
  ...typeof address === "string" && address.length > 0 ? { location: address } : {},
330
330
  segments
331
331
  };
332
332
  }
333
+ function segmentTimestamp(startedAtMs, offset) {
334
+ if (typeof offset !== "number") return void 0;
335
+ const instant = new Date(startedAtMs + offset * 1e3);
336
+ return Number.isNaN(instant.getTime()) ? void 0 : instant.toISOString();
337
+ }
333
338
  function memoryToNativeMemory(memory) {
334
339
  const content = typeof memory.content === "string" ? memory.content.trim() : "";
335
340
  if (content.length === 0) return null;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/normalize.ts"],"sourcesContent":["/**\n * @remnic/connector-omi — Omi AI wearable connector.\n *\n * À-la-carte optional companion of @remnic/core (computed-specifier\n * discovery; importing this module self-registers idempotently).\n *\n * Requires an Omi Developer API key from Settings → Developer →\n * Create Key:\n * - key via `REMNIC_OMI_API_KEY` / `OMI_API_KEY` env (or `apiKey`)\n *\n * Legacy External Integration app installs remain supported when both\n * `wearables.sources.omi.appId` and `wearables.sources.omi.userId`\n * are configured.\n */\n\nimport {\n selfRegisterWearableConnector,\n type WearableConnectorFactoryOptions,\n type WearableConnectorRegistration,\n type WearableFetchOptions,\n type WearableFetchPage,\n type WearableNativeMemoryPage,\n type WearableSourceConnector,\n} from \"@remnic/core\";\n\nimport { OmiClient } from \"./client.js\";\nimport {\n conversationToWearable,\n memoryToNativeMemory,\n omiDayWindow,\n OMI_SOURCE_ID,\n} from \"./normalize.js\";\n\nexport { OmiApiError, OmiClient, OMI_DEFAULT_BASE_URL } from \"./client.js\";\nexport type {\n OmiClientOptions,\n OmiConversation,\n OmiConversationsPage,\n OmiMemoriesPage,\n OmiMemory,\n OmiTranscriptSegment,\n} from \"./client.js\";\nexport {\n conversationToWearable,\n memoryToNativeMemory,\n nextIsoDate,\n omiDayWindow,\n OMI_SOURCE_ID,\n timezoneOffsetIso,\n zonedDayBounds,\n zonedDayStartIso,\n} from \"./normalize.js\";\n\nexport function resolveOmiApiKey(\n configuredKey: string | undefined,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n if (typeof configuredKey === \"string\" && configuredKey.trim().length > 0) {\n return configuredKey.trim();\n }\n for (const name of [\"REMNIC_OMI_API_KEY\", \"OMI_API_KEY\"]) {\n const value = env[name];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\nfunction parseOffsetCursor(cursor: string | null | undefined): number {\n if (typeof cursor !== \"string\" || cursor.length === 0) return 0;\n const parsed = Number(cursor);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nexport function createOmiConnector(\n options: WearableConnectorFactoryOptions,\n): WearableSourceConnector {\n // Construction is lazy so `wearables status` works without\n // credentials; the client constructor throws the actionable\n // missing-credential messages at call time.\n let client: OmiClient | null = null;\n const getClient = (): OmiClient => {\n if (!client) {\n client = new OmiClient({\n apiKey: resolveOmiApiKey(options.settings.apiKey) ?? \"\",\n appId: options.settings.appId ?? \"\",\n userId: options.settings.userId ?? \"\",\n baseUrl: options.settings.baseUrl,\n });\n }\n return client;\n };\n\n return {\n id: OMI_SOURCE_ID,\n displayName: \"Omi\",\n async verifyAuth(signal?: AbortSignal) {\n return getClient().verifyAuth(signal);\n },\n async fetchConversations(opts: WearableFetchOptions): Promise<WearableFetchPage> {\n const bounds = omiDayWindow(opts.date, opts.timezone);\n const page = await getClient().listConversations({\n startIso: bounds.startIso,\n endIso: bounds.endIso,\n offset: parseOffsetCursor(opts.cursor),\n signal: opts.signal,\n });\n return {\n conversations: page.conversations\n .filter((conversation) => conversation.discarded !== true)\n .map(conversationToWearable),\n nextCursor: page.nextOffset !== null ? String(page.nextOffset) : null,\n };\n },\n async fetchNativeMemories(opts: {\n cursor?: string | null;\n signal?: AbortSignal;\n }): Promise<WearableNativeMemoryPage> {\n const page = await getClient().listMemories({\n offset: parseOffsetCursor(opts.cursor),\n signal: opts.signal,\n });\n const memories = [];\n for (const memory of page.memories) {\n const mapped = memoryToNativeMemory(memory);\n if (mapped !== null) memories.push(mapped);\n }\n return {\n memories,\n nextCursor: page.nextOffset !== null ? String(page.nextOffset) : null,\n };\n },\n };\n}\n\nexport const wearableConnectorRegistration: WearableConnectorRegistration = {\n id: OMI_SOURCE_ID,\n displayName: \"Omi\",\n factory: createOmiConnector,\n};\n\n/**\n * Idempotently register the connector with the core registry. Importing\n * this module registers it as a side effect; calling this again is safe\n * (returns false when already registered).\n */\nexport const ensureOmiConnectorRegistered =\n selfRegisterWearableConnector(wearableConnectorRegistration);\n","/**\n * Minimal Omi Developer API client (raw fetch, no SDK).\n *\n * Current contract verified against docs.omi.me in 2026-07:\n *\n * - base `https://api.omi.me`, auth `Authorization: Bearer omi_dev_...`\n * (Developer API key from Settings → Developer → Create Key)\n * - `GET /v1/dev/user/conversations` with `limit`/`offset`\n * pagination, `start_date`/`end_date` (ISO 8601), and\n * `include_transcript=true`\n * - `GET /v1/dev/user/memories` with `limit`/`offset`\n * - responses are arrays; every optional field may be absent\n * - errors are FastAPI-shaped `{\"detail\": \"...\"}`\n *\n * The older app-scoped Integrations API remains supported when both\n * `appId` and `userId` are configured.\n *\n * The API key is never logged and never appears in thrown error\n * messages.\n */\n\nimport {\n ConnectorApiError,\n describeNetworkError,\n retryingFetch,\n stripTrailingSlashes,\n} from \"@remnic/core/http-retry\";\n\nexport const OMI_DEFAULT_BASE_URL = \"https://api.omi.me\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n/** Page size for both conversations and memories (API max is 1000). */\nconst PAGE_SIZE = 100;\n\nexport interface OmiTranscriptSegment {\n text?: string;\n speaker?: string;\n speaker_id?: number | string | null;\n speaker_name?: string | null;\n is_user?: boolean;\n person_id?: string | null;\n start?: number;\n end?: number;\n}\n\nexport interface OmiConversation {\n id: string;\n created_at?: string;\n started_at?: string;\n finished_at?: string;\n structured?: {\n title?: string;\n overview?: string;\n category?: string;\n action_items?: Array<{ description?: string; completed?: boolean }>;\n };\n transcript_segments?: OmiTranscriptSegment[];\n geolocation?: { address?: string | null } | null;\n status?: string;\n discarded?: boolean;\n}\n\nexport interface OmiMemory {\n id: string;\n content?: string;\n category?: string;\n tags?: string[];\n created_at?: string;\n}\n\nexport interface OmiConversationsPage {\n conversations: OmiConversation[];\n nextOffset: number | null;\n}\n\nexport interface OmiMemoriesPage {\n memories: OmiMemory[];\n nextOffset: number | null;\n}\n\nexport interface OmiClientOptions {\n apiKey: string;\n appId?: string;\n userId?: string;\n baseUrl?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\ntype OmiApiMode = \"developer\" | \"integration\";\n\nexport class OmiApiError extends ConnectorApiError {\n constructor(\n message: string,\n status?: number,\n detail?: string,\n ) {\n super(message, status, detail);\n this.name = \"OmiApiError\";\n }\n}\n\nexport class OmiClient {\n private readonly apiKey: string;\n private readonly appId?: string;\n private readonly userId?: string;\n private readonly mode: OmiApiMode;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: OmiClientOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new OmiApiError(\n \"Omi API key is missing. Set wearables.sources.omi.apiKey or the \" +\n \"OMI_API_KEY environment variable (create a Developer API key in \" +\n \"Settings → Developer → Create Key in the Omi app).\",\n );\n }\n\n const appId =\n typeof options.appId === \"string\" && options.appId.trim().length > 0\n ? options.appId.trim()\n : undefined;\n const userId =\n typeof options.userId === \"string\" && options.userId.trim().length > 0\n ? options.userId.trim()\n : undefined;\n if ((appId === undefined) !== (userId === undefined)) {\n throw new OmiApiError(\n \"Omi legacy integration mode requires both wearables.sources.omi.appId \" +\n \"and wearables.sources.omi.userId. Omit both to use the Developer \" +\n \"API key-only process.\",\n );\n }\n\n this.apiKey = options.apiKey.trim();\n this.appId = appId;\n this.userId = userId;\n this.mode = appId !== undefined && userId !== undefined ? \"integration\" : \"developer\";\n this.baseUrl = stripTrailingSlashes(options.baseUrl ?? OMI_DEFAULT_BASE_URL);\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep =\n options.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));\n }\n\n /** One page of completed conversations inside [startIso, endIso). */\n async listConversations(params: {\n startIso: string;\n endIso: string;\n offset?: number;\n signal?: AbortSignal;\n }): Promise<OmiConversationsPage> {\n const offset = params.offset ?? 0;\n const search = new URLSearchParams({\n limit: String(PAGE_SIZE),\n offset: String(offset),\n start_date: params.startIso,\n end_date: params.endIso,\n });\n let payload: unknown;\n if (this.mode === \"developer\") {\n search.set(\"include_transcript\", \"true\");\n payload = await this.requestJson(\n `/v1/dev/user/conversations?${search.toString()}`,\n params.signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n search.set(\"include_discarded\", \"false\");\n // -1 = unlimited; the legacy API default silently truncates\n // transcripts to their first 100 segments.\n search.set(\"max_transcript_segments\", \"-1\");\n // Repeated param (FastAPI List[str]) — comma-joining does NOT work.\n search.append(\"statuses\", \"completed\");\n payload = await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/conversations?${search.toString()}`,\n params.signal,\n );\n }\n const conversations =\n this.mode === \"developer\"\n ? payload\n : (payload as { conversations?: unknown }).conversations;\n if (!Array.isArray(conversations)) {\n throw new OmiApiError(\n this.mode === \"developer\"\n ? \"Omi API returned an unexpected conversations shape (expected array)\"\n : \"Omi API returned an unexpected conversations shape (missing conversations array)\",\n );\n }\n const valid = conversations.filter(\n (entry): entry is OmiConversation =>\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"string\",\n )\n .filter(isCompletedOrStatuslessConversation)\n .filter((conversation) =>\n startsInsideHalfOpenWindow(conversation, params.startIso, params.endIso),\n );\n return {\n conversations: valid,\n nextOffset: conversations.length === PAGE_SIZE ? offset + PAGE_SIZE : null,\n };\n }\n\n /** One page of Omi memories (provider-extracted facts). */\n async listMemories(params: {\n offset?: number;\n signal?: AbortSignal;\n } = {}): Promise<OmiMemoriesPage> {\n const offset = params.offset ?? 0;\n const search = new URLSearchParams({\n limit: String(PAGE_SIZE),\n offset: String(offset),\n });\n let payload: unknown;\n if (this.mode === \"developer\") {\n payload = await this.requestJson(\n `/v1/dev/user/memories?${search.toString()}`,\n params.signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n payload = await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/memories?${search.toString()}`,\n params.signal,\n );\n }\n const memories =\n this.mode === \"developer\" ? payload : (payload as { memories?: unknown }).memories;\n if (!Array.isArray(memories)) {\n throw new OmiApiError(\n this.mode === \"developer\"\n ? \"Omi API returned an unexpected memories shape (expected array)\"\n : \"Omi API returned an unexpected memories shape (missing memories array)\",\n );\n }\n const valid = memories.filter(\n (entry): entry is OmiMemory =>\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"string\",\n );\n return {\n memories: valid,\n nextOffset: memories.length === PAGE_SIZE ? offset + PAGE_SIZE : null,\n };\n }\n\n async verifyAuth(signal?: AbortSignal): Promise<{ ok: boolean; detail?: string }> {\n try {\n const search = new URLSearchParams({\n limit: \"1\",\n offset: \"0\",\n });\n if (this.mode === \"developer\") {\n search.set(\"include_transcript\", \"false\");\n await this.requestJson(\n `/v1/dev/user/conversations?${search.toString()}`,\n signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n search.set(\"max_transcript_segments\", \"1\");\n await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/conversations?${search.toString()}`,\n signal,\n );\n }\n return { ok: true };\n } catch (err) {\n if (err instanceof OmiApiError && err.status !== undefined) {\n // The auth chain yields distinct, actionable detail strings.\n // Legacy integration mode can also surface app/user capability\n // failures, so keep those hints mode-specific.\n const hint =\n err.status === 401\n ? \"missing/malformed Authorization header\"\n : err.status === 403\n ? this.mode === \"developer\"\n ? \"Developer API key rejected or missing conversation access\"\n : \"key rejected, app not enabled for this uid, or missing read_conversations capability\"\n : err.status === 404\n ? this.mode === \"developer\"\n ? \"Developer API endpoint not found — check the configured baseUrl\"\n : \"app not found — check wearables.sources.omi.appId\"\n : undefined;\n return {\n ok: false,\n detail: [err.detail, hint].filter(Boolean).join(\" — \") || err.message,\n };\n }\n // OmiApiError messages are our own constructed strings (already\n // scrubbed — network text is reduced to name + code inside\n // requestJson); foreign errors reduce to name + errno code so raw\n // Node text (paths, loader stacks) never reaches operator\n // surfaces.\n return {\n ok: false,\n detail: err instanceof OmiApiError ? err.message : describeNetworkError(err),\n };\n }\n }\n\n private async requestJson(pathAndQuery: string, signal?: AbortSignal): Promise<unknown> {\n const response = await retryingFetch(`${this.baseUrl}${pathAndQuery}`, {\n init: {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n },\n },\n fetchImpl: this.fetchImpl,\n sleep: this.sleep,\n signal,\n timeoutMs: this.timeoutMs,\n networkError: (err, attempts) =>\n new OmiApiError(\n `Omi API request failed after ${attempts} attempts: ${describeNetworkError(err)}`,\n ),\n retryableError: async (retryable) =>\n new OmiApiError(\n `Omi API responded ${retryable.status}`,\n retryable.status,\n await readDetail(retryable),\n ),\n });\n if (!response.ok) {\n throw new OmiApiError(\n `Omi API responded ${response.status} for ${pathAndQuery.split(\"?\")[0]}`,\n response.status,\n await readDetail(response),\n );\n }\n try {\n return await response.json();\n } catch {\n throw new OmiApiError(\"Omi API returned a non-JSON body\");\n }\n }\n}\n\nfunction startsInsideHalfOpenWindow(\n conversation: OmiConversation,\n startIso: string,\n endIso: string,\n): boolean {\n const startMs = Date.parse(startIso);\n const endMs = Date.parse(endIso);\n if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) return true;\n\n const conversationIso =\n typeof conversation.started_at === \"string\"\n ? conversation.started_at\n : conversation.created_at;\n if (typeof conversationIso !== \"string\" || conversationIso.length === 0) {\n return true;\n }\n const conversationMs = Date.parse(conversationIso);\n if (!Number.isFinite(conversationMs)) return true;\n return conversationMs >= startMs && conversationMs < endMs;\n}\n\nfunction isCompletedOrStatuslessConversation(conversation: OmiConversation): boolean {\n if (typeof conversation.status !== \"string\" || conversation.status.length === 0) {\n return true;\n }\n return conversation.status === \"completed\";\n}\n\nasync function readDetail(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.clone().json()) as { detail?: unknown };\n return typeof body?.detail === \"string\" ? body.detail : undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Normalize Omi conversations into Remnic's provider-agnostic\n * `WearableConversation` shape, plus the timezone-aware day-window\n * helpers the Omi API needs (its date filters are ISO datetimes).\n *\n * Legacy Omi integration segments carry `is_user` for the wearer,\n * opaque `SPEAKER_NN` diarization labels, optional `person_id`s\n * (user-defined people), and start/end offsets in seconds relative to\n * the conversation start. The current Developer API returns\n * `speaker_name`/`speaker_id` instead; normalize both shapes.\n */\n\nimport {\n activityDayWindow,\n assertValidTimezone,\n timezoneOffsetIso as resolveTimezoneOffset,\n type WearableConversation,\n type WearableNativeMemory,\n type WearableTranscriptSegment,\n} from \"@remnic/core\";\n\nimport type { OmiConversation, OmiMemory } from \"./client.js\";\n\nexport const OMI_SOURCE_ID = \"omi\";\n\n/** \"GMT+05:30\" → \"+05:30\"; an unknown zone falls back to \"+00:00\" so a bad config never crashes the sync. */\nexport function timezoneOffsetIso(instant: Date, timezone: string): string {\n try {\n return resolveTimezoneOffset(instant, timezone);\n } catch {\n return \"+00:00\";\n }\n}\n\nexport function nextIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() + 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * Half-open [start, end) local-midnight ISO bounds of a local day, in the\n * offset-datetime form the Omi API's date filters expect. Field names and\n * format map to the Omi API; the DST-aware window math is core's\n * `activityDayWindow`.\n */\nexport function omiDayWindow(\n date: string,\n timezone: string,\n): { startIso: string; endIso: string } {\n // Keep this package's documented never-throw zone contract (see\n // `timezoneOffsetIso`): an unknown zone resolves to a UTC day instead of\n // surfacing core's fail-fast RangeError.\n let zone = timezone;\n try {\n assertValidTimezone(timezone);\n } catch {\n zone = \"UTC\";\n }\n const { startUtc, endUtc } = activityDayWindow(date, zone);\n return {\n startIso: instantToOffsetIso(new Date(startUtc), zone),\n endIso: instantToOffsetIso(new Date(endUtc), zone),\n };\n}\n\n/**\n * Render a resolved instant as an offset datetime in `timezone`. Formatting\n * the wall-clock at that instant (not `T00:00:00`) is what preserves skipped\n * local midnights: a 00:00→01:00 spring-forward resolves to 01:00 local, and\n * rebuilding `T00:00:00` would move the instant an hour into the prior day.\n */\nfunction instantToOffsetIso(instant: Date, timezone: string): string {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n }).formatToParts(instant);\n const field = (type: Intl.DateTimeFormatPartTypes): string =>\n parts.find((part) => part.type === type)?.value ?? \"00\";\n const day = `${field(\"year\")}-${field(\"month\")}-${field(\"day\")}`;\n const time = `${field(\"hour\")}:${field(\"minute\")}:${field(\"second\")}`;\n return `${day}T${time}${timezoneOffsetIso(instant, timezone)}`;\n}\n\n/**\n * @deprecated Compatibility wrapper (pre-core-refactor export); delegates to\n * `omiDayWindow`. Returns the resolved start bound, not a rebuilt midnight.\n */\nexport function zonedDayStartIso(date: string, timezone: string): string {\n warnLegacyDayWindow(\"zonedDayStartIso\");\n return omiDayWindow(date, timezone).startIso;\n}\n\n/** @deprecated Compatibility wrapper (pre-core-refactor export); delegates to `omiDayWindow`. */\nexport function zonedDayBounds(\n date: string,\n timezone: string,\n): { startIso: string; endIso: string } {\n warnLegacyDayWindow(\"zonedDayBounds\");\n return omiDayWindow(date, timezone);\n}\n\nconst warnedLegacyDayWindow = new Set<string>();\n\nfunction warnLegacyDayWindow(name: \"zonedDayBounds\" | \"zonedDayStartIso\"): void {\n if (warnedLegacyDayWindow.has(name)) return;\n warnedLegacyDayWindow.add(name);\n process.emitWarning(\n `@remnic/connector-omi: ${name} is deprecated; use omiDayWindow instead.`,\n { type: \"DeprecationWarning\", code: `REMNIC_DEP_OMI_${name.toUpperCase()}` },\n );\n}\n\nexport function conversationToWearable(\n conversation: OmiConversation,\n): WearableConversation {\n const startedAtMs = conversation.started_at\n ? Date.parse(conversation.started_at)\n : Number.NaN;\n const segments: WearableTranscriptSegment[] = [];\n for (const segment of conversation.transcript_segments ?? []) {\n const text = typeof segment.text === \"string\" ? segment.text.trim() : \"\";\n if (text.length === 0) continue;\n const isWearer = segment.is_user === true;\n const personId =\n typeof segment.person_id === \"string\" && segment.person_id.length > 0\n ? segment.person_id\n : undefined;\n const speakerId =\n typeof segment.speaker_id === \"string\" || typeof segment.speaker_id === \"number\"\n ? String(segment.speaker_id)\n : undefined;\n const label =\n typeof segment.speaker_name === \"string\" && segment.speaker_name.trim().length > 0\n ? segment.speaker_name.trim()\n : typeof segment.speaker === \"string\" && segment.speaker.trim().length > 0\n ? segment.speaker.trim()\n : undefined;\n const startIso =\n !Number.isNaN(startedAtMs) && typeof segment.start === \"number\"\n ? new Date(startedAtMs + segment.start * 1_000).toISOString()\n : undefined;\n const endIso =\n !Number.isNaN(startedAtMs) && typeof segment.end === \"number\"\n ? new Date(startedAtMs + segment.end * 1_000).toISOString()\n : undefined;\n segments.push({\n text,\n // person_id is the most stable key when the user has tagged the\n // speaker as a known person in Omi. The Developer API's\n // speaker_id is the next best stable key, then the diarization\n // label.\n speakerKey: isWearer ? \"user\" : (personId ?? speakerId ?? label ?? \"unknown\"),\n ...(label !== undefined ? { speakerName: label } : {}),\n ...(isWearer ? { isWearer: true } : {}),\n ...(startIso !== undefined ? { startIso } : {}),\n ...(endIso !== undefined ? { endIso } : {}),\n });\n }\n\n const title = conversation.structured?.title?.trim();\n const overview = conversation.structured?.overview?.trim();\n const address = conversation.geolocation?.address;\n\n return {\n id: conversation.id,\n source: OMI_SOURCE_ID,\n ...(title && title.length > 0 ? { title } : {}),\n ...(overview && overview.length > 0 ? { summary: overview } : {}),\n startIso: conversation.started_at ?? conversation.created_at ?? \"\",\n ...(conversation.finished_at !== undefined\n ? { endIso: conversation.finished_at }\n : {}),\n ...(typeof address === \"string\" && address.length > 0\n ? { location: address }\n : {}),\n segments,\n };\n}\n\nexport function memoryToNativeMemory(memory: OmiMemory): WearableNativeMemory | null {\n const content = typeof memory.content === \"string\" ? memory.content.trim() : \"\";\n if (content.length === 0) return null;\n return {\n id: memory.id,\n content,\n ...(memory.created_at !== undefined ? { createdIso: memory.created_at } : {}),\n ...(Array.isArray(memory.tags) && memory.tags.length > 0\n ? { tags: memory.tags }\n : {}),\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/normalize.ts"],"sourcesContent":["/**\n * @remnic/connector-omi — Omi AI wearable connector.\n *\n * À-la-carte optional companion of @remnic/core (computed-specifier\n * discovery; importing this module self-registers idempotently).\n *\n * Requires an Omi Developer API key from Settings → Developer →\n * Create Key:\n * - key via `REMNIC_OMI_API_KEY` / `OMI_API_KEY` env (or `apiKey`)\n *\n * Legacy External Integration app installs remain supported when both\n * `wearables.sources.omi.appId` and `wearables.sources.omi.userId`\n * are configured.\n */\n\nimport {\n selfRegisterWearableConnector,\n type WearableConnectorFactoryOptions,\n type WearableConnectorRegistration,\n type WearableFetchOptions,\n type WearableFetchPage,\n type WearableNativeMemoryPage,\n type WearableSourceConnector,\n} from \"@remnic/core\";\n\nimport { OmiClient } from \"./client.js\";\nimport {\n conversationToWearable,\n memoryToNativeMemory,\n omiDayWindow,\n OMI_SOURCE_ID,\n} from \"./normalize.js\";\n\nexport { OmiApiError, OmiClient, OMI_DEFAULT_BASE_URL } from \"./client.js\";\nexport type {\n OmiClientOptions,\n OmiConversation,\n OmiConversationsPage,\n OmiMemoriesPage,\n OmiMemory,\n OmiTranscriptSegment,\n} from \"./client.js\";\nexport {\n conversationToWearable,\n memoryToNativeMemory,\n nextIsoDate,\n omiDayWindow,\n OMI_SOURCE_ID,\n timezoneOffsetIso,\n zonedDayBounds,\n zonedDayStartIso,\n} from \"./normalize.js\";\n\nexport function resolveOmiApiKey(\n configuredKey: string | undefined,\n env: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n if (typeof configuredKey === \"string\" && configuredKey.trim().length > 0) {\n return configuredKey.trim();\n }\n for (const name of [\"REMNIC_OMI_API_KEY\", \"OMI_API_KEY\"]) {\n const value = env[name];\n if (typeof value === \"string\" && value.trim().length > 0) {\n return value.trim();\n }\n }\n return undefined;\n}\n\nfunction parseOffsetCursor(cursor: string | null | undefined): number {\n if (typeof cursor !== \"string\" || cursor.length === 0) return 0;\n const parsed = Number(cursor);\n return Number.isInteger(parsed) && parsed > 0 ? parsed : 0;\n}\n\nexport function createOmiConnector(\n options: WearableConnectorFactoryOptions,\n): WearableSourceConnector {\n // Construction is lazy so `wearables status` works without\n // credentials; the client constructor throws the actionable\n // missing-credential messages at call time.\n let client: OmiClient | null = null;\n const getClient = (): OmiClient => {\n if (!client) {\n client = new OmiClient({\n apiKey: resolveOmiApiKey(options.settings.apiKey) ?? \"\",\n appId: options.settings.appId ?? \"\",\n userId: options.settings.userId ?? \"\",\n baseUrl: options.settings.baseUrl,\n });\n }\n return client;\n };\n\n return {\n id: OMI_SOURCE_ID,\n displayName: \"Omi\",\n async verifyAuth(signal?: AbortSignal) {\n return getClient().verifyAuth(signal);\n },\n async fetchConversations(opts: WearableFetchOptions): Promise<WearableFetchPage> {\n const bounds = omiDayWindow(opts.date, opts.timezone);\n const page = await getClient().listConversations({\n startIso: bounds.startIso,\n endIso: bounds.endIso,\n offset: parseOffsetCursor(opts.cursor),\n signal: opts.signal,\n });\n return {\n conversations: page.conversations\n .filter((conversation) => conversation.discarded !== true)\n .map(conversationToWearable),\n nextCursor: page.nextOffset !== null ? String(page.nextOffset) : null,\n };\n },\n async fetchNativeMemories(opts: {\n cursor?: string | null;\n signal?: AbortSignal;\n }): Promise<WearableNativeMemoryPage> {\n const page = await getClient().listMemories({\n offset: parseOffsetCursor(opts.cursor),\n signal: opts.signal,\n });\n const memories = [];\n for (const memory of page.memories) {\n const mapped = memoryToNativeMemory(memory);\n if (mapped !== null) memories.push(mapped);\n }\n return {\n memories,\n nextCursor: page.nextOffset !== null ? String(page.nextOffset) : null,\n };\n },\n };\n}\n\nexport const wearableConnectorRegistration: WearableConnectorRegistration = {\n id: OMI_SOURCE_ID,\n displayName: \"Omi\",\n factory: createOmiConnector,\n};\n\n/**\n * Idempotently register the connector with the core registry. Importing\n * this module registers it as a side effect; calling this again is safe\n * (returns false when already registered).\n */\nexport const ensureOmiConnectorRegistered =\n selfRegisterWearableConnector(wearableConnectorRegistration);\n","/**\n * Minimal Omi Developer API client (raw fetch, no SDK).\n *\n * Current contract verified against docs.omi.me in 2026-07:\n *\n * - base `https://api.omi.me`, auth `Authorization: Bearer omi_dev_...`\n * (Developer API key from Settings → Developer → Create Key)\n * - `GET /v1/dev/user/conversations` with `limit`/`offset`\n * pagination, `start_date`/`end_date` (ISO 8601), and\n * `include_transcript=true`\n * - `GET /v1/dev/user/memories` with `limit`/`offset`\n * - responses are arrays; every optional field may be absent\n * - errors are FastAPI-shaped `{\"detail\": \"...\"}`\n *\n * The older app-scoped Integrations API remains supported when both\n * `appId` and `userId` are configured.\n *\n * The API key is never logged and never appears in thrown error\n * messages.\n */\n\nimport {\n ConnectorApiError,\n describeNetworkError,\n retryingFetch,\n stripTrailingSlashes,\n} from \"@remnic/core/http-retry\";\n\nexport const OMI_DEFAULT_BASE_URL = \"https://api.omi.me\";\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n/** Page size for both conversations and memories (API max is 1000). */\nconst PAGE_SIZE = 100;\n\nexport interface OmiTranscriptSegment {\n text?: string;\n speaker?: string;\n speaker_id?: number | string | null;\n speaker_name?: string | null;\n is_user?: boolean;\n person_id?: string | null;\n start?: number;\n end?: number;\n}\n\nexport interface OmiConversation {\n id: string;\n created_at?: string;\n started_at?: string | null;\n finished_at?: string | null;\n structured?: {\n title?: string;\n overview?: string;\n category?: string;\n action_items?: Array<{ description?: string; completed?: boolean }>;\n };\n transcript_segments?: OmiTranscriptSegment[] | null;\n geolocation?: { address?: string | null } | null;\n status?: string;\n discarded?: boolean;\n}\n\nexport interface OmiMemory {\n id: string;\n content?: string;\n category?: string;\n tags?: string[];\n created_at?: string;\n}\n\nexport interface OmiConversationsPage {\n conversations: OmiConversation[];\n nextOffset: number | null;\n}\n\nexport interface OmiMemoriesPage {\n memories: OmiMemory[];\n nextOffset: number | null;\n}\n\nexport interface OmiClientOptions {\n apiKey: string;\n appId?: string;\n userId?: string;\n baseUrl?: string;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n sleep?: (ms: number) => Promise<void>;\n}\n\ntype OmiApiMode = \"developer\" | \"integration\";\n\nexport class OmiApiError extends ConnectorApiError {\n constructor(\n message: string,\n status?: number,\n detail?: string,\n ) {\n super(message, status, detail);\n this.name = \"OmiApiError\";\n }\n}\n\nexport class OmiClient {\n private readonly apiKey: string;\n private readonly appId?: string;\n private readonly userId?: string;\n private readonly mode: OmiApiMode;\n private readonly baseUrl: string;\n private readonly fetchImpl: typeof fetch;\n private readonly timeoutMs: number;\n private readonly sleep: (ms: number) => Promise<void>;\n\n constructor(options: OmiClientOptions) {\n if (typeof options.apiKey !== \"string\" || options.apiKey.trim().length === 0) {\n throw new OmiApiError(\n \"Omi API key is missing. Set wearables.sources.omi.apiKey or the \" +\n \"OMI_API_KEY environment variable (create a Developer API key in \" +\n \"Settings → Developer → Create Key in the Omi app).\",\n );\n }\n\n const appId =\n typeof options.appId === \"string\" && options.appId.trim().length > 0\n ? options.appId.trim()\n : undefined;\n const userId =\n typeof options.userId === \"string\" && options.userId.trim().length > 0\n ? options.userId.trim()\n : undefined;\n if ((appId === undefined) !== (userId === undefined)) {\n throw new OmiApiError(\n \"Omi legacy integration mode requires both wearables.sources.omi.appId \" +\n \"and wearables.sources.omi.userId. Omit both to use the Developer \" +\n \"API key-only process.\",\n );\n }\n\n this.apiKey = options.apiKey.trim();\n this.appId = appId;\n this.userId = userId;\n this.mode = appId !== undefined && userId !== undefined ? \"integration\" : \"developer\";\n this.baseUrl = stripTrailingSlashes(options.baseUrl ?? OMI_DEFAULT_BASE_URL);\n this.fetchImpl = options.fetchImpl ?? fetch;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.sleep =\n options.sleep ?? ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms)));\n }\n\n /** One page of completed conversations inside [startIso, endIso). */\n async listConversations(params: {\n startIso: string;\n endIso: string;\n offset?: number;\n signal?: AbortSignal;\n }): Promise<OmiConversationsPage> {\n const offset = params.offset ?? 0;\n const search = new URLSearchParams({\n limit: String(PAGE_SIZE),\n offset: String(offset),\n start_date: params.startIso,\n end_date: params.endIso,\n });\n let payload: unknown;\n if (this.mode === \"developer\") {\n search.set(\"include_transcript\", \"true\");\n payload = await this.requestJson(\n `/v1/dev/user/conversations?${search.toString()}`,\n params.signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n search.set(\"include_discarded\", \"false\");\n // -1 = unlimited; the legacy API default silently truncates\n // transcripts to their first 100 segments.\n search.set(\"max_transcript_segments\", \"-1\");\n // Repeated param (FastAPI List[str]) — comma-joining does NOT work.\n search.append(\"statuses\", \"completed\");\n payload = await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/conversations?${search.toString()}`,\n params.signal,\n );\n }\n const conversations =\n this.mode === \"developer\"\n ? payload\n : (payload as { conversations?: unknown }).conversations;\n if (!Array.isArray(conversations)) {\n throw new OmiApiError(\n this.mode === \"developer\"\n ? \"Omi API returned an unexpected conversations shape (expected array)\"\n : \"Omi API returned an unexpected conversations shape (missing conversations array)\",\n );\n }\n const valid = conversations.filter(\n (entry): entry is OmiConversation =>\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"string\",\n )\n .filter(isCompletedOrStatuslessConversation)\n .filter((conversation) =>\n startsInsideHalfOpenWindow(conversation, params.startIso, params.endIso),\n );\n return {\n conversations: valid,\n nextOffset: conversations.length === PAGE_SIZE ? offset + PAGE_SIZE : null,\n };\n }\n\n /** One page of Omi memories (provider-extracted facts). */\n async listMemories(params: {\n offset?: number;\n signal?: AbortSignal;\n } = {}): Promise<OmiMemoriesPage> {\n const offset = params.offset ?? 0;\n const search = new URLSearchParams({\n limit: String(PAGE_SIZE),\n offset: String(offset),\n });\n let payload: unknown;\n if (this.mode === \"developer\") {\n payload = await this.requestJson(\n `/v1/dev/user/memories?${search.toString()}`,\n params.signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n payload = await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/memories?${search.toString()}`,\n params.signal,\n );\n }\n const memories =\n this.mode === \"developer\" ? payload : (payload as { memories?: unknown }).memories;\n if (!Array.isArray(memories)) {\n throw new OmiApiError(\n this.mode === \"developer\"\n ? \"Omi API returned an unexpected memories shape (expected array)\"\n : \"Omi API returned an unexpected memories shape (missing memories array)\",\n );\n }\n const valid = memories.filter(\n (entry): entry is OmiMemory =>\n entry !== null &&\n typeof entry === \"object\" &&\n typeof (entry as { id?: unknown }).id === \"string\",\n );\n return {\n memories: valid,\n nextOffset: memories.length === PAGE_SIZE ? offset + PAGE_SIZE : null,\n };\n }\n\n async verifyAuth(signal?: AbortSignal): Promise<{ ok: boolean; detail?: string }> {\n try {\n const search = new URLSearchParams({\n limit: \"1\",\n offset: \"0\",\n });\n if (this.mode === \"developer\") {\n search.set(\"include_transcript\", \"false\");\n await this.requestJson(\n `/v1/dev/user/conversations?${search.toString()}`,\n signal,\n );\n } else {\n search.set(\"uid\", this.userId ?? \"\");\n search.set(\"max_transcript_segments\", \"1\");\n await this.requestJson(\n `/v2/integrations/${encodeURIComponent(this.appId ?? \"\")}/conversations?${search.toString()}`,\n signal,\n );\n }\n return { ok: true };\n } catch (err) {\n if (err instanceof OmiApiError && err.status !== undefined) {\n // The auth chain yields distinct, actionable detail strings.\n // Legacy integration mode can also surface app/user capability\n // failures, so keep those hints mode-specific.\n const hint =\n err.status === 401\n ? \"missing/malformed Authorization header\"\n : err.status === 403\n ? this.mode === \"developer\"\n ? \"Developer API key rejected or missing conversation access\"\n : \"key rejected, app not enabled for this uid, or missing read_conversations capability\"\n : err.status === 404\n ? this.mode === \"developer\"\n ? \"Developer API endpoint not found — check the configured baseUrl\"\n : \"app not found — check wearables.sources.omi.appId\"\n : undefined;\n return {\n ok: false,\n detail: [err.detail, hint].filter(Boolean).join(\" — \") || err.message,\n };\n }\n // OmiApiError messages are our own constructed strings (already\n // scrubbed — network text is reduced to name + code inside\n // requestJson); foreign errors reduce to name + errno code so raw\n // Node text (paths, loader stacks) never reaches operator\n // surfaces.\n return {\n ok: false,\n detail: err instanceof OmiApiError ? err.message : describeNetworkError(err),\n };\n }\n }\n\n private async requestJson(pathAndQuery: string, signal?: AbortSignal): Promise<unknown> {\n const response = await retryingFetch(`${this.baseUrl}${pathAndQuery}`, {\n init: {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n },\n },\n fetchImpl: this.fetchImpl,\n sleep: this.sleep,\n signal,\n timeoutMs: this.timeoutMs,\n networkError: (err, attempts) =>\n new OmiApiError(\n `Omi API request failed after ${attempts} attempts: ${describeNetworkError(err)}`,\n ),\n retryableError: async (retryable) =>\n new OmiApiError(\n `Omi API responded ${retryable.status}`,\n retryable.status,\n await readDetail(retryable),\n ),\n });\n if (!response.ok) {\n throw new OmiApiError(\n `Omi API responded ${response.status} for ${pathAndQuery.split(\"?\")[0]}`,\n response.status,\n await readDetail(response),\n );\n }\n try {\n return await response.json();\n } catch {\n throw new OmiApiError(\"Omi API returned a non-JSON body\");\n }\n }\n}\n\nfunction startsInsideHalfOpenWindow(\n conversation: OmiConversation,\n startIso: string,\n endIso: string,\n): boolean {\n const startMs = Date.parse(startIso);\n const endMs = Date.parse(endIso);\n if (!Number.isFinite(startMs) || !Number.isFinite(endMs)) return true;\n\n const conversationIso =\n typeof conversation.started_at === \"string\"\n ? conversation.started_at\n : conversation.created_at;\n if (typeof conversationIso !== \"string\" || conversationIso.length === 0) {\n return true;\n }\n const conversationMs = Date.parse(conversationIso);\n if (!Number.isFinite(conversationMs)) return true;\n return conversationMs >= startMs && conversationMs < endMs;\n}\n\nfunction isCompletedOrStatuslessConversation(conversation: OmiConversation): boolean {\n if (typeof conversation.status !== \"string\" || conversation.status.length === 0) {\n return true;\n }\n return conversation.status === \"completed\";\n}\n\nasync function readDetail(response: Response): Promise<string | undefined> {\n try {\n const body = (await response.clone().json()) as { detail?: unknown };\n return typeof body?.detail === \"string\" ? body.detail : undefined;\n } catch {\n return undefined;\n }\n}\n","/**\n * Normalize Omi conversations into Remnic's provider-agnostic\n * `WearableConversation` shape, plus the timezone-aware day-window\n * helpers the Omi API needs (its date filters are ISO datetimes).\n *\n * Legacy Omi integration segments carry `is_user` for the wearer,\n * opaque `SPEAKER_NN` diarization labels, optional `person_id`s\n * (user-defined people), and start/end offsets in seconds relative to\n * the conversation start. The current Developer API returns\n * `speaker_name`/`speaker_id` instead; normalize both shapes.\n */\n\nimport {\n activityDayWindow,\n assertValidTimezone,\n timezoneOffsetIso as resolveTimezoneOffset,\n type WearableConversation,\n type WearableNativeMemory,\n type WearableTranscriptSegment,\n} from \"@remnic/core\";\n\nimport type { OmiConversation, OmiMemory } from \"./client.js\";\n\nexport const OMI_SOURCE_ID = \"omi\";\n\n/** \"GMT+05:30\" → \"+05:30\"; an unknown zone falls back to \"+00:00\" so a bad config never crashes the sync. */\nexport function timezoneOffsetIso(instant: Date, timezone: string): string {\n try {\n return resolveTimezoneOffset(instant, timezone);\n } catch {\n return \"+00:00\";\n }\n}\n\nexport function nextIsoDate(date: string): string {\n const parsed = new Date(`${date}T00:00:00Z`);\n parsed.setUTCDate(parsed.getUTCDate() + 1);\n return parsed.toISOString().slice(0, 10);\n}\n\n/**\n * Half-open [start, end) local-midnight ISO bounds of a local day, in the\n * offset-datetime form the Omi API's date filters expect. Field names and\n * format map to the Omi API; the DST-aware window math is core's\n * `activityDayWindow`.\n */\nexport function omiDayWindow(\n date: string,\n timezone: string,\n): { startIso: string; endIso: string } {\n // Keep this package's documented never-throw zone contract (see\n // `timezoneOffsetIso`): an unknown zone resolves to a UTC day instead of\n // surfacing core's fail-fast RangeError.\n let zone = timezone;\n try {\n assertValidTimezone(timezone);\n } catch {\n zone = \"UTC\";\n }\n const { startUtc, endUtc } = activityDayWindow(date, zone);\n return {\n startIso: instantToOffsetIso(new Date(startUtc), zone),\n endIso: instantToOffsetIso(new Date(endUtc), zone),\n };\n}\n\n/**\n * Render a resolved instant as an offset datetime in `timezone`. Formatting\n * the wall-clock at that instant (not `T00:00:00`) is what preserves skipped\n * local midnights: a 00:00→01:00 spring-forward resolves to 01:00 local, and\n * rebuilding `T00:00:00` would move the instant an hour into the prior day.\n */\nfunction instantToOffsetIso(instant: Date, timezone: string): string {\n const parts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hourCycle: \"h23\",\n }).formatToParts(instant);\n const field = (type: Intl.DateTimeFormatPartTypes): string =>\n parts.find((part) => part.type === type)?.value ?? \"00\";\n const day = `${field(\"year\")}-${field(\"month\")}-${field(\"day\")}`;\n const time = `${field(\"hour\")}:${field(\"minute\")}:${field(\"second\")}`;\n return `${day}T${time}${timezoneOffsetIso(instant, timezone)}`;\n}\n\n/**\n * @deprecated Compatibility wrapper (pre-core-refactor export); delegates to\n * `omiDayWindow`. Returns the resolved start bound, not a rebuilt midnight.\n */\nexport function zonedDayStartIso(date: string, timezone: string): string {\n warnLegacyDayWindow(\"zonedDayStartIso\");\n return omiDayWindow(date, timezone).startIso;\n}\n\n/** @deprecated Compatibility wrapper (pre-core-refactor export); delegates to `omiDayWindow`. */\nexport function zonedDayBounds(\n date: string,\n timezone: string,\n): { startIso: string; endIso: string } {\n warnLegacyDayWindow(\"zonedDayBounds\");\n return omiDayWindow(date, timezone);\n}\n\nconst warnedLegacyDayWindow = new Set<string>();\n\nfunction warnLegacyDayWindow(name: \"zonedDayBounds\" | \"zonedDayStartIso\"): void {\n if (warnedLegacyDayWindow.has(name)) return;\n warnedLegacyDayWindow.add(name);\n process.emitWarning(\n `@remnic/connector-omi: ${name} is deprecated; use omiDayWindow instead.`,\n { type: \"DeprecationWarning\", code: `REMNIC_DEP_OMI_${name.toUpperCase()}` },\n );\n}\n\nexport function conversationToWearable(\n conversation: OmiConversation,\n): WearableConversation {\n const startedAtMs = conversation.started_at\n ? Date.parse(conversation.started_at)\n : Number.NaN;\n const segments: WearableTranscriptSegment[] = [];\n for (const segment of conversation.transcript_segments ?? []) {\n const text = typeof segment.text === \"string\" ? segment.text.trim() : \"\";\n if (text.length === 0) continue;\n const isWearer = segment.is_user === true;\n const personId =\n typeof segment.person_id === \"string\" && segment.person_id.length > 0\n ? segment.person_id\n : undefined;\n const speakerId =\n typeof segment.speaker_id === \"string\" || typeof segment.speaker_id === \"number\"\n ? String(segment.speaker_id)\n : undefined;\n const label =\n typeof segment.speaker_name === \"string\" && segment.speaker_name.trim().length > 0\n ? segment.speaker_name.trim()\n : typeof segment.speaker === \"string\" && segment.speaker.trim().length > 0\n ? segment.speaker.trim()\n : undefined;\n const startIso = segmentTimestamp(startedAtMs, segment.start);\n const endIso = segmentTimestamp(startedAtMs, segment.end);\n segments.push({\n text,\n // person_id is the most stable key when the user has tagged the\n // speaker as a known person in Omi. The Developer API's\n // speaker_id is the next best stable key, then the diarization\n // label.\n speakerKey: isWearer ? \"user\" : (personId ?? speakerId ?? label ?? \"unknown\"),\n ...(label !== undefined ? { speakerName: label } : {}),\n ...(isWearer ? { isWearer: true } : {}),\n ...(startIso !== undefined ? { startIso } : {}),\n ...(endIso !== undefined ? { endIso } : {}),\n });\n }\n\n const title = conversation.structured?.title?.trim();\n const overview = conversation.structured?.overview?.trim();\n const address = conversation.geolocation?.address;\n\n return {\n id: conversation.id,\n source: OMI_SOURCE_ID,\n ...(title && title.length > 0 ? { title } : {}),\n ...(overview && overview.length > 0 ? { summary: overview } : {}),\n startIso: conversation.started_at ?? conversation.created_at ?? \"\",\n ...(typeof conversation.finished_at === \"string\"\n ? { endIso: conversation.finished_at }\n : {}),\n ...(typeof address === \"string\" && address.length > 0\n ? { location: address }\n : {}),\n segments,\n };\n}\n\nfunction segmentTimestamp(startedAtMs: number, offset: number | undefined): string | undefined {\n if (typeof offset !== \"number\") return undefined;\n const instant = new Date(startedAtMs + offset * 1_000);\n // Invalid/out-of-range timing must not discard the segment's usable text.\n return Number.isNaN(instant.getTime()) ? undefined : instant.toISOString();\n}\n\nexport function memoryToNativeMemory(memory: OmiMemory): WearableNativeMemory | null {\n const content = typeof memory.content === \"string\" ? memory.content.trim() : \"\";\n if (content.length === 0) return null;\n return {\n id: memory.id,\n content,\n ...(memory.created_at !== undefined ? { createdIso: memory.created_at } : {}),\n ...(Array.isArray(memory.tags) && memory.tags.length > 0\n ? { tags: memory.tags }\n : {}),\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remnic/connector-omi",
3
- "version": "10.0.21",
3
+ "version": "10.0.23",
4
4
  "description": "Omi AI wearable connector for Remnic — pull, clean, and remember necklace transcripts via the Omi Developer API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -19,13 +19,13 @@
19
19
  "provenance": true
20
20
  },
21
21
  "peerDependencies": {
22
- "@remnic/core": "^10.0.21"
22
+ "@remnic/core": "^10.0.23"
23
23
  },
24
24
  "devDependencies": {
25
25
  "tsup": "^8.0.0",
26
26
  "typescript": "^5.7.0",
27
27
  "tsx": "^4.0.0",
28
- "@remnic/core": "10.0.21"
28
+ "@remnic/core": "10.0.23"
29
29
  },
30
30
  "license": "MIT",
31
31
  "repository": {