@codemem/core 0.20.3 → 0.20.4

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["#matchResult","#validatedData","#getDecodedParam","#getAllDecodedParams","#getParamValue","#cachedBody","#rawRequest","#executionCtx","#notFoundHandler","#path","#matchResult","#req","#res","#preparedHeaders","#renderer","#layout","#status","#var","#newResponse","Hono","#path","#addRoute","#notFoundHandler","#clone","#dispatch","#handleError","Node","#index","#children","#varIndex","Node","#root","#context","#middleware","#routes","#buildMatcher","#routers","#routes","#children","#methods","#patterns","#order","#params","#pushHandlerSets","#node","HonoBase"],"sources":["../src/api-types.ts","../src/claude-hooks.ts","../src/coordinator-invites.ts","../src/coordinator-store.ts","../src/observer-config.ts","../src/db.ts","../src/ingest-filters.ts","../src/project.ts","../src/schema.ts","../src/maintenance.ts","../src/sync-http-client.ts","../src/sync-identity.ts","../src/coordinator-actions.ts","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/compose.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/request/constants.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/body.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/url.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/request.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/html.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/context.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/constants.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/hono-base.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/matcher.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/node.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/trie.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/smart-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/trie-router/node.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/trie-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/hono.js","../src/sync-auth.ts","../src/coordinator-api.ts","../src/address-utils.ts","../src/coordinator-runtime.ts","../src/embeddings.ts","../src/export-import.ts","../src/filters.ts","../src/ingest-sanitize.ts","../src/ingest-events.ts","../src/ingest-prompts.ts","../src/ingest-transcript.ts","../src/ingest-xml-parser.ts","../src/tags.ts","../src/vectors.ts","../src/ingest-pipeline.ts","../src/integers.ts","../src/observer-auth.ts","../src/observer-client.ts","../src/search.ts","../src/pack.ts","../src/raw-event-flush.ts","../src/raw-event-sweeper.ts","../src/sync-replication.ts","../src/store.ts","../src/sync-discovery.ts","../src/sync-pass.ts","../src/sync-daemon.ts","../src/test-schema.generated.ts","../src/test-utils.ts","../src/index.ts"],"sourcesContent":["/**\n * Shared HTTP API response types for the codemem viewer endpoints.\n *\n * These types define the JSON contract between the Python viewer backend\n * (codemem/viewer_routes/) and the frontend (viewer_ui/src/lib/api.ts).\n *\n * ⚠️ These types are manually transcribed from Python viewer route handlers.\n * There is no automated schema validation between Python and TypeScript.\n * When modifying Python routes, update these types and add integration tests.\n *\n * Import existing store/entity types where shapes match.\n */\n\nimport type { Actor, MemoryItemResponse, PackResponse, Session, StoreStats } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n/** Standard pagination envelope returned by list endpoints. */\nexport interface ApiPagination {\n\tlimit: number;\n\toffset: number;\n\tnext_offset: number | null;\n\thas_more: boolean;\n}\n\n/** Common error shape returned by all endpoints on failure. */\nexport interface ApiErrorResponse {\n\terror: string;\n\tdetail?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Core viewer API responses — stats.py\n// ---------------------------------------------------------------------------\n\n/**\n * GET /api/stats\n *\n * Delegates directly to store.stats(); shape matches StoreStats.\n */\nexport type ApiStatsResponse = StoreStats;\n\n/** Single usage event summary row. */\nexport interface ApiUsageEventSummary {\n\tevent: string;\n\ttotal_tokens_read: number;\n\ttotal_tokens_written: number;\n\ttotal_tokens_saved: number;\n\tcount: number;\n}\n\n/** Usage totals row. */\nexport interface ApiUsageTotals {\n\ttokens_read: number;\n\ttokens_written: number;\n\ttokens_saved: number;\n\tcount: number;\n}\n\n/** Recent pack event row. */\nexport interface ApiRecentPackEvent {\n\tid: number;\n\tsession_id: number | null;\n\tevent: string;\n\ttokens_read: number;\n\ttokens_written: number;\n\ttokens_saved: number;\n\tcreated_at: string;\n\tmetadata_json: Record<string, unknown> | null;\n}\n\n/**\n * GET /api/usage\n *\n * Returns usage breakdown, optionally filtered by project.\n */\nexport interface ApiUsageResponse {\n\tproject: string | null;\n\tevents: ApiUsageEventSummary[];\n\ttotals: ApiUsageTotals;\n\tevents_global: ApiUsageEventSummary[];\n\ttotals_global: ApiUsageTotals;\n\tevents_filtered: ApiUsageEventSummary[] | null;\n\ttotals_filtered: ApiUsageTotals | null;\n\trecent_packs: ApiRecentPackEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Core viewer API responses — memory.py\n// ---------------------------------------------------------------------------\n\n/** Extended memory item with session + ownership fields attached by the viewer. */\nexport interface ApiMemoryItem extends MemoryItemResponse {\n\tproject?: string;\n\tcwd?: string;\n\towned_by_self?: boolean;\n}\n\n/**\n * GET /api/sessions\n *\n * Returns recent sessions with parsed metadata.\n */\nexport interface ApiSessionsResponse {\n\titems: (Session & { metadata_json: Record<string, unknown> | null })[];\n}\n\n/**\n * GET /api/projects\n *\n * Returns deduplicated, sorted project names.\n */\nexport interface ApiProjectsResponse {\n\tprojects: string[];\n}\n\n/**\n * GET /api/observations (also GET /api/memories — aliased)\n * GET /api/summaries\n *\n * Paginated memory items with session/ownership fields attached.\n */\nexport interface ApiMemoryListResponse {\n\titems: ApiMemoryItem[];\n\tpagination: ApiPagination;\n}\n\n/**\n * GET /api/session\n *\n * Aggregate counts for a project (or global).\n */\nexport interface ApiSessionCountsResponse {\n\ttotal: number;\n\tmemories: number;\n\tartifacts: number;\n\tprompts: number;\n\tobservations: number;\n}\n\n/**\n * GET /api/pack\n *\n * Delegates directly to store.build_memory_pack(); shape matches PackResponse.\n */\nexport type ApiPackResponse = PackResponse;\n\n/**\n * GET /api/memory\n *\n * Returns a list of recent memories, optionally filtered by kind/project/scope.\n */\nexport interface ApiMemoryResponse {\n\titems: ApiMemoryItem[];\n}\n\n/**\n * GET /api/artifacts\n *\n * Returns artifacts for a given session.\n */\nexport interface ApiArtifactsResponse {\n\titems: Record<string, unknown>[];\n}\n\n/**\n * POST /api/memories/visibility — request body.\n */\nexport interface ApiUpdateVisibilityRequest {\n\tmemory_id: number;\n\tvisibility: \"private\" | \"shared\";\n}\n\n/**\n * POST /api/memories/visibility — response.\n */\nexport interface ApiUpdateVisibilityResponse {\n\titem: ApiMemoryItem;\n}\n\n// ---------------------------------------------------------------------------\n// Observer status — observer_status.py\n// ---------------------------------------------------------------------------\n\n/** Active observer runtime status (from observer.get_status()). */\nexport interface ApiObserverActiveStatus {\n\tprovider: string | null;\n\tmodel: string | null;\n\truntime: string | null;\n\tauth: string | null;\n\tlast_error?: string | null;\n}\n\n/** Per-provider credential probe result. */\nexport interface ApiProviderCredential {\n\toauth: boolean;\n\tapi_key: boolean;\n\tsource: string | null;\n}\n\n/** Credential availability — provider-keyed map (from probe_available_credentials()). */\nexport type ApiAvailableCredentials = Record<string, ApiProviderCredential>;\n\n/** Latest flush failure with impact annotation. */\nexport interface ApiFlushFailure {\n\tid: number;\n\tsource: string;\n\tstream_id: string;\n\topencode_session_id: string;\n\tstatus: string;\n\terror_message: string | null;\n\terror_type: string | null;\n\timpact: string | null;\n}\n\n/** Queue status within observer-status. */\nexport interface ApiObserverQueue {\n\tpending: number;\n\tsessions: number;\n\tauth_backoff_active: boolean;\n\tauth_backoff_remaining_s: number;\n}\n\n/**\n * GET /api/observer-status\n */\nexport interface ApiObserverStatusResponse {\n\tactive: ApiObserverActiveStatus | null;\n\tavailable_credentials: ApiAvailableCredentials;\n\tlatest_failure: ApiFlushFailure | null;\n\tqueue: ApiObserverQueue;\n}\n\n// ---------------------------------------------------------------------------\n// Config — config.py\n// ---------------------------------------------------------------------------\n\n/**\n * GET /api/config\n */\nexport interface ApiConfigGetResponse {\n\tpath: string;\n\tconfig: Record<string, unknown>;\n\tdefaults: Record<string, unknown>;\n\teffective: Record<string, unknown>;\n\tenv_overrides: Record<string, string>;\n\tproviders: string[];\n}\n\n/** Manual action suggestion in config save effects. */\nexport interface ApiConfigManualAction {\n\tkind: string;\n\tcommand: string;\n\tlabel: string;\n\treason: string;\n}\n\n/** Sync effect detail in config save response. */\nexport interface ApiConfigSyncEffect {\n\taffected_keys: string[];\n\taction: string | null;\n\treason: string | null;\n\tattempted: boolean;\n\tok: boolean | null;\n\tmessage: string | null;\n\tmanual_action: ApiConfigManualAction | null;\n}\n\n/** Effects block in config save response. */\nexport interface ApiConfigEffects {\n\tsaved_keys: string[];\n\teffective_keys: string[];\n\thot_reloaded_keys: string[];\n\tlive_applied_keys: string[];\n\trestart_required_keys: string[];\n\tignored_by_env_keys: string[];\n\twarnings: string[];\n\tsync: ApiConfigSyncEffect;\n\tmanual_actions: ApiConfigManualAction[];\n}\n\n/**\n * POST /api/config — request body.\n * Accepts a direct updates object, or wrapped as { config: {...} }.\n * Python unwraps: `updates = payload.get(\"config\") if \"config\" in payload else payload`\n */\nexport type ApiConfigSaveRequest = Record<string, unknown>;\n\n/**\n * POST /api/config — response.\n */\nexport interface ApiConfigSaveResponse {\n\tpath: string;\n\tconfig: Record<string, unknown>;\n\teffective: Record<string, unknown>;\n\teffects: ApiConfigEffects;\n}\n\n// ---------------------------------------------------------------------------\n// Raw events — raw_events.py\n// ---------------------------------------------------------------------------\n\n/**\n * Raw event session backlog item (from store.raw_event_backlog() + _with_session_aliases).\n * Fields vary by query — max_seq/pending come from the backlog query,\n * last_received_event_seq/updated_at may be absent.\n */\nexport interface ApiRawEventBacklogItem {\n\tstream_id: string;\n\topencode_session_id: string;\n\tsession_stream_id?: string;\n\tsession_id?: string;\n\tcwd?: string | null;\n\tproject?: string | null;\n\tstarted_at?: string | null;\n\tmax_seq?: number;\n\tpending?: number;\n\tlast_seen_ts_wall_ms?: number | null;\n\tlast_received_event_seq?: number;\n\tlast_flushed_event_seq?: number;\n\tupdated_at?: string;\n}\n\n/** Raw event backlog totals. */\nexport interface ApiRawEventBacklogTotals {\n\tpending: number;\n\tsessions: number;\n}\n\n/** Ingest capability metadata. */\nexport interface ApiRawEventIngestInfo {\n\tavailable: boolean;\n\tmode: string;\n\tmax_body_bytes: number;\n}\n\n/**\n * GET /api/raw-events\n *\n * Returns backlog totals directly (compat endpoint for stats panel).\n */\nexport type ApiRawEventsResponse = ApiRawEventBacklogTotals;\n\n/**\n * GET /api/raw-events/status\n */\nexport interface ApiRawEventsStatusResponse {\n\titems: ApiRawEventBacklogItem[];\n\ttotals: ApiRawEventBacklogTotals;\n\tingest: ApiRawEventIngestInfo;\n}\n\n/**\n * POST /api/raw-events — response.\n */\nexport interface ApiRawEventsPostResponse {\n\tinserted: number;\n\treceived: number;\n}\n\n/**\n * POST /api/claude-hooks — response.\n */\nexport interface ApiClaudeHooksPostResponse {\n\tinserted: number;\n\tskipped: number;\n}\n\n// ---------------------------------------------------------------------------\n// Sync — sync.py\n// ---------------------------------------------------------------------------\n\n/** Project scope filter for a peer. */\nexport interface ApiProjectScope {\n\tinclude: string[];\n\texclude: string[];\n\teffective_include: string[];\n\teffective_exclude: string[];\n\tinherits_global: boolean;\n}\n\n/** Peer status breakdown. */\nexport interface ApiPeerStatus {\n\tsync_status: \"ok\" | \"error\" | \"stale\" | \"unknown\";\n\tping_status: \"ok\" | \"stale\" | \"unknown\";\n\tpeer_state: \"online\" | \"offline\" | \"degraded\" | \"stale\" | \"unknown\";\n\tfresh: boolean;\n\tlast_sync_at: string | null;\n\tlast_ping_at: string | null;\n}\n\n/** Peer item in sync status/peers responses. */\nexport interface ApiSyncPeerItem {\n\tpeer_device_id: string;\n\tname: string | null;\n\tfingerprint: string | null;\n\tpinned: boolean;\n\taddresses: string[];\n\tlast_seen_at: string | null;\n\tlast_sync_at: string | null;\n\tlast_error: string | null;\n\thas_error: boolean;\n\tclaimed_local_actor: boolean;\n\tactor_id: string | null;\n\tactor_display_name: string | null;\n\tproject_scope: ApiProjectScope;\n\tstatus?: ApiPeerStatus;\n}\n\n/** Sync attempt item. */\n/** Raw sync attempt row — returned by /api/sync/attempts. */\nexport interface ApiSyncAttemptItem {\n\tpeer_device_id: string;\n\tok: number;\n\terror: string | null;\n\tstarted_at: string;\n\tfinished_at: string | null;\n\tops_in: number;\n\tops_out: number;\n}\n\n/** Enriched sync attempt — embedded in /api/sync/status with extra fields. */\nexport interface ApiSyncAttemptItemEnriched extends ApiSyncAttemptItem {\n\tstatus: string;\n\taddress: string | null;\n}\n\nexport interface ApiSharingReviewItem {\n\tpeer_device_id: string;\n\tpeer_name: string;\n\tactor_id: string;\n\tactor_display_name: string;\n\tproject: string | null;\n\tscope_label: string;\n\tshareable_count: number;\n\tprivate_count: number;\n\ttotal_count: number;\n}\n\nexport interface ApiLegacyDeviceItem {\n\torigin_device_id: string;\n\tmemory_count: number;\n\tlast_seen_at: string | null;\n}\n\n/**\n * Coordinator status snapshot (from coordinator.status_snapshot()).\n * Shape varies by state — fields are optional to cover enabled/disabled modes.\n */\nexport interface ApiCoordinatorStatus {\n\tenabled: boolean;\n\tconfigured: boolean;\n\tcoordinator_url?: string | null;\n\tgroups?: string[];\n\tgroup_id?: string | null;\n\tlast_sync_at?: string | null;\n\tlast_error?: string | null;\n}\n\n/** Join request item. */\nexport interface ApiJoinRequest {\n\trequest_id: string;\n\tdevice_id: string;\n\tfingerprint: string;\n\tstatus: string;\n}\n\n/** Status block nested in sync status response. */\nexport interface ApiSyncStatusBlock {\n\tenabled: boolean;\n\tinterval_s: number;\n\tpeer_count: number;\n\tlast_sync_at: string | null;\n\tdaemon_state: \"ok\" | \"disabled\" | \"error\" | \"stopped\" | \"degraded\" | \"offline-peers\" | \"stale\";\n\tdaemon_running: boolean;\n\tdaemon_detail: string | null;\n\tproject_filter_active: boolean;\n\tproject_filter: { include: string[]; exclude: string[] };\n\tredacted: boolean;\n\tpeers: Record<string, ApiPeerStatus>;\n\tpending: number;\n\tsync: Record<string, unknown>;\n\tping: Record<string, unknown>;\n}\n\n/**\n * GET /api/sync/status\n */\nexport interface ApiSyncStatusResponse {\n\t/* Top-level status fields */\n\tenabled: boolean;\n\tinterval_s: number;\n\tpeer_count: number;\n\tlast_sync_at: string | null;\n\tdaemon_state: \"ok\" | \"disabled\" | \"error\" | \"stopped\" | \"degraded\" | \"offline-peers\" | \"stale\";\n\tdaemon_running: boolean;\n\tdaemon_detail: string | null;\n\tproject_filter_active: boolean;\n\tproject_filter: { include: string[]; exclude: string[] };\n\tredacted: boolean;\n\t/* Diagnostics (present when includeDiagnostics=1) */\n\tdevice_id?: string | null;\n\tfingerprint?: string | null;\n\tbind?: string;\n\tdaemon_last_error?: string | null;\n\tdaemon_last_error_at?: string | null;\n\tdaemon_last_ok_at?: string | null;\n\t/* Nested collections */\n\tstatus: ApiSyncStatusBlock;\n\tpeers: ApiSyncPeerItem[];\n\tattempts: ApiSyncAttemptItemEnriched[];\n\tlegacy_devices: ApiLegacyDeviceItem[];\n\tsharing_review: ApiSharingReviewItem[];\n\tcoordinator: ApiCoordinatorStatus;\n\tjoin_requests: ApiJoinRequest[];\n}\n\n/**\n * GET /api/sync/peers\n */\nexport interface ApiSyncPeersResponse {\n\titems: ApiSyncPeerItem[];\n\tredacted: boolean;\n}\n\n/**\n * GET /api/sync/actors\n */\nexport interface ApiSyncActorsResponse {\n\titems: Actor[];\n}\n\n/**\n * GET /api/sync/attempts\n */\nexport interface ApiSyncAttemptsResponse {\n\titems: ApiSyncAttemptItem[];\n}\n\n/**\n * GET /api/sync/pairing\n */\nexport interface ApiSyncPairingResponse {\n\tdevice_id?: string;\n\tfingerprint?: string;\n\tpublic_key?: string;\n\tpairing_filter_hint: string;\n\taddresses?: string[];\n\tredacted?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Sync mutations — request/response types\n// ---------------------------------------------------------------------------\n\n/** POST /api/sync/actors — request. */\nexport interface ApiCreateActorRequest {\n\tdisplay_name: string;\n\tactor_id?: string | null;\n}\n\n/**\n * POST /api/sync/actors — response.\n *\n * Returns the full Actor row from store.create_actor().\n */\nexport type ApiCreateActorResponse = Actor;\n\n/** POST /api/sync/actors/rename — request. */\nexport interface ApiRenameActorRequest {\n\tactor_id: string;\n\tdisplay_name: string;\n}\n\n/**\n * POST /api/sync/actors/rename — response.\n *\n * Returns the full Actor row from store.rename_actor().\n */\nexport type ApiRenameActorResponse = Actor;\n\n/** POST /api/sync/actors/merge — request. */\nexport interface ApiMergeActorRequest {\n\tprimary_actor_id: string;\n\tsecondary_actor_id: string;\n}\n\n/** POST /api/sync/actors/merge — response. */\nexport interface ApiMergeActorResponse {\n\tmerged_count: number;\n}\n\n/** POST /api/sync/peers/rename — request. */\nexport interface ApiRenamePeerRequest {\n\tpeer_device_id: string;\n\tname: string;\n}\n\n/** POST /api/sync/peers/rename — response. */\nexport interface ApiOkResponse {\n\tok: true;\n}\n\n/** POST /api/sync/peers/scope — request. */\nexport interface ApiPeerScopeRequest {\n\tpeer_device_id: string;\n\tinclude?: string[] | null;\n\texclude?: string[] | null;\n\tinherit_global?: boolean;\n}\n\n/** POST /api/sync/peers/scope — response. */\nexport interface ApiPeerScopeResponse {\n\tok: true;\n\tproject_scope: ApiProjectScope;\n}\n\n/** POST /api/sync/peers/identity — request. */\nexport interface ApiPeerIdentityRequest {\n\tpeer_device_id: string;\n\tactor_id?: string | null;\n\tclaimed_local_actor?: boolean;\n}\n\n/** POST /api/sync/peers/identity — response. */\nexport interface ApiPeerIdentityResponse {\n\tok: true;\n\tactor_id: string | null;\n\tclaimed_local_actor: boolean;\n}\n\n/** POST /api/sync/legacy-devices/claim — request. */\nexport interface ApiClaimLegacyDeviceRequest {\n\torigin_device_id: string;\n}\n\n/** POST /api/sync/legacy-devices/claim — response. */\nexport interface ApiClaimLegacyDeviceResponse {\n\tok: true;\n\torigin_device_id: string;\n\tupdated: number;\n}\n\n/** POST /api/sync/invites/create — request. */\nexport interface ApiCreateInviteRequest {\n\tgroup_id: string;\n\tcoordinator_url?: string | null;\n\tpolicy?: \"auto_admit\" | \"approval_required\";\n\tttl_hours?: number;\n}\n\n/** POST /api/sync/invites/create — response. */\nexport interface ApiCreateInviteResponse extends ApiOkResponse {\n\tinvite: string;\n}\n\n/** POST /api/sync/invites/import — request. */\nexport interface ApiImportInviteRequest {\n\tinvite: string;\n}\n\n/** POST /api/sync/invites/import — response. */\nexport interface ApiImportInviteResponse extends ApiOkResponse {\n\tgroup_id: string;\n}\n\n/** POST /api/sync/join-requests/review — request. */\nexport interface ApiReviewJoinRequestRequest {\n\trequest_id: string;\n\taction: \"approve\" | \"deny\";\n}\n\n/** POST /api/sync/join-requests/review — response. */\nexport interface ApiReviewJoinRequestResponse {\n\tok: true;\n\trequest: ApiJoinRequest;\n}\n\n/** DELETE /api/sync/peers/:peer_device_id — response. */\nexport type ApiDeletePeerResponse = ApiOkResponse;\n\n/** POST /api/sync/actions/sync-now — request. */\nexport interface ApiSyncNowRequest {\n\tpeer_device_id?: string;\n\taddress?: string;\n}\n\n/** POST /api/sync/actions/sync-now — response. */\nexport interface ApiSyncNowResponse {\n\titems: Record<string, unknown>[];\n}\n","/**\n * Claude hook payload mapping.\n *\n * Ports codemem/claude_hooks.py — normalizes raw Claude Code hook payloads\n * (PreToolUse, PostToolUse, Stop, etc.) into raw event envelopes suitable\n * for the raw event sweeper pipeline.\n *\n * Entry points:\n * mapClaudeHookPayload(payload) → adapter event or null\n * buildRawEventEnvelopeFromHook(payload) → raw event envelope or null\n * buildIngestPayloadFromHook(payload) → ingest payload or null\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, resolve } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** Expand `~/...` paths like Python's `Path(...).expanduser()`. */\nfunction expandUser(value: string): string {\n\treturn value.startsWith(\"~/\") ? resolve(homedir(), value.slice(2)) : value;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const MAPPABLE_CLAUDE_HOOK_EVENTS = new Set([\n\t\"SessionStart\",\n\t\"UserPromptSubmit\",\n\t\"PreToolUse\",\n\t\"PostToolUse\",\n\t\"PostToolUseFailure\",\n\t\"Stop\",\n\t\"SessionEnd\",\n]);\n\n// ---------------------------------------------------------------------------\n// Timestamp helpers\n// ---------------------------------------------------------------------------\n\nfunction nowIso(): string {\n\treturn new Date()\n\t\t.toISOString()\n\t\t.replace(\"+00:00\", \"\")\n\t\t.replace(/\\.(\\d{3})\\d*Z$/, \".$1Z\");\n}\n\n/**\n * Normalize an ISO timestamp string, returning null if invalid.\n *\n * Matches Python's `datetime.isoformat().replace(\"+00:00\", \"Z\")`:\n * - No fractional seconds if the input has none → \"2026-03-04T01:00:00Z\"\n * - Preserves fractional seconds when present → \"2026-03-04T01:00:00.123000Z\"\n *\n * JS `Date.toISOString()` always outputs \".000Z\" which would produce different\n * sha256 event IDs than Python during the migration crossover period.\n */\nfunction normalizeIsoTs(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst text = value.trim();\n\tif (!text) return null;\n\ttry {\n\t\t// Python: datetime.fromisoformat(text.replace(\"Z\", \"+00:00\"))\n\t\t// then: if parsed.tzinfo is None: parsed = parsed.replace(tzinfo=UTC)\n\t\t//\n\t\t// JS `new Date(\"2026-03-04T01:00:00\")` treats naive timestamps as LOCAL time,\n\t\t// but Python treats them as UTC. Detect naive timestamps and append Z.\n\t\tconst hasTimezone =\n\t\t\t/[Zz]$/.test(text) || /[+-]\\d{2}:\\d{2}$/.test(text) || /[+-]\\d{4}$/.test(text);\n\t\tconst parseText = hasTimezone ? text : `${text}Z`;\n\n\t\tconst d = new Date(parseText);\n\t\tif (Number.isNaN(d.getTime())) return null;\n\n\t\t// Detect whether the input has fractional seconds\n\t\t// Strip timezone suffix first, then check for a dot before \"Z\"/\"+\"/\"-\"\n\t\tconst hasFractional = /\\.\\d+([Zz+-]|$)/.test(text);\n\t\tif (!hasFractional) {\n\t\t\t// No fractional seconds — produce \"YYYY-MM-DDTHH:MM:SSZ\" (matches Python)\n\t\t\treturn d.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n\t\t}\n\t\t// Has fractional seconds — produce microsecond precision like Python (6 digits)\n\t\tconst iso = d.toISOString(); // \"YYYY-MM-DDTHH:MM:SS.mmmZ\"\n\t\t// Pad from 3 digits to 6 to match Python's microsecond output\n\t\treturn iso.replace(/\\.(\\d{3})Z$/, \".$1000Z\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Parse an ISO timestamp to wall-clock milliseconds. */\nfunction isoToWallMs(value: string): number {\n\treturn new Date(value).getTime();\n}\n\n// ---------------------------------------------------------------------------\n// Stable event id\n// ---------------------------------------------------------------------------\n\nfunction stableEventId(...parts: string[]): string {\n\tconst joined = parts.join(\"|\");\n\tconst digest = createHash(\"sha256\").update(joined, \"utf-8\").digest(\"hex\").slice(0, 24);\n\treturn `cld_evt_${digest}`;\n}\n\n// ---------------------------------------------------------------------------\n// Project inference (mirrors Python's _infer_project_from_cwd /\n// _resolve_hook_project / _resolve_hook_project_from_payload_paths)\n// ---------------------------------------------------------------------------\n\n/** Normalize a raw label value to a plain project name (basename if path). */\nexport function normalizeProjectLabel(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst cleaned = value.trim().replace(/[\\\\/]+$/, \"\");\n\tif (!cleaned) return null;\n\tif (cleaned.includes(\"/\") || cleaned.includes(\"\\\\\")) {\n\t\t// Windows-style path (drive letter or backslash)\n\t\tconst isWindows =\n\t\t\tcleaned.includes(\"\\\\\") ||\n\t\t\t(cleaned.length >= 2 && cleaned[1] === \":\" && /[a-zA-Z]/.test(cleaned[0] ?? \"\"));\n\t\tif (isWindows) {\n\t\t\tconst parts = cleaned.replaceAll(\"\\\\\", \"/\").split(\"/\");\n\t\t\treturn parts[parts.length - 1] || null;\n\t\t}\n\t\tconst parts = cleaned.split(\"/\");\n\t\treturn parts[parts.length - 1] || null;\n\t}\n\treturn cleaned;\n}\n\n/**\n * Walk up from `cwd` looking for a .git marker, then return the basename of\n * that directory (or the cwd basename if no git root found).\n * Returns null if cwd is not an absolute, existing directory.\n */\nfunction inferProjectFromCwd(cwd: string | null): string | null {\n\tif (typeof cwd !== \"string\" || !cwd.trim()) return null;\n\tconst text = expandUser(cwd.trim());\n\tif (!isAbsolute(text)) return null;\n\ttry {\n\t\tconst stat = statSync(text, { throwIfNoEntry: false });\n\t\tif (!stat?.isDirectory()) return null;\n\t} catch {\n\t\treturn null;\n\t}\n\n\tlet current = text;\n\twhile (true) {\n\t\tconst gitPath = resolve(current, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\treturn basename(current) || null;\n\t\t}\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) break;\n\t\tcurrent = parent;\n\t}\n\treturn basename(text) || null;\n}\n\n/**\n * Infer project from a file path hint (e.g. a tool input `filePath`).\n * Walks up from the file's directory.\n */\nfunction inferProjectFromPathHint(pathHint: unknown, cwdHint?: string | null): string | null {\n\tif (typeof pathHint !== \"string\" || !pathHint.trim()) return null;\n\tconst text = expandUser(pathHint.trim());\n\n\tlet candidate: string;\n\tif (isAbsolute(text)) {\n\t\tcandidate = text;\n\t} else {\n\t\tif (typeof cwdHint !== \"string\" || !cwdHint.trim()) return null;\n\t\tconst base = expandUser(cwdHint.trim());\n\t\tif (!isAbsolute(base)) return null;\n\t\ttry {\n\t\t\tconst stat = statSync(base, { throwIfNoEntry: false });\n\t\t\tif (!stat?.isDirectory()) return null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tcandidate = resolve(base, text);\n\t}\n\n\t// Determine starting dir: if candidate is a dir use it, else use parent\n\tlet start: string;\n\ttry {\n\t\tconst stat = statSync(candidate, { throwIfNoEntry: false });\n\t\tstart = stat?.isDirectory() ? candidate : dirname(candidate);\n\t} catch {\n\t\tstart = dirname(candidate);\n\t}\n\n\t// Walk up to find an existing directory\n\tlet current = start;\n\twhile (!existsSync(current)) {\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return null;\n\t\tcurrent = parent;\n\t}\n\n\treturn inferProjectFromCwd(current);\n}\n\n/**\n * Resolve the project for a hook payload.\n * Priority: CODEMEM_PROJECT env → cwd git root → payload project label.\n */\nexport function resolveHookProject(cwd: string | null, payloadProject: unknown): string | null {\n\tconst envProject = normalizeProjectLabel(process.env.CODEMEM_PROJECT);\n\tif (envProject) return envProject;\n\n\tconst payloadLabel = normalizeProjectLabel(payloadProject);\n\tconst cwdLabel = inferProjectFromCwd(cwd);\n\n\tif (cwdLabel) {\n\t\t// If payload label matches cwd label exactly, prefer payload (avoids ambiguity)\n\t\tif (payloadLabel && payloadLabel === cwdLabel) return payloadLabel;\n\t\treturn cwdLabel;\n\t}\n\treturn payloadLabel ?? null;\n}\n\n/**\n * Try to infer project from tool_input paths or transcript_path in a hook payload.\n */\nfunction resolveHookProjectFromPayloadPaths(hookPayload: Record<string, unknown>): string | null {\n\tconst cwdHint = typeof hookPayload.cwd === \"string\" ? hookPayload.cwd : null;\n\tconst toolInput = hookPayload.tool_input;\n\tif (toolInput != null && typeof toolInput === \"object\" && !Array.isArray(toolInput)) {\n\t\tconst ti = toolInput as Record<string, unknown>;\n\t\tfor (const key of [\"filePath\", \"file_path\", \"path\"]) {\n\t\t\tconst project = inferProjectFromPathHint(ti[key], cwdHint);\n\t\t\tif (project) return project;\n\t\t}\n\t}\n\tconst project = inferProjectFromPathHint(hookPayload.transcript_path, cwdHint);\n\tif (project) return project;\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Usage normalization\n// ---------------------------------------------------------------------------\n\nfunction normalizeUsage(value: unknown): Record<string, number> | null {\n\tif (value == null || typeof value !== \"object\" || Array.isArray(value)) return null;\n\tconst v = value as Record<string, unknown>;\n\n\t// Matches Python: int(value.get(key) or 0) — no clamping to >= 0\n\tconst toInt = (key: string): number => {\n\t\ttry {\n\t\t\tconst n = Number(v[key] ?? 0);\n\t\t\treturn Number.isFinite(n) ? Math.trunc(n) : 0;\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t};\n\n\tconst normalized = {\n\t\tinput_tokens: toInt(\"input_tokens\"),\n\t\toutput_tokens: toInt(\"output_tokens\"),\n\t\tcache_creation_input_tokens: toInt(\"cache_creation_input_tokens\"),\n\t\tcache_read_input_tokens: toInt(\"cache_read_input_tokens\"),\n\t};\n\tconst total = Object.values(normalized).reduce((a, b) => a + b, 0);\n\treturn total > 0 ? normalized : null;\n}\n\n// ---------------------------------------------------------------------------\n// Text extraction from content blocks\n// ---------------------------------------------------------------------------\n\nfunction textFromContent(value: unknown): string {\n\tif (typeof value === \"string\") return value.trim();\n\tif (Array.isArray(value)) {\n\t\tconst parts = value.map(textFromContent).filter(Boolean);\n\t\treturn parts.join(\"\\n\").trim();\n\t}\n\tif (value != null && typeof value === \"object\") {\n\t\tconst v = value as Record<string, unknown>;\n\t\tif (typeof v.text === \"string\") return v.text.trim();\n\t\treturn textFromContent(v.content);\n\t}\n\treturn \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Transcript extraction (for Stop events without last_assistant_message)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the transcript JSONL and return the last assistant message text + usage.\n * Returns [null, null] on any read or parse failure.\n */\nfunction extractFromTranscript(\n\ttranscriptPath: unknown,\n\tcwdHint?: string | null,\n): [string | null, Record<string, number> | null] {\n\tif (typeof transcriptPath !== \"string\") return [null, null];\n\tconst raw = expandUser(transcriptPath.trim());\n\tif (!raw) return [null, null];\n\n\tlet resolvedPath: string;\n\tif (isAbsolute(raw)) {\n\t\tresolvedPath = raw;\n\t} else {\n\t\tif (typeof cwdHint !== \"string\" || !cwdHint.trim()) return [null, null];\n\t\tconst base = expandUser(cwdHint.trim());\n\t\tif (!isAbsolute(base)) return [null, null];\n\t\ttry {\n\t\t\tconst stat = statSync(base, { throwIfNoEntry: false });\n\t\t\tif (!stat?.isDirectory()) return [null, null];\n\t\t} catch {\n\t\t\treturn [null, null];\n\t\t}\n\t\tresolvedPath = resolve(base, raw);\n\t}\n\n\ttry {\n\t\tconst stat = statSync(resolvedPath, { throwIfNoEntry: false });\n\t\tif (!stat?.isFile()) return [null, null];\n\t} catch {\n\t\treturn [null, null];\n\t}\n\n\tlet assistantText: string | null = null;\n\tlet assistantUsage: Record<string, number> | null = null;\n\n\ttry {\n\t\tconst content = readFileSync(resolvedPath, \"utf-8\");\n\t\tfor (const rawLine of content.split(\"\\n\")) {\n\t\t\tconst line = rawLine.trim();\n\t\t\tif (!line) continue;\n\t\t\tlet record: unknown;\n\t\t\ttry {\n\t\t\t\trecord = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (record == null || typeof record !== \"object\" || Array.isArray(record)) continue;\n\t\t\tconst r = record as Record<string, unknown>;\n\n\t\t\t// A line may be the record itself or have a nested `message` field\n\t\t\tconst candidates: Record<string, unknown>[] = [r];\n\t\t\tif (r.message != null && typeof r.message === \"object\" && !Array.isArray(r.message)) {\n\t\t\t\tcandidates.push(r.message as Record<string, unknown>);\n\t\t\t}\n\n\t\t\tlet role = \"\";\n\t\t\tlet contentValue: unknown = null;\n\t\t\tlet usageValue: unknown = null;\n\n\t\t\tfor (const c of candidates) {\n\t\t\t\tif (!role) {\n\t\t\t\t\tif (typeof c.role === \"string\") role = c.role.trim().toLowerCase();\n\t\t\t\t\telse if (c.type === \"assistant\") role = \"assistant\";\n\t\t\t\t}\n\t\t\t\tif (contentValue == null) {\n\t\t\t\t\tfor (const field of [\"content\", \"text\"]) {\n\t\t\t\t\t\tif (field in c) {\n\t\t\t\t\t\t\tcontentValue = c[field];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (usageValue == null) {\n\t\t\t\t\tfor (const field of [\"usage\", \"token_usage\", \"tokenUsage\"]) {\n\t\t\t\t\t\tif (field in c) {\n\t\t\t\t\t\t\tusageValue = c[field];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (role !== \"assistant\") continue;\n\t\t\tconst text = textFromContent(contentValue);\n\t\t\tif (!text) continue;\n\t\t\tassistantText = text;\n\t\t\tassistantUsage = normalizeUsage(usageValue);\n\t\t}\n\t} catch {\n\t\treturn [null, null];\n\t}\n\n\treturn [assistantText, assistantUsage];\n}\n\n// ---------------------------------------------------------------------------\n// Session id\n// ---------------------------------------------------------------------------\n\nfunction coerceSessionId(payload: Record<string, unknown>): string | null {\n\tconst raw = payload.session_id;\n\tif (typeof raw !== \"string\") return null;\n\tconst value = raw.trim();\n\treturn value || null;\n}\n\n// ---------------------------------------------------------------------------\n// mapClaudeHookPayload\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeHookAdapterEvent {\n\tschema_version: \"1.0\";\n\tsource: \"claude\";\n\tsession_id: string;\n\tevent_id: string;\n\tevent_type: string;\n\tts: string;\n\tordering_confidence: \"low\";\n\tcwd: string | null;\n\tpayload: Record<string, unknown>;\n\tmeta: Record<string, unknown>;\n}\n\n/**\n * Map a raw Claude Code hook payload to a normalized adapter event.\n * Returns null if the event type is unsupported or required fields are missing.\n */\nexport function mapClaudeHookPayload(\n\tpayload: Record<string, unknown>,\n): ClaudeHookAdapterEvent | null {\n\tconst hookEvent = String(payload.hook_event_name ?? \"\").trim();\n\tif (!MAPPABLE_CLAUDE_HOOK_EVENTS.has(hookEvent)) return null;\n\n\tconst sessionId = coerceSessionId(payload);\n\tif (!sessionId) return null;\n\n\tconst rawTs = payload.ts ?? payload.timestamp;\n\tconst normalizedRawTs = normalizeIsoTs(rawTs);\n\tconst ts = normalizedRawTs ?? nowIso();\n\tconst toolUseId = String(payload.tool_use_id ?? \"\").trim();\n\n\tconst consumed = new Set([\n\t\t\"hook_event_name\",\n\t\t\"session_id\",\n\t\t\"cwd\",\n\t\t\"ts\",\n\t\t\"timestamp\",\n\t\t\"transcript_path\",\n\t\t\"permission_mode\",\n\t\t\"tool_use_id\",\n\t]);\n\n\tlet eventType: string;\n\tlet eventPayload: Record<string, unknown>;\n\tlet eventIdPayload: Record<string, unknown>;\n\n\tif (hookEvent === \"SessionStart\") {\n\t\teventType = \"session_start\";\n\t\teventPayload = { source: payload.source };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"source\");\n\t} else if (hookEvent === \"UserPromptSubmit\") {\n\t\tconst text = String(payload.prompt ?? \"\").trim();\n\t\tif (!text) return null;\n\t\teventType = \"prompt\";\n\t\teventPayload = { text };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"prompt\");\n\t} else if (hookEvent === \"PreToolUse\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\teventType = \"tool_call\";\n\t\teventPayload = { tool_name: toolName, tool_input: toolInput };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t} else if (hookEvent === \"PostToolUse\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tconst toolResponse = payload.tool_response ?? null;\n\t\teventType = \"tool_result\";\n\t\teventPayload = {\n\t\t\ttool_name: toolName,\n\t\t\tstatus: \"ok\",\n\t\t\ttool_input: toolInput,\n\t\t\ttool_output: toolResponse,\n\t\t\ttool_error: null,\n\t\t};\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t\tconsumed.add(\"tool_response\");\n\t} else if (hookEvent === \"PostToolUseFailure\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tconst error = payload.error ?? null;\n\t\teventType = \"tool_result\";\n\t\teventPayload = {\n\t\t\ttool_name: toolName,\n\t\t\tstatus: \"error\",\n\t\t\ttool_input: toolInput,\n\t\t\ttool_output: null,\n\t\t\terror,\n\t\t};\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t\tconsumed.add(\"error\");\n\t\tconsumed.add(\"is_interrupt\");\n\t} else if (hookEvent === \"Stop\") {\n\t\tconst rawAssistantText = String(payload.last_assistant_message ?? \"\").trim();\n\t\tconst rawUsage = normalizeUsage(payload.usage);\n\n\t\tlet assistantText = rawAssistantText;\n\t\tlet usage = rawUsage;\n\n\t\tif (!assistantText || usage === null) {\n\t\t\tconst cwd = typeof payload.cwd === \"string\" ? payload.cwd : null;\n\t\t\tconst [transcriptText, transcriptUsage] = extractFromTranscript(payload.transcript_path, cwd);\n\t\t\tif (!assistantText && transcriptText) assistantText = transcriptText;\n\t\t\tif (usage === null && transcriptUsage !== null) usage = transcriptUsage;\n\t\t}\n\n\t\tif (!assistantText) return null;\n\n\t\teventType = \"assistant\";\n\t\teventPayload = { text: assistantText };\n\t\tif (usage !== null) eventPayload.usage = usage;\n\n\t\teventIdPayload = { text: rawAssistantText };\n\t\tif (rawUsage !== null) eventIdPayload.usage = rawUsage;\n\t\tif (!rawAssistantText && rawUsage === null) {\n\t\t\tconst transcriptPath = payload.transcript_path;\n\t\t\tif (typeof transcriptPath === \"string\" && transcriptPath.trim()) {\n\t\t\t\teventIdPayload.transcript_path = transcriptPath.trim();\n\t\t\t}\n\t\t}\n\t\tconsumed.add(\"stop_hook_active\");\n\t\tconsumed.add(\"last_assistant_message\");\n\t\tconsumed.add(\"usage\");\n\t} else {\n\t\t// SessionEnd\n\t\teventType = \"session_end\";\n\t\teventPayload = { reason: payload.reason ?? null };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"reason\");\n\t}\n\n\t// Build meta — forward unknown fields as hook_fields\n\tconst meta: Record<string, unknown> = {\n\t\thook_event_name: hookEvent,\n\t\tordering_confidence: \"low\",\n\t};\n\tif (toolUseId) meta.tool_use_id = toolUseId;\n\tif (normalizedRawTs === null) meta.ts_normalized = \"generated\";\n\n\tconst unknown: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(payload)) {\n\t\tif (!consumed.has(k)) unknown[k] = v;\n\t}\n\tif (Object.keys(unknown).length > 0) meta.hook_fields = unknown;\n\n\t// Compute stable event id\n\tconst eventIdTsSeed = normalizedRawTs ?? ts;\n\t// Matches Python's json.dumps(sort_keys=True, default=str):\n\t// - sortKeys() recursively sorts object keys\n\t// - the replacer coerces non-JSON-native values to strings (like Python's default=str)\n\tconst payloadHash = createHash(\"sha256\")\n\t\t.update(\n\t\t\tJSON.stringify(sortKeys(eventIdPayload), (_key, value) => {\n\t\t\t\tif (value === undefined) return \"None\"; // Python stringifies None\n\t\t\t\tif (typeof value === \"bigint\") return String(value);\n\t\t\t\treturn value;\n\t\t\t}),\n\t\t\t\"utf-8\",\n\t\t)\n\t\t.digest(\"hex\");\n\n\tconst eventId = stableEventId(sessionId, hookEvent, eventIdTsSeed, toolUseId, payloadHash);\n\n\tconst cwd = typeof payload.cwd === \"string\" ? payload.cwd : null;\n\n\treturn {\n\t\tschema_version: \"1.0\",\n\t\tsource: \"claude\",\n\t\tsession_id: sessionId,\n\t\tevent_id: eventId,\n\t\tevent_type: eventType,\n\t\tts,\n\t\tordering_confidence: \"low\",\n\t\tcwd,\n\t\tpayload: eventPayload,\n\t\tmeta,\n\t};\n}\n\n/** Recursively sort object keys (matches Python's json.dumps(sort_keys=True)). */\nfunction sortKeys(value: unknown): unknown {\n\tif (value == null || typeof value !== \"object\" || Array.isArray(value)) return value;\n\tconst sorted: Record<string, unknown> = {};\n\tfor (const k of Object.keys(value as Record<string, unknown>).sort()) {\n\t\tsorted[k] = sortKeys((value as Record<string, unknown>)[k]);\n\t}\n\treturn sorted;\n}\n\n// ---------------------------------------------------------------------------\n// buildRawEventEnvelopeFromHook\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeHookRawEventEnvelope {\n\tsession_stream_id: string;\n\tsession_id: string;\n\topencode_session_id: string;\n\tsource: string;\n\tevent_id: string;\n\tevent_type: \"claude.hook\";\n\tpayload: Record<string, unknown>;\n\tts_wall_ms: number;\n\tcwd: string | null;\n\tproject: string | null;\n\tstarted_at: string | null;\n}\n\n/**\n * Build a raw event envelope from a Claude Code hook payload.\n * Returns null if the payload is unsupported or missing required fields.\n */\nexport function buildRawEventEnvelopeFromHook(\n\thookPayload: Record<string, unknown>,\n): ClaudeHookRawEventEnvelope | null {\n\tconst adapterEvent = mapClaudeHookPayload(hookPayload);\n\tif (adapterEvent === null) return null;\n\n\tconst sessionId = adapterEvent.session_id.trim();\n\tif (!sessionId) return null;\n\n\tconst ts = adapterEvent.ts.trim();\n\tif (!ts) return null;\n\n\tconst source = adapterEvent.source || \"claude\";\n\tconst hookEventName = String(hookPayload.hook_event_name ?? \"\");\n\tconst cwd = typeof hookPayload.cwd === \"string\" ? hookPayload.cwd : null;\n\n\tlet project = resolveHookProject(cwd, hookPayload.project);\n\tif (project === null) {\n\t\tproject = resolveHookProjectFromPayloadPaths(hookPayload);\n\t}\n\n\treturn {\n\t\tsession_stream_id: sessionId,\n\t\tsession_id: sessionId,\n\t\topencode_session_id: sessionId,\n\t\tsource,\n\t\tevent_id: adapterEvent.event_id,\n\t\tevent_type: \"claude.hook\",\n\t\tpayload: {\n\t\t\ttype: \"claude.hook\",\n\t\t\ttimestamp: ts,\n\t\t\t_adapter: adapterEvent,\n\t\t},\n\t\tts_wall_ms: isoToWallMs(ts),\n\t\tcwd,\n\t\tproject,\n\t\tstarted_at: hookEventName === \"SessionStart\" ? ts : null,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// buildIngestPayloadFromHook\n// ---------------------------------------------------------------------------\n\n/**\n * Build an ingest pipeline payload from a Claude Code hook payload.\n * Used by the direct-ingest path (non-raw-event path).\n * Returns null if the payload is unsupported.\n */\nexport function buildIngestPayloadFromHook(\n\thookPayload: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst adapterEvent = mapClaudeHookPayload(hookPayload);\n\tif (adapterEvent === null) return null;\n\n\tconst sessionId = adapterEvent.session_id;\n\treturn {\n\t\tcwd: hookPayload.cwd ?? null,\n\t\tevents: [\n\t\t\t{\n\t\t\t\ttype: \"claude.hook\",\n\t\t\t\ttimestamp: adapterEvent.ts,\n\t\t\t\t_adapter: adapterEvent,\n\t\t\t},\n\t\t],\n\t\tsession_context: {\n\t\t\tsource: \"claude\",\n\t\t\tstream_id: sessionId,\n\t\t\tsession_stream_id: sessionId,\n\t\t\tsession_id: sessionId,\n\t\t\topencode_session_id: sessionId,\n\t\t},\n\t};\n}\n","/**\n * Invite link encoding/decoding for the coordinator join flow.\n *\n * Ported from codemem/coordinator_invites.py.\n */\n\nexport interface InvitePayload {\n\tv: number;\n\tkind: string;\n\tcoordinator_url: string;\n\tgroup_id: string;\n\tpolicy: string;\n\ttoken: string;\n\texpires_at: string;\n\tteam_name: string | null;\n}\n\n/** Encode an invite payload as a compact base64url string (no padding). */\nexport function encodeInvitePayload(payload: InvitePayload): string {\n\tconst json = JSON.stringify(payload);\n\tconst bytes = new TextEncoder().encode(json);\n\tconst base64 = Buffer.from(bytes).toString(\"base64url\");\n\t// base64url from Buffer already omits padding — strip defensively\n\treturn base64.replace(/=+$/, \"\");\n}\n\n/** Decode a base64url invite payload string back to an InvitePayload. */\nexport function decodeInvitePayload(value: string): InvitePayload {\n\t// Re-add padding that was stripped\n\tconst padded = value + \"=\".repeat((4 - (value.length % 4)) % 4);\n\tconst json = Buffer.from(padded, \"base64url\").toString(\"utf-8\");\n\tconst data: unknown = JSON.parse(json);\n\tif (typeof data !== \"object\" || data === null) {\n\t\tthrow new Error(\"invalid invite payload\");\n\t}\n\treturn data as InvitePayload;\n}\n\n/** Build a `codemem://join?invite=...` link from an encoded payload. */\nexport function inviteLink(encodedPayload: string): string {\n\treturn `codemem://join?invite=${encodeURIComponent(encodedPayload)}`;\n}\n\n/**\n * Extract the raw encoded payload from either a `codemem://` link or a raw value.\n *\n * Throws if a `codemem://` link is provided but has no `invite` query parameter.\n */\nexport function extractInvitePayload(value: string): string {\n\tconst raw = value.trim();\n\tif (raw.startsWith(\"codemem://\")) {\n\t\tconst url = new URL(raw);\n\t\tconst invite = url.searchParams.get(\"invite\") ?? \"\";\n\t\tif (!invite) {\n\t\t\tthrow new Error(\"invite payload missing from link\");\n\t\t}\n\t\treturn invite;\n\t}\n\treturn raw;\n}\n","/**\n * CoordinatorStore — SQLite-backed state for the coordinator relay server.\n *\n * The coordinator is a cloud relay that manages group membership, device\n * enrollment, presence, invites, and join requests for sync between devices.\n *\n * This store uses its OWN database (separate from the main codemem DB) and\n * owns its own schema — the TS side creates tables directly.\n *\n * Ported from codemem/coordinator_store.py.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { Database as DatabaseType } from \"better-sqlite3\";\nimport Database from \"better-sqlite3\";\n\nexport const DEFAULT_COORDINATOR_DB_PATH = join(homedir(), \".codemem\", \"coordinator.sqlite\");\n\n// ---------------------------------------------------------------------------\n// Address helpers (subset of sync/discovery.py, inlined to avoid circular deps)\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize an address using the URL constructor.\n * Duplicated from sync-discovery.ts to avoid circular deps\n * (coordinator-store loads before sync-discovery in the stack).\n * TODO: extract to shared address-utils.ts module.\n */\nfunction normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst parsed = new URL(address);\n\t\tconst host = parsed.hostname.toLowerCase();\n\t\tif (\n\t\t\t(parsed.protocol === \"http:\" || parsed.protocol === \"\") &&\n\t\t\thost &&\n\t\t\tparsed.port &&\n\t\t\tparsed.pathname === \"/\"\n\t\t) {\n\t\t\treturn `${host}:${parsed.port}`;\n\t\t}\n\t} catch {\n\t\t// Not parseable\n\t}\n\treturn address;\n}\n\nfunction mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction nowISO(): string {\n\treturn new Date().toISOString();\n}\n\nfunction tokenUrlSafe(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"base64url\").replace(/=+$/, \"\");\n}\n\nfunction rowToRecord(row: unknown): Record<string, unknown> {\n\tif (row == null) return {};\n\t// better-sqlite3 returns plain objects when using .get()/.all()\n\treturn row as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nfunction initializeSchema(db: DatabaseType): void {\n\tdb.exec(`\n\t\tCREATE TABLE IF NOT EXISTS groups (\n\t\t\tgroup_id TEXT PRIMARY KEY,\n\t\t\tdisplay_name TEXT,\n\t\t\tcreated_at TEXT NOT NULL\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS enrolled_devices (\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tpublic_key TEXT NOT NULL,\n\t\t\tfingerprint TEXT NOT NULL,\n\t\t\tdisplay_name TEXT,\n\t\t\tenabled INTEGER NOT NULL DEFAULT 1,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (group_id, device_id)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS presence_records (\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\taddresses_json TEXT NOT NULL,\n\t\t\tlast_seen_at TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tcapabilities_json TEXT NOT NULL DEFAULT '{}',\n\t\t\tPRIMARY KEY (group_id, device_id)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS request_nonces (\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tnonce TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (device_id, nonce)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS coordinator_invites (\n\t\t\tinvite_id TEXT PRIMARY KEY,\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\ttoken TEXT NOT NULL UNIQUE,\n\t\t\tpolicy TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tcreated_by TEXT,\n\t\t\tteam_name_snapshot TEXT,\n\t\t\trevoked_at TEXT\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS coordinator_join_requests (\n\t\t\trequest_id TEXT PRIMARY KEY,\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tpublic_key TEXT NOT NULL,\n\t\t\tfingerprint TEXT NOT NULL,\n\t\t\tdisplay_name TEXT,\n\t\t\ttoken TEXT NOT NULL,\n\t\t\tstatus TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\treviewed_at TEXT,\n\t\t\treviewed_by TEXT\n\t\t);\n\t`);\n}\n\n// ---------------------------------------------------------------------------\n// Connection\n// ---------------------------------------------------------------------------\n\nexport function connectCoordinator(path?: string): DatabaseType {\n\tconst dbPath = path ?? DEFAULT_COORDINATOR_DB_PATH;\n\tmkdirSync(dirname(dbPath), { recursive: true });\n\tconst db = new Database(dbPath);\n\tdb.pragma(\"foreign_keys = ON\");\n\tdb.pragma(\"busy_timeout = 5000\");\n\tdb.pragma(\"journal_mode = WAL\");\n\tdb.pragma(\"synchronous = NORMAL\");\n\tinitializeSchema(db);\n\treturn db;\n}\n\n// ---------------------------------------------------------------------------\n// CoordinatorStore\n// ---------------------------------------------------------------------------\n\nexport class CoordinatorStore {\n\treadonly path: string;\n\treadonly db: DatabaseType;\n\n\tconstructor(path?: string) {\n\t\tthis.path = path ?? DEFAULT_COORDINATOR_DB_PATH;\n\t\tthis.db = connectCoordinator(this.path);\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n\n\t// -- Groups -------------------------------------------------------------\n\n\tcreateGroup(groupId: string, displayName?: string | null): void {\n\t\tthis.db\n\t\t\t.prepare(\"INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)\")\n\t\t\t.run(groupId, displayName ?? null, nowISO());\n\t}\n\n\tgetGroup(groupId: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?\")\n\t\t\t.get(groupId);\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\tlistGroups(): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC\")\n\t\t\t.all()\n\t\t\t.map(rowToRecord);\n\t}\n\n\t// -- Devices ------------------------------------------------------------\n\n\tenrollDevice(\n\t\tgroupId: string,\n\t\topts: {\n\t\t\tdeviceId: string;\n\t\t\tfingerprint: string;\n\t\t\tpublicKey: string;\n\t\t\tdisplayName?: string | null;\n\t\t},\n\t): void {\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO enrolled_devices(\n\t\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, 1, ?)\n\t\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\t\tenabled = 1`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tgroupId,\n\t\t\t\topts.deviceId,\n\t\t\t\topts.publicKey,\n\t\t\t\topts.fingerprint,\n\t\t\t\topts.displayName ?? null,\n\t\t\t\tnowISO(),\n\t\t\t);\n\t}\n\n\tlistEnrolledDevices(groupId: string, includeDisabled = false): Record<string, unknown>[] {\n\t\tconst where = includeDisabled ? \"\" : \"AND enabled = 1\";\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT group_id, device_id, fingerprint, display_name, enabled, created_at\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t WHERE group_id = ? ${where}\n\t\t\t\t ORDER BY created_at ASC, device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId)\n\t\t\t.map(rowToRecord);\n\t}\n\n\tgetEnrollment(groupId: string, deviceId: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT device_id, public_key, fingerprint, display_name\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t WHERE group_id = ? AND device_id = ? AND enabled = 1`,\n\t\t\t)\n\t\t\t.get(groupId, deviceId);\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\trenameDevice(groupId: string, deviceId: string, displayName: string): boolean {\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`UPDATE enrolled_devices SET display_name = ?\n\t\t\t\t WHERE group_id = ? AND device_id = ?`,\n\t\t\t)\n\t\t\t.run(displayName, groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\tsetDeviceEnabled(groupId: string, deviceId: string, enabled: boolean): boolean {\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`UPDATE enrolled_devices SET enabled = ?\n\t\t\t\t WHERE group_id = ? AND device_id = ?`,\n\t\t\t)\n\t\t\t.run(enabled ? 1 : 0, groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\tremoveDevice(groupId: string, deviceId: string): boolean {\n\t\tthis.db\n\t\t\t.prepare(\"DELETE FROM presence_records WHERE group_id = ? AND device_id = ?\")\n\t\t\t.run(groupId, deviceId);\n\t\tconst result = this.db\n\t\t\t.prepare(\"DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?\")\n\t\t\t.run(groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\t// -- Invites ------------------------------------------------------------\n\n\tcreateInvite(opts: {\n\t\tgroupId: string;\n\t\tpolicy: string;\n\t\texpiresAt: string;\n\t\tcreatedBy?: string | null;\n\t}): Record<string, unknown> {\n\t\tconst now = nowISO();\n\t\tconst inviteId = tokenUrlSafe(12);\n\t\tconst token = tokenUrlSafe(24);\n\t\tconst group = this.getGroup(opts.groupId);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO coordinator_invites(\n\t\t\t\t\tinvite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tinviteId,\n\t\t\t\topts.groupId,\n\t\t\t\ttoken,\n\t\t\t\topts.policy,\n\t\t\t\topts.expiresAt,\n\t\t\t\tnow,\n\t\t\t\topts.createdBy ?? null,\n\t\t\t\t(group?.display_name as string) ?? null,\n\t\t\t);\n\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites WHERE invite_id = ?`,\n\t\t\t)\n\t\t\t.get(inviteId);\n\t\treturn row ? rowToRecord(row) : {};\n\t}\n\n\tgetInviteByToken(token: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites\n\t\t\t\t WHERE token = ?\n\t\t\t\t AND revoked_at IS NULL\n\t\t\t\t AND expires_at > ?`,\n\t\t\t)\n\t\t\t.get(token, new Date().toISOString());\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\tlistInvites(groupId: string): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites WHERE group_id = ?\n\t\t\t\t ORDER BY created_at ASC`,\n\t\t\t)\n\t\t\t.all(groupId)\n\t\t\t.map(rowToRecord);\n\t}\n\n\t// -- Join requests ------------------------------------------------------\n\n\tcreateJoinRequest(opts: {\n\t\tgroupId: string;\n\t\tdeviceId: string;\n\t\tpublicKey: string;\n\t\tfingerprint: string;\n\t\tdisplayName?: string | null;\n\t\ttoken: string;\n\t}): Record<string, unknown> {\n\t\tconst now = nowISO();\n\t\tconst requestId = tokenUrlSafe(12);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO coordinator_join_requests(\n\t\t\t\t\trequest_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\trequestId,\n\t\t\t\topts.groupId,\n\t\t\t\topts.deviceId,\n\t\t\t\topts.publicKey,\n\t\t\t\topts.fingerprint,\n\t\t\t\topts.displayName ?? null,\n\t\t\t\topts.token,\n\t\t\t\tnow,\n\t\t\t);\n\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t)\n\t\t\t.get(requestId);\n\t\treturn row ? rowToRecord(row) : {};\n\t}\n\n\tlistJoinRequests(groupId: string, status = \"pending\"): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t FROM coordinator_join_requests\n\t\t\t\t WHERE group_id = ? AND status = ?\n\t\t\t\t ORDER BY created_at ASC, device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId, status)\n\t\t\t.map(rowToRecord);\n\t}\n\n\treviewJoinRequest(opts: {\n\t\trequestId: string;\n\t\tapproved: boolean;\n\t\treviewedBy?: string | null;\n\t}): (Record<string, unknown> & { _no_transition?: boolean }) | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status\n\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t)\n\t\t\t.get(opts.requestId) as Record<string, unknown> | undefined;\n\n\t\tif (!row) return null;\n\n\t\tif (row.status !== \"pending\") {\n\t\t\treturn { ...rowToRecord(row), _no_transition: true };\n\t\t}\n\n\t\t// Wrap enroll + status update in a transaction for atomicity\n\t\treturn this.db.transaction(() => {\n\t\t\tconst reviewedAt = nowISO();\n\t\t\tconst nextStatus = opts.approved ? \"approved\" : \"denied\";\n\n\t\t\tif (opts.approved) {\n\t\t\t\tthis.enrollDevice(row.group_id as string, {\n\t\t\t\t\tdeviceId: row.device_id as string,\n\t\t\t\t\tfingerprint: row.fingerprint as string,\n\t\t\t\t\tpublicKey: row.public_key as string,\n\t\t\t\t\tdisplayName: ((row.display_name as string) ?? \"\").trim() || null,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`UPDATE coordinator_join_requests\n\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.run(nextStatus, reviewedAt, opts.reviewedBy ?? null, opts.requestId);\n\n\t\t\tconst updated = this.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.get(opts.requestId);\n\t\t\treturn updated ? rowToRecord(updated) : null;\n\t\t})();\n\t}\n\n\t// -- Presence -----------------------------------------------------------\n\n\tupsertPresence(opts: {\n\t\tgroupId: string;\n\t\tdeviceId: string;\n\t\taddresses: string[];\n\t\tttlS: number;\n\t\tcapabilities?: Record<string, unknown> | null;\n\t}): Record<string, unknown> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + opts.ttlS * 1000).toISOString();\n\t\tconst normalized = mergeAddresses([], opts.addresses);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO presence_records(group_id, device_id, addresses_json, last_seen_at, expires_at, capabilities_json)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?)\n\t\t\t\t ON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\taddresses_json = excluded.addresses_json,\n\t\t\t\t\tlast_seen_at = excluded.last_seen_at,\n\t\t\t\t\texpires_at = excluded.expires_at,\n\t\t\t\t\tcapabilities_json = excluded.capabilities_json`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\topts.groupId,\n\t\t\t\topts.deviceId,\n\t\t\t\tJSON.stringify(normalized),\n\t\t\t\tnow.toISOString(),\n\t\t\t\texpiresAt,\n\t\t\t\tJSON.stringify(opts.capabilities ?? {}),\n\t\t\t);\n\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tdevice_id: opts.deviceId,\n\t\t\taddresses: normalized,\n\t\t\texpires_at: expiresAt,\n\t\t};\n\t}\n\n\tlistGroupPeers(groupId: string, requestingDeviceId: string): Record<string, unknown>[] {\n\t\tconst now = new Date();\n\t\tconst rows = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT enrolled_devices.device_id, enrolled_devices.fingerprint, enrolled_devices.display_name,\n\t\t\t\t\t\tpresence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n\t\t\t\t\t\tpresence_records.capabilities_json\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t LEFT JOIN presence_records\n\t\t\t\t ON presence_records.group_id = enrolled_devices.group_id\n\t\t\t\t AND presence_records.device_id = enrolled_devices.device_id\n\t\t\t\t WHERE enrolled_devices.group_id = ?\n\t\t\t\t AND enrolled_devices.enabled = 1\n\t\t\t\t AND enrolled_devices.device_id != ?\n\t\t\t\t ORDER BY enrolled_devices.device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId, requestingDeviceId) as Record<string, unknown>[];\n\n\t\treturn rows.map((row) => {\n\t\t\tconst expiresRaw = String(row.expires_at ?? \"\").trim();\n\t\t\tlet stale = true;\n\t\t\tif (expiresRaw) {\n\t\t\t\tconst expiresAt = new Date(expiresRaw);\n\t\t\t\t// Invalid Date has NaN time — treat as stale\n\t\t\t\tstale = Number.isNaN(expiresAt.getTime()) || expiresAt <= now;\n\t\t\t}\n\t\t\tconst addresses = stale\n\t\t\t\t? []\n\t\t\t\t: mergeAddresses([], JSON.parse((row.addresses_json as string) || \"[]\") as string[]);\n\n\t\t\treturn {\n\t\t\t\tdevice_id: row.device_id,\n\t\t\t\tfingerprint: row.fingerprint,\n\t\t\t\tdisplay_name: row.display_name,\n\t\t\t\taddresses,\n\t\t\t\tlast_seen_at: row.last_seen_at,\n\t\t\t\texpires_at: row.expires_at,\n\t\t\t\tstale,\n\t\t\t\tcapabilities: JSON.parse((row.capabilities_json as string) || \"{}\"),\n\t\t\t};\n\t\t});\n\t}\n}\n","/**\n * OpenCode provider configuration loading and resolution.\n *\n * Mirrors codemem/observer_config.py — reads ~/.config/opencode/opencode.json{c},\n * resolves custom provider settings (base URL, headers, API keys), and expands\n * environment variable / file placeholders in config values.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// JSONC helpers\n// ---------------------------------------------------------------------------\n\n/** Strip JavaScript-style `//` line comments and `/* ... *​/` block comments from JSONC text. */\nexport function stripJsonComments(text: string): string {\n\tconst result: string[] = [];\n\tlet inString = false;\n\tlet escapeNext = false;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst char = text.charAt(i);\n\t\tif (escapeNext) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"\\\\\" && inString) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = !inString;\n\t\t\tresult.push(char);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inString && char === \"/\" && i + 1 < text.length) {\n\t\t\tconst next = text.charAt(i + 1);\n\t\t\tif (next === \"/\") {\n\t\t\t\t// Line comment — skip until newline\n\t\t\t\tlet j = i + 2;\n\t\t\t\twhile (j < text.length && text.charAt(j) !== \"\\n\") j++;\n\t\t\t\ti = j - 1; // outer loop will increment\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (next === \"*\") {\n\t\t\t\t// Block comment — skip until */\n\t\t\t\tlet j = i + 2;\n\t\t\t\twhile (j < text.length - 1) {\n\t\t\t\t\tif (text.charAt(j) === \"*\" && text.charAt(j + 1) === \"/\") {\n\t\t\t\t\t\tj += 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t\ti = j - 1; // outer loop will increment\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tresult.push(char);\n\t}\n\treturn result.join(\"\");\n}\n\n/** Remove trailing commas before `]` or `}` (outside strings). */\nexport function stripTrailingCommas(text: string): string {\n\tconst result: string[] = [];\n\tlet inString = false;\n\tlet escapeNext = false;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst char = text.charAt(i);\n\t\tif (escapeNext) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"\\\\\" && inString) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = !inString;\n\t\t\tresult.push(char);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inString && char === \",\") {\n\t\t\t// Look ahead past whitespace for a closing bracket/brace\n\t\t\tlet j = i + 1;\n\t\t\twhile (j < text.length && /\\s/.test(text.charAt(j))) j++;\n\t\t\tif (j < text.length && (text.charAt(j) === \"]\" || text.charAt(j) === \"}\")) {\n\t\t\t\tcontinue; // skip the trailing comma\n\t\t\t}\n\t\t}\n\t\tresult.push(char);\n\t}\n\treturn result.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Config loading\n// ---------------------------------------------------------------------------\n\n/** Load OpenCode config from `~/.config/opencode/opencode.json{c}`. */\nexport function loadOpenCodeConfig(): Record<string, unknown> {\n\tconst configDir = join(homedir(), \".config\", \"opencode\");\n\tconst candidates = [join(configDir, \"opencode.json\"), join(configDir, \"opencode.jsonc\")];\n\n\tconst configPath = candidates.find((p) => existsSync(p));\n\tif (!configPath) return {};\n\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configPath, \"utf-8\");\n\t} catch {\n\t\treturn {};\n\t}\n\n\t// Try plain JSON first\n\ttry {\n\t\treturn JSON.parse(text) as Record<string, unknown>;\n\t} catch {\n\t\t// fall through to JSONC\n\t}\n\n\ttry {\n\t\tconst cleaned = stripTrailingCommas(stripJsonComments(text));\n\t\treturn JSON.parse(cleaned) as Record<string, unknown>;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Expand `~/...` paths like Python's `Path(...).expanduser()`. */\nexport function expandUserPath(value: string): string {\n\treturn value.startsWith(\"~/\") ? join(homedir(), value.slice(2)) : value;\n}\n\n/** Env var overrides matching Python's CONFIG_ENV_OVERRIDES. */\nexport const CODEMEM_CONFIG_ENV_OVERRIDES: Record<string, string> = {\n\tactor_id: \"CODEMEM_ACTOR_ID\",\n\tactor_display_name: \"CODEMEM_ACTOR_DISPLAY_NAME\",\n\tclaude_command: \"CODEMEM_CLAUDE_COMMAND\",\n\tobserver_provider: \"CODEMEM_OBSERVER_PROVIDER\",\n\tobserver_model: \"CODEMEM_OBSERVER_MODEL\",\n\tobserver_base_url: \"CODEMEM_OBSERVER_BASE_URL\",\n\tobserver_runtime: \"CODEMEM_OBSERVER_RUNTIME\",\n\tobserver_auth_source: \"CODEMEM_OBSERVER_AUTH_SOURCE\",\n\tobserver_auth_file: \"CODEMEM_OBSERVER_AUTH_FILE\",\n\tobserver_auth_command: \"CODEMEM_OBSERVER_AUTH_COMMAND\",\n\tobserver_auth_timeout_ms: \"CODEMEM_OBSERVER_AUTH_TIMEOUT_MS\",\n\tobserver_auth_cache_ttl_s: \"CODEMEM_OBSERVER_AUTH_CACHE_TTL_S\",\n\tobserver_headers: \"CODEMEM_OBSERVER_HEADERS\",\n\tobserver_max_chars: \"CODEMEM_OBSERVER_MAX_CHARS\",\n\tpack_observation_limit: \"CODEMEM_PACK_OBSERVATION_LIMIT\",\n\tpack_session_limit: \"CODEMEM_PACK_SESSION_LIMIT\",\n\tsync_enabled: \"CODEMEM_SYNC_ENABLED\",\n\tsync_host: \"CODEMEM_SYNC_HOST\",\n\tsync_port: \"CODEMEM_SYNC_PORT\",\n\tsync_interval_s: \"CODEMEM_SYNC_INTERVAL_S\",\n\tsync_mdns: \"CODEMEM_SYNC_MDNS\",\n\tsync_advertise: \"CODEMEM_SYNC_ADVERTISE\",\n\tsync_coordinator_url: \"CODEMEM_SYNC_COORDINATOR_URL\",\n\tsync_coordinator_group: \"CODEMEM_SYNC_COORDINATOR_GROUP\",\n\tsync_coordinator_groups: \"CODEMEM_SYNC_COORDINATOR_GROUPS\",\n\tsync_coordinator_timeout_s: \"CODEMEM_SYNC_COORDINATOR_TIMEOUT_S\",\n\tsync_coordinator_presence_ttl_s: \"CODEMEM_SYNC_COORDINATOR_PRESENCE_TTL_S\",\n\tsync_coordinator_admin_secret: \"CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET\",\n\tsync_projects_include: \"CODEMEM_SYNC_PROJECTS_INCLUDE\",\n\tsync_projects_exclude: \"CODEMEM_SYNC_PROJECTS_EXCLUDE\",\n\traw_events_sweeper_interval_s: \"CODEMEM_RAW_EVENTS_SWEEPER_INTERVAL_S\",\n};\n\n/** Resolve codemem config path with CODEMEM_CONFIG override. */\nexport function getCodememConfigPath(): string {\n\tconst envPath = process.env.CODEMEM_CONFIG?.trim();\n\tif (envPath) return expandUserPath(envPath);\n\tconst configDir = join(homedir(), \".config\", \"codemem\");\n\tconst candidates = [join(configDir, \"config.json\"), join(configDir, \"config.jsonc\")];\n\treturn candidates.find((p) => existsSync(p)) ?? join(configDir, \"config.json\");\n}\n\n/** Read codemem config file with the same JSON/JSONC behavior as Python. */\nexport function readCodememConfigFile(): Record<string, unknown> {\n\tconst configPath = getCodememConfigPath();\n\tif (!existsSync(configPath)) return {};\n\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configPath, \"utf-8\");\n\t} catch {\n\t\treturn {};\n\t}\n\n\tif (!text.trim()) return {};\n\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: {};\n\t} catch {\n\t\t// fall through to JSONC\n\t}\n\n\ttry {\n\t\tconst cleaned = stripTrailingCommas(stripJsonComments(text));\n\t\tconst parsed = JSON.parse(cleaned) as unknown;\n\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Persist the codemem config file as normalized JSON. */\nexport function writeCodememConfigFile(data: Record<string, unknown>, configPath?: string): string {\n\tconst targetPath = configPath ? expandUserPath(configPath) : getCodememConfigPath();\n\tmkdirSync(dirname(targetPath), { recursive: true });\n\twriteFileSync(targetPath, `${JSON.stringify(data, null, 2)}\\n`, \"utf8\");\n\treturn targetPath;\n}\n\n/** Return active env overrides for codemem config keys. */\nexport function getCodememEnvOverrides(): Record<string, string> {\n\tconst overrides: Record<string, string> = {};\n\tfor (const [key, envVar] of Object.entries(CODEMEM_CONFIG_ENV_OVERRIDES)) {\n\t\tconst val = process.env[envVar];\n\t\tif (val != null && val !== \"\") overrides[key] = envVar;\n\t}\n\treturn overrides;\n}\n\n// ---------------------------------------------------------------------------\n// Provider helpers\n// ---------------------------------------------------------------------------\n\ntype AnyRecord = Record<string, unknown>;\n\nfunction asRecord(value: unknown): AnyRecord | null {\n\treturn value != null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as AnyRecord)\n\t\t: null;\n}\n\n/** Get provider-specific config block from the opencode config. */\nexport function getOpenCodeProviderConfig(provider: string): AnyRecord {\n\tconst config = loadOpenCodeConfig();\n\tconst providerConfig = asRecord(config.provider);\n\tif (!providerConfig) return {};\n\tconst data = asRecord(providerConfig[provider]);\n\treturn data ?? {};\n}\n\n/** List all custom provider keys from the opencode config. */\nexport function listCustomProviders(): Set<string> {\n\tconst config = loadOpenCodeConfig();\n\tconst providerConfig = asRecord(config.provider);\n\tif (!providerConfig) return new Set();\n\treturn new Set(Object.keys(providerConfig));\n}\n\nconst BUILTIN_MODEL_PREFIX_PROVIDERS = new Set([\"openai\", \"anthropic\", \"opencode\"]);\n\nfunction extractProviderPrefix(value: unknown): string | null {\n\tif (typeof value !== \"string\" || !value.includes(\"/\")) return null;\n\tconst prefix = value.split(\"/\")[0]?.trim().toLowerCase();\n\treturn prefix ? prefix : null;\n}\n\nexport function listConfiguredOpenCodeProviders(): Set<string> {\n\tconst providers = listCustomProviders();\n\tconst config = loadOpenCodeConfig();\n\tfor (const key of [\"model\", \"small_model\"]) {\n\t\tconst prefix = extractProviderPrefix(config[key]);\n\t\tif (prefix) providers.add(prefix);\n\t}\n\treturn providers;\n}\n\nexport function listObserverProviderOptions(): string[] {\n\tconst providers = listConfiguredOpenCodeProviders();\n\tfor (const provider of BUILTIN_MODEL_PREFIX_PROVIDERS) providers.add(provider);\n\treturn Array.from(providers).sort((a, b) => a.localeCompare(b));\n}\n\nexport function resolveBuiltInProviderFromModel(model: string): string | null {\n\tconst prefix = extractProviderPrefix(model);\n\treturn prefix && BUILTIN_MODEL_PREFIX_PROVIDERS.has(prefix) ? prefix : null;\n}\n\nexport function resolveBuiltInProviderDefaultModel(provider: string): string | null {\n\tif (provider === \"openai\") return \"gpt-5.1-codex-mini\";\n\tif (provider === \"anthropic\") return \"claude-sonnet-4-20250514\";\n\tif (provider === \"opencode\") return \"opencode/gpt-5.1-codex-mini\";\n\treturn null;\n}\n\nexport function resolveBuiltInProviderModel(\n\tprovider: string,\n\tmodelName: string,\n): [baseUrl: string | null, modelId: string | null, headers: Record<string, string>] {\n\tif (provider !== \"opencode\") {\n\t\treturn [null, modelName || resolveBuiltInProviderDefaultModel(provider), {}];\n\t}\n\tconst name = modelName || resolveBuiltInProviderDefaultModel(provider) || \"\";\n\tconst prefix = `${provider}/`;\n\tconst shortName = name.startsWith(prefix) ? name.slice(prefix.length) : name;\n\treturn [\"https://opencode.ai/zen/v1\", shortName || null, {}];\n}\n\n/** Extract provider prefix from a model string like `\"myprovider/model-name\"`. */\nexport function resolveCustomProviderFromModel(\n\tmodel: string,\n\tproviders: Set<string>,\n): string | null {\n\tif (!model || !model.includes(\"/\")) return null;\n\tconst prefix = model.split(\"/\")[0] ?? \"\";\n\treturn prefix && providers.has(prefix) ? prefix : null;\n}\n\n// ---------------------------------------------------------------------------\n// Placeholder resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Expand `$ENV_VAR` / `${ENV_VAR}` references and `{file:/path}` placeholders.\n *\n * Environment variable expansion mirrors Python's `os.path.expandvars`.\n * File placeholders read the file content and substitute it inline.\n */\nexport function resolvePlaceholder(value: string): string {\n\tconst expanded = expandEnvVars(value);\n\treturn resolveFilePlaceholder(expanded);\n}\n\n/** Expand `$VAR` and `${VAR}` environment variable references. */\nfunction expandEnvVars(value: string): string {\n\treturn value.replace(/\\$\\{([^}]+)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, bare) => {\n\t\tconst name = braced ?? bare;\n\t\treturn process.env[name] ?? match;\n\t});\n}\n\n/** Expand `{file:path}` placeholders by reading the referenced file. */\nfunction resolveFilePlaceholder(value: string): string {\n\tif (!value.includes(\"{file:\")) return value;\n\treturn value.replace(/\\{file:([^}]+)\\}/g, (match, rawPath: string) => {\n\t\tconst trimmed = rawPath.trim();\n\t\tif (!trimmed) return match;\n\t\tconst resolved = expandEnvVars(trimmed).replace(/^~/, homedir());\n\t\ttry {\n\t\t\treturn readFileSync(resolved, \"utf-8\").trim();\n\t\t} catch {\n\t\t\treturn match;\n\t\t}\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Provider option extraction\n// ---------------------------------------------------------------------------\n\n/** Extract the `options` sub-object from a provider config block. */\nexport function getProviderOptions(providerConfig: AnyRecord): AnyRecord {\n\tconst options = asRecord(providerConfig.options);\n\treturn options ?? {};\n}\n\n/** Extract `baseURL` / `baseUrl` / `base_url` from provider config. */\nexport function getProviderBaseUrl(providerConfig: AnyRecord): string | null {\n\tconst options = getProviderOptions(providerConfig);\n\t// Use || (not ??) so empty strings fall through to the next candidate (matches Python's `or`)\n\tconst baseUrl = options.baseURL || options.baseUrl || options.base_url || providerConfig.base_url;\n\treturn typeof baseUrl === \"string\" && baseUrl ? baseUrl : null;\n}\n\n/** Extract and resolve headers (with placeholder expansion) from provider config. */\nexport function getProviderHeaders(providerConfig: AnyRecord): Record<string, string> {\n\tconst options = getProviderOptions(providerConfig);\n\tconst headers = asRecord(options.headers);\n\tif (!headers) return {};\n\tconst parsed: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (typeof key !== \"string\" || typeof value !== \"string\") continue;\n\t\tparsed[key] = resolvePlaceholder(value);\n\t}\n\treturn parsed;\n}\n\n/** Extract API key from provider config (direct value or env-var reference). */\nexport function getProviderApiKey(providerConfig: AnyRecord): string | null {\n\tconst options = getProviderOptions(providerConfig);\n\t// Use || (not ??) so empty strings fall through (matches Python's `or`)\n\tconst apiKey = options.apiKey || providerConfig.apiKey;\n\tif (typeof apiKey === \"string\" && apiKey) {\n\t\treturn resolvePlaceholder(apiKey);\n\t}\n\tconst apiKeyEnv = (options.apiKeyEnv ?? options.api_key_env) as string | undefined;\n\tif (typeof apiKeyEnv === \"string\" && apiKeyEnv) {\n\t\tconst value = process.env[apiKeyEnv];\n\t\tif (value) return value;\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Custom provider model resolution\n// ---------------------------------------------------------------------------\n\n/** Find the default model for a custom provider. */\nexport function resolveCustomProviderDefaultModel(provider: string): string | null {\n\tconst providerConfig = getOpenCodeProviderConfig(provider);\n\tconst options = getProviderOptions(providerConfig);\n\tconst defaultModel =\n\t\toptions.defaultModel ??\n\t\toptions.default_model ??\n\t\tproviderConfig.defaultModel ??\n\t\tproviderConfig.default_model;\n\tif (typeof defaultModel === \"string\" && defaultModel) {\n\t\treturn defaultModel.startsWith(`${provider}/`) ? defaultModel : `${provider}/${defaultModel}`;\n\t}\n\tconst models = asRecord(providerConfig.models);\n\tif (models) {\n\t\tconst firstKey = Object.keys(models)[0];\n\t\tif (typeof firstKey === \"string\" && firstKey) {\n\t\t\treturn `${provider}/${firstKey}`;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Resolve base_url, model_id, and headers for a custom provider model.\n *\n * Returns `[baseUrl, modelId, headers]`.\n */\nexport function resolveCustomProviderModel(\n\tprovider: string,\n\tmodelName: string,\n): [baseUrl: string | null, modelId: string | null, headers: Record<string, string>] {\n\tconst providerConfig = getOpenCodeProviderConfig(provider);\n\tconst baseUrl = getProviderBaseUrl(providerConfig);\n\tconst headers = getProviderHeaders(providerConfig);\n\n\tlet name = modelName;\n\tif (!name) {\n\t\tname = resolveCustomProviderDefaultModel(provider) ?? \"\";\n\t}\n\n\tconst prefix = `${provider}/`;\n\tconst shortName = name.startsWith(prefix) ? name.slice(prefix.length) : name;\n\n\tconst models = asRecord(providerConfig.models);\n\tlet modelId: string | null = shortName;\n\tif (models) {\n\t\tconst modelConfig = asRecord(models[shortName]);\n\t\tif (modelConfig && typeof modelConfig.id === \"string\") {\n\t\t\tmodelId = modelConfig.id;\n\t\t}\n\t}\n\n\tif (typeof modelId !== \"string\" || !modelId) {\n\t\tmodelId = null;\n\t}\n\n\treturn [baseUrl, modelId, headers];\n}\n","/**\n * Database connection and schema initialization for the codemem TS backend.\n *\n * Mirrors codemem/db.py — same pragmas, same WAL mode, same sqlite-vec loading.\n * During Phase 1 of the migration, Python owns DDL (schema migrations).\n * The TS runtime validates the schema version but does NOT run migrations.\n */\n\nimport {\n\tcloseSync,\n\tcopyFileSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\trenameSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join } from \"node:path\";\nimport type { Database as DatabaseType } from \"better-sqlite3\";\nimport Database from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport { expandUserPath } from \"./observer-config.js\";\n\n// Re-export the Database type for consumers\nexport type { DatabaseType as Database };\n\n/** Current schema version this TS runtime was built against. */\nexport const SCHEMA_VERSION = 6;\n\n/**\n * Minimum schema version the TS runtime can operate with.\n * Per the coexistence contract (docs/plans/2026-03-15-db-coexistence-contract.md),\n * TS must tolerate additive newer schemas. It only hard-fails if the schema is\n * too old to have the tables/columns it needs.\n */\nexport const MIN_COMPATIBLE_SCHEMA = 6;\n\n/** Required tables the TS runtime needs to function. */\nconst REQUIRED_TABLES = [\n\t\"memory_items\",\n\t\"sessions\",\n\t\"artifacts\",\n\t\"raw_events\",\n\t\"raw_event_sessions\",\n\t\"usage_events\",\n] as const;\n\n/** Marker file written after the first successful TS access to a DB. */\nconst TS_MARKER = \".codemem-ts-accessed\";\n\n/** Lock file to avoid concurrent first-access backups. */\nconst TS_BACKUP_LOCK = \".codemem-ts-backup.lock\";\n\n/** Consider a backup lock stale after 15 minutes. */\nconst TS_BACKUP_LOCK_STALE_MS = 15 * 60 * 1000;\n\ninterface BackupLockAcquireResult {\n\tfd: number | null;\n\tcontention: boolean;\n}\n\nfunction acquireBackupLock(lockPath: string): BackupLockAcquireResult {\n\tmkdirSync(dirname(lockPath), { recursive: true });\n\ttry {\n\t\treturn { fd: openSync(lockPath, \"wx\"), contention: false };\n\t} catch (err) {\n\t\tconst code = err && typeof err === \"object\" ? (err as NodeJS.ErrnoException).code : undefined;\n\t\tif (code === \"EEXIST\") {\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tconst ageMs = Date.now() - statSync(lockPath).mtimeMs;\n\t\t\t\tstale = Number.isFinite(ageMs) && ageMs > TS_BACKUP_LOCK_STALE_MS;\n\t\t\t} catch {\n\t\t\t\t// If we can't read lock metadata, treat as live contention.\n\t\t\t}\n\n\t\t\tif (!stale) {\n\t\t\t\treturn { fd: null, contention: true };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tunlinkSync(lockPath);\n\t\t\t} catch (unlinkErr) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[codemem] Warning: failed to clear stale backup lock at ${lockPath}:`,\n\t\t\t\t\tunlinkErr,\n\t\t\t\t);\n\t\t\t\treturn { fd: null, contention: false };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn { fd: openSync(lockPath, \"wx\"), contention: false };\n\t\t\t} catch (retryErr) {\n\t\t\t\tconst retryCode =\n\t\t\t\t\tretryErr && typeof retryErr === \"object\"\n\t\t\t\t\t\t? (retryErr as NodeJS.ErrnoException).code\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (retryCode === \"EEXIST\") {\n\t\t\t\t\treturn { fd: null, contention: true };\n\t\t\t\t}\n\t\t\t\tconsole.error(`[codemem] Warning: failed to acquire backup lock at ${lockPath}:`, retryErr);\n\t\t\t\treturn { fd: null, contention: false };\n\t\t\t}\n\t\t}\n\n\t\tconsole.error(`[codemem] Warning: failed to acquire backup lock at ${lockPath}:`, err);\n\t\treturn { fd: null, contention: false };\n\t}\n}\n\n/** Default database path — matches Python's DEFAULT_DB_PATH. */\nexport const DEFAULT_DB_PATH = join(homedir(), \".codemem\", \"mem.sqlite\");\n\n/**\n * Resolve the database path from explicit arg → CODEMEM_DB env → default.\n * All TS entry points should use this instead of hardcoding fallback logic.\n */\nexport function resolveDbPath(explicit?: string): string {\n\tif (explicit) return expandUserPath(explicit);\n\tconst envPath = process.env.CODEMEM_DB;\n\tif (envPath) return expandUserPath(envPath);\n\treturn DEFAULT_DB_PATH;\n}\n\n/** Legacy database paths that may exist from older installs. */\nconst LEGACY_DB_PATHS = [\n\tjoin(homedir(), \".codemem.sqlite\"),\n\tjoin(homedir(), \".opencode-mem.sqlite\"),\n];\n\n/** WAL sidecar extensions for a SQLite database file. */\nconst SIDECAR_EXTENSIONS = [\"-wal\", \"-shm\"];\n\n/**\n * Move a file and its WAL sidecars to a new location.\n * Falls back to copy+delete if rename fails (cross-device).\n */\nfunction moveWithSidecars(src: string, dst: string): void {\n\tmkdirSync(dirname(dst), { recursive: true });\n\tconst pairs: [string, string][] = [\n\t\t[src, dst],\n\t\t...SIDECAR_EXTENSIONS.map((ext): [string, string] => [src + ext, dst + ext]),\n\t];\n\tfor (const [srcPath, dstPath] of pairs) {\n\t\tif (!existsSync(srcPath)) continue;\n\t\ttry {\n\t\t\trenameSync(srcPath, dstPath);\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tcopyFileSync(srcPath, dstPath);\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(srcPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Best effort — original left in place\n\t\t\t\t}\n\t\t\t} catch (copyErr) {\n\t\t\t\t// TOCTOU: source vanished between existsSync and copy (concurrent migration).\n\t\t\t\t// If target now exists, another process already migrated — that's fine.\n\t\t\t\tif (existsSync(dstPath)) continue;\n\t\t\t\tthrow copyErr;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Migrate legacy database paths to the current default location.\n *\n * Only runs when dbPath is the DEFAULT_DB_PATH and doesn't already exist.\n * Matches Python's migrate_legacy_default_db in db.py.\n */\nexport function migrateLegacyDbPath(dbPath: string): void {\n\tif (dbPath !== DEFAULT_DB_PATH) return;\n\tif (existsSync(dbPath)) return;\n\n\tfor (const legacyPath of LEGACY_DB_PATHS) {\n\t\tif (!existsSync(legacyPath)) continue;\n\t\tmoveWithSidecars(legacyPath, dbPath);\n\t\treturn;\n\t}\n}\n\n/**\n * Open a better-sqlite3 connection with the standard codemem pragmas.\n *\n * Migrates legacy DB paths if needed, creates parent directories,\n * sets WAL mode, busy timeout, foreign keys.\n * Does NOT initialize or migrate the schema — during Phase 1, Python owns DDL.\n */\nexport function connect(dbPath: string = DEFAULT_DB_PATH): DatabaseType {\n\tmigrateLegacyDbPath(dbPath);\n\tmkdirSync(dirname(dbPath), { recursive: true });\n\tconst db = new Database(dbPath);\n\n\t// Match Python's connect() pragmas exactly\n\tdb.pragma(\"foreign_keys = ON\");\n\tdb.pragma(\"busy_timeout = 5000\");\n\n\tconst journalMode = db.pragma(\"journal_mode = WAL\", { simple: true }) as string;\n\tif (journalMode.toLowerCase() !== \"wal\") {\n\t\tconsole.warn(\n\t\t\t`Failed to enable WAL mode (got ${journalMode}). Concurrent access may not work correctly.`,\n\t\t);\n\t}\n\n\tdb.pragma(\"synchronous = NORMAL\");\n\n\treturn db;\n}\n\n/**\n * Load the sqlite-vec extension into an open database connection.\n *\n * Call this after connect() when vector operations are needed.\n * Skipped when CODEMEM_EMBEDDING_DISABLED=1.\n */\nexport function loadSqliteVec(db: DatabaseType): void {\n\tif (isEmbeddingDisabled()) {\n\t\treturn;\n\t}\n\n\tsqliteVec.load(db);\n\n\tconst row = db.prepare(\"SELECT vec_version() AS v\").get() as { v: string } | undefined;\n\tif (!row?.v) {\n\t\tthrow new Error(\"sqlite-vec loaded but version check failed\");\n\t}\n}\n\n/** Check if embeddings are disabled via environment variable. */\nexport function isEmbeddingDisabled(): boolean {\n\tconst val = process.env.CODEMEM_EMBEDDING_DISABLED?.toLowerCase();\n\treturn val === \"1\" || val === \"true\" || val === \"yes\";\n}\n\n/**\n * Read the schema user_version from the database.\n *\n * During Phase 1, the TS runtime uses this to verify the schema is initialized\n * (by Python) before operating. It does NOT set or bump the version.\n */\nexport function getSchemaVersion(db: DatabaseType): number {\n\tconst row = db.pragma(\"user_version\", { simple: true });\n\treturn typeof row === \"number\" ? row : 0;\n}\n\n/**\n * Create a timestamped backup of the database on first TS access.\n *\n * The backup file is named `mem.sqlite.pre-ts-YYYYMMDDTHHMMSS.bak` and placed\n * next to the original. A marker file prevents repeated backups. This ensures\n * users can recover if the TS runtime introduces bugs that corrupt the DB\n * during the migration period.\n */\nexport function backupOnFirstAccess(dbPath: string): void {\n\tconst markerPath = join(dirname(dbPath), TS_MARKER);\n\tif (existsSync(markerPath)) return;\n\n\t// Find the actual DB file to back up. It may be at the target path already,\n\t// or at a legacy location that migrateLegacyDbPath() will move later.\n\t// Back up whichever exists BEFORE migration runs.\n\tlet sourceDbPath = dbPath;\n\tif (!existsSync(dbPath)) {\n\t\t// Check legacy locations — same order as migrateLegacyDbPath()\n\t\tconst legacyPaths = [\n\t\t\tjoin(dirname(dbPath), \"..\", \".codemem.sqlite\"),\n\t\t\tjoin(dirname(dbPath), \"..\", \".opencode-mem.sqlite\"),\n\t\t];\n\t\tconst legacyPath = legacyPaths.find((p) => existsSync(p));\n\t\tif (legacyPath) {\n\t\t\tsourceDbPath = legacyPath;\n\t\t} else {\n\t\t\treturn; // Fresh DB, nothing to back up\n\t\t}\n\t}\n\n\tconst lockPath = join(dirname(dbPath), TS_BACKUP_LOCK);\n\tconst { fd: lockFd, contention } = acquireBackupLock(lockPath);\n\tif (contention) {\n\t\t// Another process is handling first-access backup.\n\t\treturn;\n\t}\n\n\tconst writeMarker = (): void => {\n\t\ttry {\n\t\t\tmkdirSync(dirname(markerPath), { recursive: true });\n\t\t\twriteFileSync(markerPath, new Date().toISOString(), \"utf-8\");\n\t\t} catch {\n\t\t\t// Non-fatal — worst case we attempt backup again later.\n\t\t}\n\t};\n\n\tconst hasViableExistingBackup = (): boolean => {\n\t\tlet sourceSize = 0;\n\t\ttry {\n\t\t\tsourceSize = statSync(sourceDbPath).size;\n\t\t} catch {\n\t\t\tsourceSize = 0;\n\t\t}\n\t\tconst minViableSize = sourceSize > 0 ? Math.floor(sourceSize * 0.9) : 1;\n\t\tconst prefix = `${basename(sourceDbPath)}.pre-ts-`;\n\t\ttry {\n\t\t\tconst entries = readdirSync(dirname(sourceDbPath));\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.startsWith(prefix) || !entry.endsWith(\".bak\")) continue;\n\t\t\t\tconst fullPath = join(dirname(sourceDbPath), entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (statSync(fullPath).size >= minViableSize) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore races while inspecting backup candidates.\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore directory read errors — proceed with normal backup path.\n\t\t}\n\t\treturn false;\n\t};\n\n\tconst ts = new Date().toISOString().replace(/[:.]/g, \"\").slice(0, 15);\n\tconst backupPath = `${sourceDbPath}.pre-ts-${ts}.bak`;\n\tlet backupSucceeded = false;\n\ttry {\n\t\tif (existsSync(markerPath)) return;\n\t\tif (hasViableExistingBackup()) {\n\t\t\twriteMarker();\n\t\t\treturn;\n\t\t}\n\n\t\tcopyFileSync(sourceDbPath, backupPath);\n\t\t// Also back up WAL/SHM if present (they contain uncommitted data)\n\t\tconst walPath = `${sourceDbPath}-wal`;\n\t\tconst shmPath = `${sourceDbPath}-shm`;\n\t\tif (existsSync(walPath)) copyFileSync(walPath, `${backupPath}-wal`);\n\t\tif (existsSync(shmPath)) copyFileSync(shmPath, `${backupPath}-shm`);\n\t\tconsole.error(`[codemem] First TS access — backed up database to ${backupPath}`);\n\t\tbackupSucceeded = true;\n\t} catch (err) {\n\t\tconsole.error(`[codemem] Warning: failed to create backup at ${backupPath}:`, err);\n\t\t// Continue — backup failure shouldn't prevent operation, but don't write marker\n\t} finally {\n\t\tif (lockFd != null) {\n\t\t\ttry {\n\t\t\t\tcloseSync(lockFd);\n\t\t\t} catch {\n\t\t\t\t// Ignore close errors.\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tunlinkSync(lockPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore lock cleanup errors.\n\t\t\t}\n\t\t}\n\t}\n\n\t// Only write marker after backup succeeds — a transient failure should\n\t// retry on next access, not permanently suppress the safety net.\n\tif (backupSucceeded) {\n\t\twriteMarker();\n\t}\n}\n\n/**\n * Verify the database schema is initialized and compatible.\n *\n * Per the coexistence contract: TS tolerates additive newer schemas (Python may\n * have run migrations that add tables/columns the TS runtime doesn't know about).\n * TS only hard-fails if:\n * - Schema is uninitialized (version 0)\n * - Schema is too old (below MIN_COMPATIBLE_SCHEMA)\n * - Required tables are missing\n * - FTS5 index is missing (needed for search)\n *\n * Warns (but continues) if schema is newer than SCHEMA_VERSION — the additive\n * changes are assumed safe per the coexistence contract.\n */\nexport function assertSchemaReady(db: DatabaseType): void {\n\tconst version = getSchemaVersion(db);\n\tif (version === 0) {\n\t\tthrow new Error(\n\t\t\t\"Database schema is not initialized. \" +\n\t\t\t\t\"Run the Python runtime to initialize: uv run codemem stats\",\n\t\t);\n\t}\n\tif (version < MIN_COMPATIBLE_SCHEMA) {\n\t\tthrow new Error(\n\t\t\t`Database schema version ${version} is older than minimum compatible (${MIN_COMPATIBLE_SCHEMA}). ` +\n\t\t\t\t\"Run the Python runtime to complete migrations: uv run codemem stats\",\n\t\t);\n\t}\n\tif (version > SCHEMA_VERSION) {\n\t\tconsole.warn(\n\t\t\t`Database schema version ${version} is newer than this TS runtime (${SCHEMA_VERSION}). ` +\n\t\t\t\t\"Running in compatibility mode — additive schema changes are tolerated.\",\n\t\t);\n\t}\n\n\t// Validate required tables exist (catches corrupt or partially migrated DBs)\n\tconst missing = REQUIRED_TABLES.filter((t) => !tableExists(db, t));\n\tif (missing.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Required tables missing: ${missing.join(\", \")}. ` +\n\t\t\t\t\"The database may be corrupt or from an incompatible version.\",\n\t\t);\n\t}\n\n\t// FTS5 index is required for search\n\tif (!tableExists(db, \"memory_fts\")) {\n\t\tthrow new Error(\n\t\t\t\"FTS5 index (memory_fts) is missing. \" +\n\t\t\t\t\"Run the Python runtime to rebuild: uv run codemem stats\",\n\t\t);\n\t}\n}\n\n/** Check if a table exists in the database. */\nexport function tableExists(db: DatabaseType, table: string): boolean {\n\tconst row = db\n\t\t.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\")\n\t\t.get(table);\n\treturn row !== undefined;\n}\n\n/** Check if a column exists in a table. */\nexport function columnExists(db: DatabaseType, table: string, column: string): boolean {\n\tif (!tableExists(db, table)) return false;\n\tconst row = db\n\t\t.prepare(\"SELECT 1 FROM pragma_table_info(?) WHERE name = ? LIMIT 1\")\n\t\t.get(table, column);\n\treturn row !== undefined;\n}\n\n/**\n * Apply additive compatibility fixes for legacy TS-era schemas.\n *\n * These are safe, one-way `ALTER TABLE ... ADD COLUMN` updates used to\n * prevent runtime failures when older local databases are missing columns\n * introduced in later releases.\n */\nexport function ensureAdditiveSchemaCompatibility(db: DatabaseType): void {\n\tif (!tableExists(db, \"raw_event_flush_batches\")) return;\n\n\tconst additiveColumns: Array<{ name: string; ddl: string }> = [\n\t\t{ name: \"error_message\", ddl: \"TEXT\" },\n\t\t{ name: \"error_type\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_provider\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_model\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_runtime\", ddl: \"TEXT\" },\n\t\t{ name: \"attempt_count\", ddl: \"INTEGER NOT NULL DEFAULT 0\" },\n\t];\n\n\tfor (const { name, ddl } of additiveColumns) {\n\t\tif (columnExists(db, \"raw_event_flush_batches\", name)) continue;\n\t\ttry {\n\t\t\tdb.exec(`ALTER TABLE raw_event_flush_batches ADD COLUMN ${name} ${ddl}`);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message.toLowerCase() : \"\";\n\t\t\tconst duplicateColumn = message.includes(\"duplicate column name\");\n\t\t\tif (duplicateColumn && columnExists(db, \"raw_event_flush_batches\", name)) {\n\t\t\t\t// Another process may have raced and added the column first.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n\n/** Safely parse a JSON string, returning {} on failure. */\nexport function fromJson(text: string | null | undefined): Record<string, unknown> {\n\tif (!text) return {};\n\ttry {\n\t\tconst parsed = JSON.parse(text);\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\treturn {};\n\t\t}\n\t\treturn parsed as Record<string, unknown>;\n\t} catch {\n\t\tconsole.warn(`[codemem] fromJson: invalid JSON (${text.slice(0, 80)}...)`);\n\t\treturn {};\n\t}\n}\n\n/**\n * Parse a JSON string strictly — throws on invalid input.\n *\n * Use in mutation paths (replication apply, import) where silently\n * returning {} would mask data corruption. Read paths should use\n * fromJson() which is forgiving.\n */\nexport function fromJsonStrict(text: string | null | undefined): Record<string, unknown> {\n\tif (!text) return {};\n\tconst parsed = JSON.parse(text);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\n\t\t\t`fromJsonStrict: expected object, got ${Array.isArray(parsed) ? \"array\" : typeof parsed}`,\n\t\t);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\n/** Serialize a value to JSON, defaulting null/undefined to \"{}\". */\nexport function toJson(data: unknown): string {\n\tif (data == null) return \"{}\";\n\treturn JSON.stringify(data);\n}\n\n/**\n * Serialize a value to JSON, preserving null as SQL NULL.\n *\n * Use this for columns that store JSON arrays (facts, concepts, files_read,\n * files_modified) where NULL means \"no data\" and \"{}\" would be corruption.\n * Also normalizes empty objects ({}) to null — Python's from_json returns {}\n * for empty DB values, so imported exports carry {} instead of null.\n * Use `toJson` for metadata_json where \"{}\" is the correct empty default.\n */\nexport function toJsonNullable(data: unknown): string | null {\n\tif (data == null) return null;\n\t// Normalize empty objects to null — these are never valid array column values\n\tif (\n\t\ttypeof data === \"object\" &&\n\t\t!Array.isArray(data) &&\n\t\tObject.keys(data as object).length === 0\n\t) {\n\t\treturn null;\n\t}\n\treturn JSON.stringify(data);\n}\n","/**\n * Low-signal observation filtering for the ingest pipeline.\n *\n * Ports the relevant parts of codemem/summarizer.py — detects observations\n * that are too generic or noisy to store as memories.\n */\n\n// ---------------------------------------------------------------------------\n// Patterns that indicate low-signal content\n// ---------------------------------------------------------------------------\n\n/**\n * Observation-level patterns that indicate content too generic to store.\n * Empty by default — trust the observer LLM. Only patterns that consistently\n * get through observer guidance are added here.\n */\nconst LOW_SIGNAL_OBSERVATION_PATTERNS: RegExp[] = [\n\t/\\bno\\s+code\\s+changes?\\s+(?:were|was)\\s+(?:recorded|made)\\b/i,\n\t/\\bno\\s+code\\s+was\\s+modified\\b/i,\n\t/\\bno\\s+new\\s+(?:code|configuration|config|documentation)(?:\\s+or\\s+(?:code|configuration|config|documentation))?\\s+(?:was|were)\\s+(?:shipped|delivered)\\b/i,\n\t/\\bno\\s+new\\s+deliverables?\\b/i,\n\t/\\bno\\s+definitive\\s+(?:code\\s+rewrite|feature\\s+delivery)(?:\\s+or\\s+(?:code\\s+rewrite|feature\\s+delivery))?\\s+(?:occurred|happened)\\b/i,\n\t/\\bonly\\s+file\\s+inspection\\s+occurred\\b/i,\n\t/\\bonly\\s+produced\\s+(?:an?\\s+)?understanding\\b/i,\n\t/\\bconsisted\\s+entirely\\s+of\\s+capturing\\b/i,\n\t/\\bno\\s+fully\\s+resolved\\s+deliverable\\b/i,\n\t/\\beffort\\s+focused\\s+on\\s+clarifying\\b/i,\n\t/\\bno\\s+code\\s*,?\\s+configuration\\s*,?\\s+or\\s+documentation\\s+changes?\\s+(?:were|was)\\s+made\\b/i,\n\t/\\bwork\\s+consisted\\s+entirely\\s+of\\s+capturing\\s+the\\s+current\\s+state\\b/i,\n\t/\\bprimary\\s+user\\s+request\\s+details\\s+were\\s+absent\\b/i,\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** Normalize observation text: strip leading bullets/markers, collapse whitespace. */\nexport function normalizeObservation(text: string): string {\n\tlet cleaned = text.trim().replace(/^[\\s\\-\\u2022\\u2514\\u203a>$]+/, \"\");\n\tcleaned = cleaned.replace(/\\s+/g, \" \").trim();\n\treturn cleaned;\n}\n\n/**\n * Return true if the observation text is too generic / low-signal to store.\n *\n * Checks against known patterns of empty or self-referential content that\n * the observer LLM sometimes generates.\n */\nexport function isLowSignalObservation(text: string): boolean {\n\tconst normalized = normalizeObservation(text);\n\tif (!normalized) return true;\n\treturn LOW_SIGNAL_OBSERVATION_PATTERNS.some((p) => p.test(normalized));\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\nexport function projectBasename(value: string): string {\n\tconst normalized = value.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\tif (!normalized) return \"\";\n\tconst parts = normalized.split(\"/\");\n\treturn parts[parts.length - 1] ?? \"\";\n}\n\nexport function projectColumnClause(\n\tcolumnExpr: string,\n\tproject: string,\n): { clause: string; params: string[] } {\n\tconst trimmed = project.trim();\n\tif (!trimmed) return { clause: \"\", params: [] };\n\tconst value = /[\\\\/]/.test(trimmed) ? projectBasename(trimmed) : trimmed;\n\tif (!value) return { clause: \"\", params: [] };\n\treturn {\n\t\tclause: `(${columnExpr} = ? OR ${columnExpr} LIKE ? OR ${columnExpr} LIKE ?)`,\n\t\tparams: [value, `%/${value}`, `%\\\\${value}`],\n\t};\n}\n\nexport function projectClause(project: string): { clause: string; params: string[] } {\n\treturn projectColumnClause(\"sessions.project\", project);\n}\n\nexport function projectMatchesFilter(\n\tprojectFilter: string | null | undefined,\n\titemProject: string | null | undefined,\n): boolean {\n\tif (!projectFilter) return true;\n\tif (!itemProject) return false;\n\tconst normalizedFilter = projectFilter.trim().replaceAll(\"\\\\\", \"/\");\n\tif (!normalizedFilter) return true;\n\tconst filterValue = normalizedFilter.includes(\"/\")\n\t\t? projectBasename(normalizedFilter)\n\t\t: normalizedFilter;\n\tconst normalizedProject = itemProject.replaceAll(\"\\\\\", \"/\");\n\treturn normalizedProject === filterValue || normalizedProject.endsWith(`/${filterValue}`);\n}\n\nfunction findGitAnchor(startCwd: string): string | null {\n\tlet current = resolve(startCwd);\n\twhile (true) {\n\t\tconst gitPath = resolve(current, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tif (lstatSync(gitPath).isDirectory()) {\n\t\t\t\t\treturn current;\n\t\t\t\t}\n\t\t\t\tconst text = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\tif (text.startsWith(\"gitdir:\")) {\n\t\t\t\t\tconst gitdir = resolve(current, text.slice(\"gitdir:\".length).trim()).replaceAll(\n\t\t\t\t\t\t\"\\\\\",\n\t\t\t\t\t\t\"/\",\n\t\t\t\t\t);\n\t\t\t\t\tconst worktreeMarker = \"/.git/worktrees/\";\n\t\t\t\t\tconst worktreeIndex = gitdir.indexOf(worktreeMarker);\n\t\t\t\t\tif (worktreeIndex >= 0) {\n\t\t\t\t\t\treturn gitdir.slice(0, worktreeIndex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn current;\n\t\t\t} catch {\n\t\t\t\treturn current;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return null;\n\t\tcurrent = parent;\n\t}\n}\n\nexport function resolveProject(cwd: string, override?: string | null): string | null {\n\tif (override != null) {\n\t\tconst trimmed = override.trim();\n\t\treturn trimmed || null;\n\t}\n\tconst gitAnchor = findGitAnchor(cwd);\n\tif (gitAnchor) {\n\t\treturn basename(gitAnchor);\n\t}\n\treturn basename(resolve(cwd));\n}\n","/**\n * Drizzle ORM schema for the codemem SQLite database.\n */\n\nimport {\n\tindex,\n\tinteger,\n\tprimaryKey,\n\treal,\n\tsqliteTable,\n\ttext,\n\tuniqueIndex,\n} from \"drizzle-orm/sqlite-core\";\n\nexport const sessions = sqliteTable(\"sessions\", {\n\tid: integer(\"id\").primaryKey(),\n\tstarted_at: text(\"started_at\").notNull(),\n\tended_at: text(\"ended_at\"),\n\tcwd: text(\"cwd\"),\n\tproject: text(\"project\"),\n\tgit_remote: text(\"git_remote\"),\n\tgit_branch: text(\"git_branch\"),\n\tuser: text(\"user\"),\n\ttool_version: text(\"tool_version\"),\n\tmetadata_json: text(\"metadata_json\"),\n\timport_key: text(\"import_key\"),\n});\n\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\n\nexport const artifacts = sqliteTable(\n\t\"artifacts\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\")\n\t\t\t.notNull()\n\t\t\t.references(() => sessions.id, { onDelete: \"cascade\" }),\n\t\tkind: text(\"kind\").notNull(),\n\t\tpath: text(\"path\"),\n\t\tcontent_text: text(\"content_text\"),\n\t\tcontent_hash: text(\"content_hash\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t},\n\t(table) => [index(\"idx_artifacts_session_kind\").on(table.session_id, table.kind)],\n);\n\nexport type Artifact = typeof artifacts.$inferSelect;\nexport type NewArtifact = typeof artifacts.$inferInsert;\n\nexport const memoryItems = sqliteTable(\n\t\"memory_items\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\")\n\t\t\t.notNull()\n\t\t\t.references(() => sessions.id, { onDelete: \"cascade\" }),\n\t\tkind: text(\"kind\").notNull(),\n\t\ttitle: text(\"title\").notNull(),\n\t\tsubtitle: text(\"subtitle\"),\n\t\tbody_text: text(\"body_text\").notNull(),\n\t\tconfidence: real(\"confidence\").default(0.5),\n\t\ttags_text: text(\"tags_text\").default(\"\"),\n\t\tactive: integer(\"active\").default(1),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\tactor_id: text(\"actor_id\"),\n\t\tactor_display_name: text(\"actor_display_name\"),\n\t\tvisibility: text(\"visibility\"),\n\t\tworkspace_id: text(\"workspace_id\"),\n\t\tworkspace_kind: text(\"workspace_kind\"),\n\t\torigin_device_id: text(\"origin_device_id\"),\n\t\torigin_source: text(\"origin_source\"),\n\t\ttrust_state: text(\"trust_state\"),\n\t\tfacts: text(\"facts\"),\n\t\tnarrative: text(\"narrative\"),\n\t\tconcepts: text(\"concepts\"),\n\t\tfiles_read: text(\"files_read\"),\n\t\tfiles_modified: text(\"files_modified\"),\n\t\tuser_prompt_id: integer(\"user_prompt_id\"),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tdeleted_at: text(\"deleted_at\"),\n\t\trev: integer(\"rev\").default(0),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_memory_items_active_created\").on(table.active, table.created_at),\n\t\tindex(\"idx_memory_items_session\").on(table.session_id),\n\t],\n);\n\nexport type MemoryItem = typeof memoryItems.$inferSelect;\nexport type NewMemoryItem = typeof memoryItems.$inferInsert;\n\nexport const usageEvents = sqliteTable(\n\t\"usage_events\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"set null\",\n\t\t}),\n\t\tevent: text(\"event\").notNull(),\n\t\ttokens_read: integer(\"tokens_read\").default(0),\n\t\ttokens_written: integer(\"tokens_written\").default(0),\n\t\ttokens_saved: integer(\"tokens_saved\").default(0),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_usage_events_event_created\").on(table.event, table.created_at),\n\t\tindex(\"idx_usage_events_session\").on(table.session_id),\n\t],\n);\n\nexport type UsageEvent = typeof usageEvents.$inferSelect;\nexport type NewUsageEvent = typeof usageEvents.$inferInsert;\n\nexport const rawEvents = sqliteTable(\n\t\"raw_events\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tevent_id: text(\"event_id\"),\n\t\tevent_seq: integer(\"event_seq\").notNull(),\n\t\tevent_type: text(\"event_type\").notNull(),\n\t\tts_wall_ms: integer(\"ts_wall_ms\"),\n\t\tts_mono_ms: real(\"ts_mono_ms\"),\n\t\tpayload_json: text(\"payload_json\").notNull(),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tuniqueIndex(\"idx_raw_events_source_stream_seq\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.event_seq,\n\t\t),\n\t\tuniqueIndex(\"idx_raw_events_source_stream_event_id\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.event_id,\n\t\t),\n\t\tindex(\"idx_raw_events_session_seq\").on(table.opencode_session_id, table.event_seq),\n\t\tindex(\"idx_raw_events_created\").on(table.created_at),\n\t],\n);\n\nexport type RawEvent = typeof rawEvents.$inferSelect;\nexport type NewRawEvent = typeof rawEvents.$inferInsert;\n\nexport const rawEventSessions = sqliteTable(\n\t\"raw_event_sessions\",\n\t{\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tcwd: text(\"cwd\"),\n\t\tproject: text(\"project\"),\n\t\tstarted_at: text(\"started_at\"),\n\t\tlast_seen_ts_wall_ms: integer(\"last_seen_ts_wall_ms\"),\n\t\tlast_received_event_seq: integer(\"last_received_event_seq\").notNull().default(-1),\n\t\tlast_flushed_event_seq: integer(\"last_flushed_event_seq\").notNull().default(-1),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => [primaryKey({ columns: [table.source, table.stream_id] })],\n);\n\nexport type RawEventSession = typeof rawEventSessions.$inferSelect;\nexport type NewRawEventSession = typeof rawEventSessions.$inferInsert;\n\nexport const opencodeSessions = sqliteTable(\n\t\"opencode_sessions\",\n\t{\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tprimaryKey({ columns: [table.source, table.stream_id] }),\n\t\tindex(\"idx_opencode_sessions_session\").on(table.session_id),\n\t],\n);\n\nexport type OpencodeSession = typeof opencodeSessions.$inferSelect;\nexport type NewOpencodeSession = typeof opencodeSessions.$inferInsert;\n\nexport const rawEventFlushBatches = sqliteTable(\n\t\"raw_event_flush_batches\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tstart_event_seq: integer(\"start_event_seq\").notNull(),\n\t\tend_event_seq: integer(\"end_event_seq\").notNull(),\n\t\textractor_version: text(\"extractor_version\").notNull(),\n\t\tstatus: text(\"status\").notNull(),\n\t\terror_message: text(\"error_message\"),\n\t\terror_type: text(\"error_type\"),\n\t\tobserver_provider: text(\"observer_provider\"),\n\t\tobserver_model: text(\"observer_model\"),\n\t\tobserver_runtime: text(\"observer_runtime\"),\n\t\tattempt_count: integer(\"attempt_count\").notNull().default(0),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => [\n\t\tuniqueIndex(\"idx_flush_batches_source_stream_seq_ver\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.start_event_seq,\n\t\t\ttable.end_event_seq,\n\t\t\ttable.extractor_version,\n\t\t),\n\t\tindex(\"idx_flush_batches_session_created\").on(table.opencode_session_id, table.created_at),\n\t\tindex(\"idx_flush_batches_status_updated\").on(table.status, table.updated_at),\n\t],\n);\n\nexport type RawEventFlushBatch = typeof rawEventFlushBatches.$inferSelect;\nexport type NewRawEventFlushBatch = typeof rawEventFlushBatches.$inferInsert;\n\nexport const userPrompts = sqliteTable(\n\t\"user_prompts\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tproject: text(\"project\"),\n\t\tprompt_text: text(\"prompt_text\").notNull(),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tcreated_at_epoch: integer(\"created_at_epoch\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_user_prompts_session\").on(table.session_id),\n\t\tindex(\"idx_user_prompts_project\").on(table.project),\n\t\tindex(\"idx_user_prompts_epoch\").on(table.created_at_epoch),\n\t],\n);\n\nexport type UserPrompt = typeof userPrompts.$inferSelect;\nexport type NewUserPrompt = typeof userPrompts.$inferInsert;\n\nexport const sessionSummaries = sqliteTable(\n\t\"session_summaries\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tproject: text(\"project\"),\n\t\trequest: text(\"request\"),\n\t\tinvestigated: text(\"investigated\"),\n\t\tlearned: text(\"learned\"),\n\t\tcompleted: text(\"completed\"),\n\t\tnext_steps: text(\"next_steps\"),\n\t\tnotes: text(\"notes\"),\n\t\tfiles_read: text(\"files_read\"),\n\t\tfiles_edited: text(\"files_edited\"),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tcreated_at_epoch: integer(\"created_at_epoch\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_session_summaries_session\").on(table.session_id),\n\t\tindex(\"idx_session_summaries_project\").on(table.project),\n\t\tindex(\"idx_session_summaries_epoch\").on(table.created_at_epoch),\n\t],\n);\n\nexport type SessionSummary = typeof sessionSummaries.$inferSelect;\nexport type NewSessionSummary = typeof sessionSummaries.$inferInsert;\n\nexport const replicationOps = sqliteTable(\n\t\"replication_ops\",\n\t{\n\t\top_id: text(\"op_id\").primaryKey(),\n\t\tentity_type: text(\"entity_type\").notNull(),\n\t\tentity_id: text(\"entity_id\").notNull(),\n\t\top_type: text(\"op_type\").notNull(),\n\t\tpayload_json: text(\"payload_json\"),\n\t\tclock_rev: integer(\"clock_rev\").notNull(),\n\t\tclock_updated_at: text(\"clock_updated_at\").notNull(),\n\t\tclock_device_id: text(\"clock_device_id\").notNull(),\n\t\tdevice_id: text(\"device_id\").notNull(),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tindex(\"idx_replication_ops_created\").on(table.created_at, table.op_id),\n\t\tindex(\"idx_replication_ops_entity\").on(table.entity_type, table.entity_id),\n\t],\n);\n\nexport type ReplicationOp = typeof replicationOps.$inferSelect;\nexport type NewReplicationOp = typeof replicationOps.$inferInsert;\n\nexport const replicationCursors = sqliteTable(\"replication_cursors\", {\n\tpeer_device_id: text(\"peer_device_id\").primaryKey(),\n\tlast_applied_cursor: text(\"last_applied_cursor\"),\n\tlast_acked_cursor: text(\"last_acked_cursor\"),\n\tupdated_at: text(\"updated_at\").notNull(),\n});\n\nexport type ReplicationCursor = typeof replicationCursors.$inferSelect;\nexport type NewReplicationCursor = typeof replicationCursors.$inferInsert;\n\nexport const syncPeers = sqliteTable(\"sync_peers\", {\n\tpeer_device_id: text(\"peer_device_id\").primaryKey(),\n\tname: text(\"name\"),\n\tpinned_fingerprint: text(\"pinned_fingerprint\"),\n\tpublic_key: text(\"public_key\"),\n\taddresses_json: text(\"addresses_json\"),\n\tclaimed_local_actor: integer(\"claimed_local_actor\").notNull().default(0),\n\tactor_id: text(\"actor_id\"),\n\tprojects_include_json: text(\"projects_include_json\"),\n\tprojects_exclude_json: text(\"projects_exclude_json\"),\n\tcreated_at: text(\"created_at\").notNull(),\n\tlast_seen_at: text(\"last_seen_at\"),\n\tlast_sync_at: text(\"last_sync_at\"),\n\tlast_error: text(\"last_error\"),\n});\n\nexport type SyncPeer = typeof syncPeers.$inferSelect;\nexport type NewSyncPeer = typeof syncPeers.$inferInsert;\n\nexport const syncNonces = sqliteTable(\"sync_nonces\", {\n\tnonce: text(\"nonce\").primaryKey(),\n\tdevice_id: text(\"device_id\").notNull(),\n\tcreated_at: text(\"created_at\").notNull(),\n});\n\nexport type SyncNonce = typeof syncNonces.$inferSelect;\nexport type NewSyncNonce = typeof syncNonces.$inferInsert;\n\nexport const syncDevice = sqliteTable(\"sync_device\", {\n\tdevice_id: text(\"device_id\").primaryKey(),\n\tpublic_key: text(\"public_key\").notNull(),\n\tfingerprint: text(\"fingerprint\").notNull(),\n\tcreated_at: text(\"created_at\").notNull(),\n});\n\nexport type SyncDevice = typeof syncDevice.$inferSelect;\nexport type NewSyncDevice = typeof syncDevice.$inferInsert;\n\nexport const syncAttempts = sqliteTable(\n\t\"sync_attempts\",\n\t{\n\t\tid: integer(\"id\").primaryKey({ autoIncrement: true }),\n\t\tpeer_device_id: text(\"peer_device_id\").notNull(),\n\t\tstarted_at: text(\"started_at\").notNull(),\n\t\tfinished_at: text(\"finished_at\"),\n\t\tok: integer(\"ok\").notNull(),\n\t\tops_in: integer(\"ops_in\").notNull(),\n\t\tops_out: integer(\"ops_out\").notNull(),\n\t\terror: text(\"error\"),\n\t},\n\t(table) => [index(\"idx_sync_attempts_peer_started\").on(table.peer_device_id, table.started_at)],\n);\n\nexport type SyncAttempt = typeof syncAttempts.$inferSelect;\nexport type NewSyncAttempt = typeof syncAttempts.$inferInsert;\n\nexport const syncDaemonState = sqliteTable(\"sync_daemon_state\", {\n\tid: integer(\"id\").primaryKey(),\n\tlast_error: text(\"last_error\"),\n\tlast_traceback: text(\"last_traceback\"),\n\tlast_error_at: text(\"last_error_at\"),\n\tlast_ok_at: text(\"last_ok_at\"),\n});\n\nexport type SyncDaemonState = typeof syncDaemonState.$inferSelect;\nexport type NewSyncDaemonState = typeof syncDaemonState.$inferInsert;\n\nexport const rawEventIngestSamples = sqliteTable(\"raw_event_ingest_samples\", {\n\tid: integer(\"id\").primaryKey({ autoIncrement: true }),\n\tcreated_at: text(\"created_at\").notNull(),\n\tinserted_events: integer(\"inserted_events\").notNull().default(0),\n\tskipped_invalid: integer(\"skipped_invalid\").notNull().default(0),\n\tskipped_duplicate: integer(\"skipped_duplicate\").notNull().default(0),\n\tskipped_conflict: integer(\"skipped_conflict\").notNull().default(0),\n});\n\nexport type RawEventIngestSample = typeof rawEventIngestSamples.$inferSelect;\nexport type NewRawEventIngestSample = typeof rawEventIngestSamples.$inferInsert;\n\nexport const rawEventIngestStats = sqliteTable(\"raw_event_ingest_stats\", {\n\tid: integer(\"id\").primaryKey(),\n\tinserted_events: integer(\"inserted_events\").notNull().default(0),\n\tskipped_events: integer(\"skipped_events\").notNull().default(0),\n\tskipped_invalid: integer(\"skipped_invalid\").notNull().default(0),\n\tskipped_duplicate: integer(\"skipped_duplicate\").notNull().default(0),\n\tskipped_conflict: integer(\"skipped_conflict\").notNull().default(0),\n\tupdated_at: text(\"updated_at\").notNull(),\n});\n\nexport type RawEventIngestStat = typeof rawEventIngestStats.$inferSelect;\nexport type NewRawEventIngestStat = typeof rawEventIngestStats.$inferInsert;\n\nexport const actors = sqliteTable(\n\t\"actors\",\n\t{\n\t\tactor_id: text(\"actor_id\").primaryKey(),\n\t\tdisplay_name: text(\"display_name\").notNull(),\n\t\tis_local: integer(\"is_local\").notNull().default(0),\n\t\tstatus: text(\"status\").notNull().default(\"active\"),\n\t\tmerged_into_actor_id: text(\"merged_into_actor_id\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => ({\n\t\tisLocalIdx: index(\"idx_actors_is_local\").on(table.is_local),\n\t\tstatusIdx: index(\"idx_actors_status\").on(table.status),\n\t}),\n);\n\nexport type Actor = typeof actors.$inferSelect;\nexport type NewActor = typeof actors.$inferInsert;\n\nexport const schema = {\n\tsessions,\n\tartifacts,\n\tmemoryItems,\n\tusageEvents,\n\trawEvents,\n\trawEventSessions,\n\topencodeSessions,\n\trawEventFlushBatches,\n\tuserPrompts,\n\tsessionSummaries,\n\treplicationOps,\n\treplicationCursors,\n\tsyncPeers,\n\tsyncNonces,\n\tsyncDevice,\n\tsyncAttempts,\n\tsyncDaemonState,\n\trawEventIngestSamples,\n\trawEventIngestStats,\n\tactors,\n};\n","import { statSync } from \"node:fs\";\nimport { and, eq, gt, gte, inArray, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { assertSchemaReady, connect, type Database, resolveDbPath } from \"./db.js\";\nimport { isLowSignalObservation } from \"./ingest-filters.js\";\nimport { projectClause } from \"./project.js\";\nimport * as schema from \"./schema.js\";\n\nexport interface RawEventStatusItem {\n\tsource: string;\n\tstream_id: string;\n\topencode_session_id: string | null;\n\tcwd: string | null;\n\tproject: string | null;\n\tstarted_at: string | null;\n\tlast_seen_ts_wall_ms: number | null;\n\tlast_received_event_seq: number;\n\tlast_flushed_event_seq: number;\n\tupdated_at: string;\n\tsession_stream_id: string;\n\tsession_id: string;\n}\n\nexport interface RawEventStatusResult {\n\titems: RawEventStatusItem[];\n\ttotals: { pending: number; sessions: number };\n\tingest: { available: true; mode: \"stream_queue\"; max_body_bytes: number };\n}\n\nfunction withDb<T>(dbPath: string | undefined, fn: (db: Database, resolvedPath: string) => T): T {\n\tconst resolvedPath = resolveDbPath(dbPath);\n\tconst db = connect(resolvedPath);\n\ttry {\n\t\tassertSchemaReady(db);\n\t\treturn fn(db, resolvedPath);\n\t} finally {\n\t\tdb.close();\n\t}\n}\n\nexport function initDatabase(dbPath?: string): { path: string; sizeBytes: number } {\n\treturn withDb(dbPath, (_db, resolvedPath) => {\n\t\tconst stats = statSync(resolvedPath);\n\t\treturn { path: resolvedPath, sizeBytes: stats.size };\n\t});\n}\n\nexport function vacuumDatabase(dbPath?: string): { path: string; sizeBytes: number } {\n\treturn withDb(dbPath, (db, resolvedPath) => {\n\t\tdb.exec(\"VACUUM\");\n\t\tconst stats = statSync(resolvedPath);\n\t\treturn { path: resolvedPath, sizeBytes: stats.size };\n\t});\n}\n\nexport function getRawEventStatus(dbPath?: string, limit = 25): RawEventStatusResult {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst maxEvents = d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventSessions.source,\n\t\t\t\tstream_id: schema.rawEventSessions.stream_id,\n\t\t\t\topencode_session_id: schema.rawEventSessions.opencode_session_id,\n\t\t\t\tcwd: schema.rawEventSessions.cwd,\n\t\t\t\tproject: schema.rawEventSessions.project,\n\t\t\t\tstarted_at: schema.rawEventSessions.started_at,\n\t\t\t\tlast_seen_ts_wall_ms: schema.rawEventSessions.last_seen_ts_wall_ms,\n\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\tlast_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq,\n\t\t\t\tupdated_at: schema.rawEventSessions.updated_at,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(gt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq))\n\t\t\t.orderBy(sql`${schema.rawEventSessions.updated_at} DESC`)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\tconst items = rows.map((row) => {\n\t\t\tconst streamId = String(row.stream_id ?? row.opencode_session_id ?? \"\");\n\t\t\treturn {\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstream_id: streamId,\n\t\t\t\topencode_session_id:\n\t\t\t\t\trow.opencode_session_id == null ? null : String(row.opencode_session_id),\n\t\t\t\tcwd: row.cwd == null ? null : String(row.cwd),\n\t\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\t\tstarted_at: row.started_at == null ? null : String(row.started_at),\n\t\t\t\tlast_seen_ts_wall_ms:\n\t\t\t\t\trow.last_seen_ts_wall_ms == null ? null : Number(row.last_seen_ts_wall_ms),\n\t\t\t\tlast_received_event_seq: Number(row.last_received_event_seq ?? -1),\n\t\t\t\tlast_flushed_event_seq: Number(row.last_flushed_event_seq ?? -1),\n\t\t\t\tupdated_at: String(row.updated_at ?? \"\"),\n\t\t\t\tsession_stream_id: streamId,\n\t\t\t\tsession_id: streamId,\n\t\t\t};\n\t\t});\n\n\t\tconst totalsRow = d\n\t\t\t.select({\n\t\t\t\tsessions: sql<number>`COUNT(1)`,\n\t\t\t\tpending: sql<\n\t\t\t\t\tnumber | null\n\t\t\t\t>`SUM(${maxEvents.max_seq} - ${schema.rawEventSessions.last_flushed_event_seq})`,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(gt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq))\n\t\t\t.get();\n\n\t\treturn {\n\t\t\titems,\n\t\t\ttotals: {\n\t\t\t\tpending: Number(totalsRow?.pending ?? 0),\n\t\t\t\tsessions: Number(totalsRow?.sessions ?? 0),\n\t\t\t},\n\t\t\tingest: {\n\t\t\t\tavailable: true,\n\t\t\t\tmode: \"stream_queue\",\n\t\t\t\tmax_body_bytes: 2_000_000,\n\t\t\t},\n\t\t};\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Reliability metrics\n// ---------------------------------------------------------------------------\n\nexport interface ReliabilityMetrics {\n\tcounts: {\n\t\tinserted_events: number;\n\t\tdropped_events: number;\n\t\tstarted_batches: number;\n\t\trunning_batches: number;\n\t\tcompleted_batches: number;\n\t\terrored_batches: number;\n\t\tterminal_batches: number;\n\t\tsessions_with_events: number;\n\t\tsessions_with_started_at: number;\n\t\tretry_depth_max: number;\n\t};\n\trates: {\n\t\tflush_success_rate: number;\n\t\tdropped_event_rate: number;\n\t\tsession_boundary_accuracy: number;\n\t};\n\twindow_hours: number | null;\n}\n\nexport function getReliabilityMetrics(\n\tdbPath?: string,\n\twindowHours?: number | null,\n): ReliabilityMetrics {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst cutoffIso =\n\t\t\twindowHours != null ? new Date(Date.now() - windowHours * 3600 * 1000).toISOString() : null;\n\n\t\t// Batch counts\n\t\tconst batchRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tstarted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('started', 'pending') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\trunning: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('running', 'claimed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\tcompleted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} = 'completed' THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\terrored: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('error', 'failed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(gte(schema.rawEventFlushBatches.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tstarted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('started', 'pending') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\trunning: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('running', 'claimed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\tcompleted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} = 'completed' THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\terrored: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('error', 'failed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\n\t\tconst startedBatches = Number(batchRow?.started ?? 0);\n\t\tconst runningBatches = Number(batchRow?.running ?? 0);\n\t\tconst completedBatches = Number(batchRow?.completed ?? 0);\n\t\tconst erroredBatches = Number(batchRow?.errored ?? 0);\n\t\tconst terminalBatches = completedBatches + erroredBatches;\n\t\tconst flushSuccessRate = terminalBatches > 0 ? completedBatches / terminalBatches : 1.0;\n\n\t\t// Event counts from raw_event_sessions\n\t\t// Sequences are 0-based indexes, so +1 converts to counts.\n\t\tconst eventRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\ttotal_received: sql<number>`COALESCE(SUM(${schema.rawEventSessions.last_received_event_seq} + 1), 0)`,\n\t\t\t\t\t\t\ttotal_flushed: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventSessions.last_flushed_event_seq} >= 0 THEN ${schema.rawEventSessions.last_flushed_event_seq} + 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t\t\t.where(gte(schema.rawEventSessions.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\ttotal_received: sql<number>`COALESCE(SUM(${schema.rawEventSessions.last_received_event_seq} + 1), 0)`,\n\t\t\t\t\t\t\ttotal_flushed: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventSessions.last_flushed_event_seq} >= 0 THEN ${schema.rawEventSessions.last_flushed_event_seq} + 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\n\t\t// In-flight events: sum of (end_event_seq - start_event_seq + 1) for active batches\n\t\tconst inFlightRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tin_flight: sql<number>`COALESCE(SUM(${schema.rawEventFlushBatches.end_event_seq} - ${schema.rawEventFlushBatches.start_event_seq} + 1), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\n\t\t\t\t\t\t\t\t\t\"started\",\n\t\t\t\t\t\t\t\t\t\"pending\",\n\t\t\t\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\t\t\t\"claimed\",\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\tgte(schema.rawEventFlushBatches.updated_at, cutoffIso),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tin_flight: sql<number>`COALESCE(SUM(${schema.rawEventFlushBatches.end_event_seq} - ${schema.rawEventFlushBatches.start_event_seq} + 1), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\n\t\t\t\t\t\t\t\t\"started\",\n\t\t\t\t\t\t\t\t\"pending\",\n\t\t\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\t\t\"claimed\",\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\t\tconst inFlightEvents = Number(inFlightRow?.in_flight ?? 0);\n\n\t\tconst insertedEvents = Number(eventRow?.total_flushed ?? 0);\n\t\tconst droppedEvents = Math.max(\n\t\t\t0,\n\t\t\tNumber(eventRow?.total_received ?? 0) - Number(eventRow?.total_flushed ?? 0) - inFlightEvents,\n\t\t);\n\t\tconst droppedDenom = insertedEvents + droppedEvents;\n\t\tconst droppedEventRate = droppedDenom > 0 ? droppedEvents / droppedDenom : 0.0;\n\n\t\t// Session boundary accuracy\n\t\tconst hasEvents = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.selectDistinct({\n\t\t\t\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t\t\t.where(gte(schema.rawEvents.created_at, cutoffIso))\n\t\t\t\t: d\n\t\t\t\t\t\t.selectDistinct({\n\t\t\t\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEvents)\n\t\t).as(\"has_events\");\n\n\t\tconst boundaryRow = d\n\t\t\t.select({\n\t\t\t\tsessions_with_events: sql<number>`COUNT(1)`,\n\t\t\t\tsessions_with_started_at: sql<number>`COALESCE(SUM(CASE WHEN COALESCE(${schema.rawEventSessions.started_at}, '') != '' THEN 1 ELSE 0 END), 0)`,\n\t\t\t})\n\t\t\t.from(hasEvents)\n\t\t\t.leftJoin(\n\t\t\t\tschema.rawEventSessions,\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventSessions.source, hasEvents.source),\n\t\t\t\t\teq(schema.rawEventSessions.stream_id, hasEvents.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.get() as Record<string, number> | undefined;\n\n\t\tconst sessionsWithEvents = Number(boundaryRow?.sessions_with_events ?? 0);\n\t\tconst sessionsWithStartedAt = Number(boundaryRow?.sessions_with_started_at ?? 0);\n\t\tconst sessionBoundaryAccuracy =\n\t\t\tsessionsWithEvents > 0 ? sessionsWithStartedAt / sessionsWithEvents : 1.0;\n\n\t\tconst retryDepthRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tretry_depth_max: sql<number>`COALESCE(MAX(${schema.rawEventFlushBatches.attempt_count}), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(gte(schema.rawEventFlushBatches.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tretry_depth_max: sql<number>`COALESCE(MAX(${schema.rawEventFlushBatches.attempt_count}), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\t\tconst retryDepthMax = Math.max(0, Number(retryDepthRow?.retry_depth_max ?? 0) - 1);\n\n\t\treturn {\n\t\t\tcounts: {\n\t\t\t\tinserted_events: insertedEvents,\n\t\t\t\tdropped_events: droppedEvents,\n\t\t\t\tstarted_batches: startedBatches,\n\t\t\t\trunning_batches: runningBatches,\n\t\t\t\tcompleted_batches: completedBatches,\n\t\t\t\terrored_batches: erroredBatches,\n\t\t\t\tterminal_batches: terminalBatches,\n\t\t\t\tsessions_with_events: sessionsWithEvents,\n\t\t\t\tsessions_with_started_at: sessionsWithStartedAt,\n\t\t\t\tretry_depth_max: retryDepthMax,\n\t\t\t},\n\t\t\trates: {\n\t\t\t\tflush_success_rate: flushSuccessRate,\n\t\t\t\tdropped_event_rate: droppedEventRate,\n\t\t\t\tsession_boundary_accuracy: sessionBoundaryAccuracy,\n\t\t\t},\n\t\t\twindow_hours: windowHours ?? null,\n\t\t};\n\t});\n}\n\nexport interface GateResult {\n\tpassed: boolean;\n\tfailures: string[];\n\tmetrics: ReliabilityMetrics;\n}\n\nexport function rawEventsGate(\n\tdbPath?: string,\n\topts?: {\n\t\tminFlushSuccessRate?: number;\n\t\tmaxDroppedEventRate?: number;\n\t\tminSessionBoundaryAccuracy?: number;\n\t\twindowHours?: number;\n\t},\n): GateResult {\n\tconst minFlushSuccessRate = opts?.minFlushSuccessRate ?? 0.95;\n\tconst maxDroppedEventRate = opts?.maxDroppedEventRate ?? 0.05;\n\tconst minSessionBoundaryAccuracy = opts?.minSessionBoundaryAccuracy ?? 0.9;\n\tconst windowHours = opts?.windowHours ?? 24;\n\n\tconst metrics = getReliabilityMetrics(dbPath, windowHours);\n\tconst failures: string[] = [];\n\n\tif (metrics.rates.flush_success_rate < minFlushSuccessRate) {\n\t\tfailures.push(\n\t\t\t`flush_success_rate=${metrics.rates.flush_success_rate.toFixed(4)} < min ${minFlushSuccessRate.toFixed(4)}`,\n\t\t);\n\t}\n\tif (metrics.rates.dropped_event_rate > maxDroppedEventRate) {\n\t\tfailures.push(\n\t\t\t`dropped_event_rate=${metrics.rates.dropped_event_rate.toFixed(4)} > max ${maxDroppedEventRate.toFixed(4)}`,\n\t\t);\n\t}\n\tif (metrics.rates.session_boundary_accuracy < minSessionBoundaryAccuracy) {\n\t\tfailures.push(\n\t\t\t`session_boundary_accuracy=${metrics.rates.session_boundary_accuracy.toFixed(4)} < min ${minSessionBoundaryAccuracy.toFixed(4)}`,\n\t\t);\n\t}\n\n\treturn { passed: failures.length === 0, failures, metrics };\n}\n\n// ---------------------------------------------------------------------------\n// Retry\n// ---------------------------------------------------------------------------\n\nexport function retryRawEventFailures(dbPath?: string, limit = 25): { retried: number } {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst now = new Date().toISOString();\n\t\treturn db.transaction(() => {\n\t\t\tconst candidateIds = d\n\t\t\t\t.select({ id: schema.rawEventFlushBatches.id })\n\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t.where(inArray(schema.rawEventFlushBatches.status, [\"failed\", \"error\"]))\n\t\t\t\t.orderBy(schema.rawEventFlushBatches.updated_at)\n\t\t\t\t.limit(limit)\n\t\t\t\t.all()\n\t\t\t\t.map((row) => Number(row.id));\n\n\t\t\tif (candidateIds.length === 0) return { retried: 0 };\n\n\t\t\tconst result = d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({\n\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\terror_message: null,\n\t\t\t\t\terror_type: null,\n\t\t\t\t\tobserver_provider: null,\n\t\t\t\t\tobserver_model: null,\n\t\t\t\t\tobserver_runtime: null,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.id, candidateIds),\n\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\"failed\", \"error\"]),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.run();\n\n\t\t\treturn { retried: Number(result.changes ?? 0) };\n\t\t})();\n\t});\n}\n\nexport interface BackfillTagsTextOptions {\n\tlimit?: number | null;\n\tsince?: string | null;\n\tproject?: string | null;\n\tactiveOnly?: boolean;\n\tdryRun?: boolean;\n\tmemoryIds?: number[] | null;\n}\n\nexport interface BackfillTagsTextResult {\n\tchecked: number;\n\tupdated: number;\n\tskipped: number;\n}\n\nfunction normalizeTag(value: string): string {\n\tlet normalized = value.trim().toLowerCase();\n\tif (!normalized) return \"\";\n\tnormalized = normalized.replace(/[^a-z0-9_]+/g, \"-\");\n\tnormalized = normalized.replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n\tif (!normalized) return \"\";\n\tif (normalized.length > 40) normalized = normalized.slice(0, 40).replace(/-+$/g, \"\");\n\treturn normalized;\n}\n\nfunction fileTags(pathValue: string): string[] {\n\tconst raw = pathValue.trim();\n\tif (!raw) return [];\n\tconst parts = raw.split(/[\\\\/]+/).filter((part) => part && part !== \".\" && part !== \"..\");\n\tif (parts.length === 0) return [];\n\tconst tags: string[] = [];\n\tconst basename = normalizeTag(parts[parts.length - 1] ?? \"\");\n\tif (basename) tags.push(basename);\n\tif (parts.length >= 2) {\n\t\tconst parent = normalizeTag(parts[parts.length - 2] ?? \"\");\n\t\tif (parent) tags.push(parent);\n\t}\n\tif (parts.length >= 3) {\n\t\tconst top = normalizeTag(parts[0] ?? \"\");\n\t\tif (top) tags.push(top);\n\t}\n\treturn tags;\n}\n\nfunction parseJsonStringList(value: string | null): string[] {\n\tif (!value) return [];\n\ttry {\n\t\tconst parsed = JSON.parse(value) as unknown;\n\t\tif (!Array.isArray(parsed)) return [];\n\t\treturn parsed\n\t\t\t.map((item) => (typeof item === \"string\" ? item.trim() : \"\"))\n\t\t\t.filter((item) => item.length > 0);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction deriveTags(input: {\n\tkind: string;\n\ttitle: string;\n\tconcepts: string[];\n\tfilesRead: string[];\n\tfilesModified: string[];\n}): string[] {\n\tconst tags: string[] = [];\n\tconst kindTag = normalizeTag(input.kind);\n\tif (kindTag) tags.push(kindTag);\n\n\tfor (const concept of input.concepts) {\n\t\tconst tag = normalizeTag(concept);\n\t\tif (tag) tags.push(tag);\n\t}\n\n\tfor (const filePath of [...input.filesRead, ...input.filesModified]) {\n\t\ttags.push(...fileTags(filePath));\n\t}\n\n\tif (tags.length === 0 && input.title.trim()) {\n\t\tconst tokens = input.title.toLowerCase().match(/[a-z0-9_]+/g) ?? [];\n\t\tfor (const token of tokens) {\n\t\t\tconst tag = normalizeTag(token);\n\t\t\tif (tag) tags.push(tag);\n\t\t}\n\t}\n\n\tconst deduped: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const tag of tags) {\n\t\tif (seen.has(tag)) continue;\n\t\tseen.add(tag);\n\t\tdeduped.push(tag);\n\t\tif (deduped.length >= 20) break;\n\t}\n\treturn deduped;\n}\n\n/**\n * Populate memory_items.tags_text for rows where it is empty.\n * Port of Python's backfill_tags_text() maintenance helper.\n */\nexport function backfillTagsText(\n\tdb: Database,\n\topts: BackfillTagsTextOptions = {},\n): BackfillTagsTextResult {\n\tconst { limit, since, project, activeOnly = true, dryRun = false, memoryIds } = opts;\n\n\tconst params: unknown[] = [];\n\tconst whereClauses = [\"(memory_items.tags_text IS NULL OR TRIM(memory_items.tags_text) = '')\"];\n\n\tif (activeOnly) whereClauses.push(\"memory_items.active = 1\");\n\tif (since) {\n\t\twhereClauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(since);\n\t}\n\n\tlet joinSessions = false;\n\tif (project) {\n\t\tconst pc = projectClause(project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t\tjoinSessions = true;\n\t\t}\n\t}\n\n\tif (memoryIds && memoryIds.length > 0) {\n\t\tconst placeholders = memoryIds.map(() => \"?\").join(\",\");\n\t\twhereClauses.push(`memory_items.id IN (${placeholders})`);\n\t\tparams.push(...memoryIds.map((id) => Number(id)));\n\t}\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\tconst limitClause = limit != null && limit > 0 ? \"LIMIT ?\" : \"\";\n\tif (limit != null && limit > 0) params.push(limit);\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.id, memory_items.kind, memory_items.title,\n\t\t\t memory_items.concepts, memory_items.files_read, memory_items.files_modified\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${where}\n\t\t\t ORDER BY memory_items.created_at ASC\n\t\t\t ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{\n\t\tid: number;\n\t\tkind: string | null;\n\t\ttitle: string | null;\n\t\tconcepts: string | null;\n\t\tfiles_read: string | null;\n\t\tfiles_modified: string | null;\n\t}>;\n\n\tlet checked = 0;\n\tlet updated = 0;\n\tlet skipped = 0;\n\tconst now = new Date().toISOString();\n\tconst updateStmt = db.prepare(\n\t\t\"UPDATE memory_items SET tags_text = ?, updated_at = ? WHERE id = ?\",\n\t);\n\tconst updates: Array<{ id: number; tagsText: string }> = [];\n\n\tfor (const row of rows) {\n\t\tchecked += 1;\n\t\tconst tags = deriveTags({\n\t\t\tkind: String(row.kind ?? \"\"),\n\t\t\ttitle: String(row.title ?? \"\"),\n\t\t\tconcepts: parseJsonStringList(row.concepts),\n\t\t\tfilesRead: parseJsonStringList(row.files_read),\n\t\t\tfilesModified: parseJsonStringList(row.files_modified),\n\t\t});\n\t\tconst tagsText = tags.join(\" \");\n\t\tif (!tagsText) {\n\t\t\tskipped += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tupdates.push({ id: row.id, tagsText });\n\t\tupdated += 1;\n\t}\n\n\tif (!dryRun && updates.length > 0) {\n\t\tdb.transaction(() => {\n\t\t\tfor (const update of updates) {\n\t\t\t\tupdateStmt.run(update.tagsText, now, update.id);\n\t\t\t}\n\t\t})();\n\t}\n\n\treturn { checked, updated, skipped };\n}\n\nexport interface DeactivateLowSignalResult {\n\tchecked: number;\n\tdeactivated: number;\n}\n\nexport interface DeactivateLowSignalMemoriesOptions {\n\tkinds?: string[] | null;\n\tlimit?: number | null;\n\tdryRun?: boolean;\n}\n\nconst DEFAULT_LOW_SIGNAL_KINDS = [\n\t\"observation\",\n\t\"discovery\",\n\t\"change\",\n\t\"feature\",\n\t\"bugfix\",\n\t\"refactor\",\n\t\"decision\",\n\t\"note\",\n\t\"entities\",\n\t\"session_summary\",\n];\n\nconst OBSERVATION_EQUIVALENT_KINDS = [\n\t\"observation\",\n\t\"bugfix\",\n\t\"feature\",\n\t\"refactor\",\n\t\"change\",\n\t\"discovery\",\n\t\"decision\",\n\t\"exploration\",\n];\n\n/**\n * Deactivate low-signal observations only.\n */\nexport function deactivateLowSignalObservations(\n\tdb: Database,\n\tlimit?: number | null,\n\tdryRun = false,\n): DeactivateLowSignalResult {\n\treturn deactivateLowSignalMemories(db, {\n\t\tkinds: OBSERVATION_EQUIVALENT_KINDS,\n\t\tlimit,\n\t\tdryRun,\n\t});\n}\n\n/**\n * Deactivate low-signal memories across selected kinds (does not delete rows).\n */\nexport function deactivateLowSignalMemories(\n\tdb: Database,\n\topts: DeactivateLowSignalMemoriesOptions = {},\n): DeactivateLowSignalResult {\n\tconst selectedKinds =\n\t\topts.kinds?.map((kind) => kind.trim()).filter((kind) => kind.length > 0) ?? [];\n\tconst kinds = selectedKinds.length > 0 ? selectedKinds : DEFAULT_LOW_SIGNAL_KINDS;\n\tconst placeholders = kinds.map(() => \"?\").join(\",\");\n\tconst params: unknown[] = [...kinds];\n\tlet limitClause = \"\";\n\tif (opts.limit != null && opts.limit > 0) {\n\t\tlimitClause = \"LIMIT ?\";\n\t\tparams.push(opts.limit);\n\t}\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT id, title, body_text\n\t\t\t FROM memory_items\n\t\t\t WHERE kind IN (${placeholders}) AND active = 1\n\t\t\t ORDER BY id DESC\n\t\t\t ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{ id: number; title: string | null; body_text: string | null }>;\n\n\tconst checked = rows.length;\n\tconst ids = rows\n\t\t.filter((row) => isLowSignalObservation(row.body_text || row.title || \"\"))\n\t\t.map((row) => Number(row.id));\n\n\tif (ids.length === 0 || opts.dryRun === true) {\n\t\treturn { checked, deactivated: ids.length };\n\t}\n\n\tconst now = new Date().toISOString();\n\tconst chunkSize = 200;\n\tfor (let start = 0; start < ids.length; start += chunkSize) {\n\t\tconst chunk = ids.slice(start, start + chunkSize);\n\t\tconst chunkPlaceholders = chunk.map(() => \"?\").join(\",\");\n\t\tdb.prepare(\n\t\t\t`UPDATE memory_items SET active = 0, updated_at = ? WHERE id IN (${chunkPlaceholders})`,\n\t\t).run(now, ...chunk);\n\t}\n\n\treturn { checked, deactivated: ids.length };\n}\n","/**\n * Sync HTTP client: JSON request helper using Node.js built-in fetch.\n *\n * Async counterpart to the synchronous Python http.client implementation.\n * Ported from codemem/sync/http_client.py.\n */\n\n// ---------------------------------------------------------------------------\n// URL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a peer address into a base URL.\n *\n * Adds `http://` when no scheme is present, trims whitespace and trailing slashes.\n */\nexport function buildBaseUrl(address: string): string {\n\tconst trimmed = address.trim().replace(/\\/+$/, \"\");\n\tif (!trimmed) return \"\";\n\t// Check for an existing scheme (e.g. http://, https://)\n\tif (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(trimmed)) return trimmed;\n\treturn `http://${trimmed}`;\n}\n\n// ---------------------------------------------------------------------------\n// JSON request\n// ---------------------------------------------------------------------------\n\nexport interface RequestJsonOptions {\n\theaders?: Record<string, string>;\n\tbody?: Record<string, unknown>;\n\tbodyBytes?: Uint8Array;\n\ttimeoutS?: number;\n}\n\n/**\n * Send an HTTP request and parse the JSON response.\n *\n * Returns `[statusCode, parsedBody]`. The body is null when the response\n * has no content. Non-JSON responses are returned as `{ error: \"non_json_response: ...\" }`.\n */\nexport async function requestJson(\n\tmethod: string,\n\turl: string,\n\toptions: RequestJsonOptions = {},\n): Promise<[status: number, body: Record<string, unknown> | null]> {\n\tconst { headers, body, timeoutS = 3 } = options;\n\tlet { bodyBytes } = options;\n\n\tif (bodyBytes == null && body != null) {\n\t\tbodyBytes = new TextEncoder().encode(JSON.stringify(body));\n\t}\n\n\tconst requestHeaders: Record<string, string> = {\n\t\tAccept: \"application/json\",\n\t};\n\tif (bodyBytes != null) {\n\t\trequestHeaders[\"Content-Type\"] = \"application/json\";\n\t\trequestHeaders[\"Content-Length\"] = String(bodyBytes.byteLength);\n\t}\n\tif (headers) {\n\t\tObject.assign(requestHeaders, headers);\n\t}\n\n\tconst response = await fetch(url, {\n\t\tmethod,\n\t\theaders: requestHeaders,\n\t\tbody: bodyBytes ?? null,\n\t\tsignal: AbortSignal.timeout(timeoutS * 1000),\n\t});\n\n\tconst raw = await response.text();\n\tif (!raw) return [response.status, null];\n\n\tlet payload: unknown;\n\ttry {\n\t\tpayload = JSON.parse(raw);\n\t} catch {\n\t\tconst snippet = raw.slice(0, 240).trim();\n\t\treturn [\n\t\t\tresponse.status,\n\t\t\t{ error: snippet ? `non_json_response: ${snippet}` : \"non_json_response\" },\n\t\t];\n\t}\n\n\tif (typeof payload === \"object\" && payload !== null && !Array.isArray(payload)) {\n\t\treturn [response.status, payload as Record<string, unknown>];\n\t}\n\treturn [\n\t\tresponse.status,\n\t\t{ error: `unexpected_json_type: ${Array.isArray(payload) ? \"array\" : typeof payload}` },\n\t];\n}\n","/**\n * Device identity management for the codemem sync system.\n *\n * Handles Ed25519 keypair generation, fingerprinting, and optional\n * keychain storage. Ported from codemem/sync_identity.py.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport {\n\tcreateHash,\n\tcreatePrivateKey,\n\tcreatePublicKey,\n\tgenerateKeyPairSync,\n\trandomUUID,\n} from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { eq } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { connect as connectDb, resolveDbPath } from \"./db.js\";\nimport * as schema from \"./schema.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_KEYS_DIR = join(homedir(), \".config\", \"codemem\", \"keys\");\nconst PRIVATE_KEY_NAME = \"device.key\";\nconst PUBLIC_KEY_NAME = \"device.key.pub\";\nconst KEYCHAIN_SERVICE = \"codemem-sync\";\n\n// ---------------------------------------------------------------------------\n// Fingerprint\n// ---------------------------------------------------------------------------\n\n/** SHA-256 hex digest of a public key string. */\nexport function fingerprintPublicKey(publicKey: string): string {\n\treturn createHash(\"sha256\").update(publicKey, \"utf-8\").digest(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Key store mode\n// ---------------------------------------------------------------------------\n\nfunction keyStoreMode(): \"file\" | \"keychain\" {\n\tconst env = process.env.CODEMEM_SYNC_KEY_STORE?.toLowerCase();\n\tif (env === \"keychain\") return \"keychain\";\n\treturn \"file\";\n}\n\n// ---------------------------------------------------------------------------\n// CLI availability helpers\n// ---------------------------------------------------------------------------\n\nfunction cliAvailable(cmd: string): boolean {\n\ttry {\n\t\texecFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Keychain storage (platform-specific)\n// ---------------------------------------------------------------------------\n\nfunction warnKeychainLimitations(): void {\n\tif (process.platform !== \"darwin\") return;\n\tif (keyStoreMode() !== \"keychain\") return;\n\tif (process.env.CODEMEM_SYNC_KEYCHAIN_WARN === \"0\") return;\n\tconsole.warn(\n\t\t\"[codemem] keychain storage on macOS uses the `security` CLI and may expose the key in process arguments.\",\n\t);\n}\n\n/** Store a private key in the OS keychain. Returns true on success. */\nexport function storePrivateKeyKeychain(privateKey: Buffer, deviceId: string): boolean {\n\tif (process.platform === \"linux\") {\n\t\tif (!cliAvailable(\"secret-tool\")) return false;\n\t\ttry {\n\t\t\texecFileSync(\n\t\t\t\t\"secret-tool\",\n\t\t\t\t[\"store\", \"--label\", \"codemem sync key\", \"service\", KEYCHAIN_SERVICE, \"account\", deviceId],\n\t\t\t\t{ input: privateKey, stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (process.platform === \"darwin\") {\n\t\tif (!cliAvailable(\"security\")) return false;\n\t\ttry {\n\t\t\texecFileSync(\n\t\t\t\t\"security\",\n\t\t\t\t[\n\t\t\t\t\t\"add-generic-password\",\n\t\t\t\t\t\"-a\",\n\t\t\t\t\tdeviceId,\n\t\t\t\t\t\"-s\",\n\t\t\t\t\tKEYCHAIN_SERVICE,\n\t\t\t\t\t\"-w\",\n\t\t\t\t\tprivateKey.toString(\"utf-8\"),\n\t\t\t\t\t\"-U\",\n\t\t\t\t],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\n/** Load a private key from the OS keychain. Returns null if unavailable. */\nexport function loadPrivateKeyKeychain(deviceId: string): Buffer | null {\n\tif (process.platform === \"linux\") {\n\t\tif (!cliAvailable(\"secret-tool\")) return null;\n\t\ttry {\n\t\t\tconst out = execFileSync(\n\t\t\t\t\"secret-tool\",\n\t\t\t\t[\"lookup\", \"service\", KEYCHAIN_SERVICE, \"account\", deviceId],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn Buffer.from(out);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tif (process.platform === \"darwin\") {\n\t\tif (!cliAvailable(\"security\")) return null;\n\t\ttry {\n\t\t\tconst out = execFileSync(\n\t\t\t\t\"security\",\n\t\t\t\t[\"find-generic-password\", \"-a\", deviceId, \"-s\", KEYCHAIN_SERVICE, \"-w\"],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn Buffer.from(out);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Key path resolution\n// ---------------------------------------------------------------------------\n\n/** Resolve private and public key file paths. */\nexport function resolveKeyPaths(keysDir?: string): [string, string] {\n\tconst dir = keysDir ?? DEFAULT_KEYS_DIR;\n\treturn [join(dir, PRIVATE_KEY_NAME), join(dir, PUBLIC_KEY_NAME)];\n}\n\n// ---------------------------------------------------------------------------\n// Key file I/O\n// ---------------------------------------------------------------------------\n\n/** Read the public key from disk. Returns null if missing or empty. */\nexport function loadPublicKey(keysDir?: string): string | null {\n\tconst [, publicPath] = resolveKeyPaths(keysDir);\n\tif (!existsSync(publicPath)) return null;\n\tconst content = readFileSync(publicPath, \"utf-8\").trim();\n\treturn content || null;\n}\n\n/** Read the private key from disk (with keychain fallback). Returns null if unavailable. */\nexport function loadPrivateKey(keysDir?: string, dbPath?: string): Buffer | null {\n\tif (keyStoreMode() === \"keychain\") {\n\t\tconst deviceId = loadDeviceId(dbPath);\n\t\tif (deviceId) {\n\t\t\tconst keychainValue = loadPrivateKeyKeychain(deviceId);\n\t\t\tif (keychainValue) return keychainValue;\n\t\t}\n\t}\n\tconst [privatePath] = resolveKeyPaths(keysDir);\n\tif (!existsSync(privatePath)) return null;\n\treturn readFileSync(privatePath);\n}\n\n/** Load device_id from the database (for keychain lookups). */\nfunction loadDeviceId(dbPath?: string): string | null {\n\tconst path = resolveDbPath(dbPath);\n\tif (!existsSync(path)) return null;\n\tconst conn = connectDb(path);\n\ttry {\n\t\tconst d = drizzle(conn, { schema });\n\t\tconst row = d\n\t\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t\t.from(schema.syncDevice)\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\treturn row?.device_id ?? null;\n\t} finally {\n\t\tconn.close();\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Key generation & validation\n// ---------------------------------------------------------------------------\n\nfunction publicKeyLooksValid(publicKey: string): boolean {\n\tconst value = publicKey.trim();\n\treturn (\n\t\tvalue.startsWith(\"ssh-ed25519 \") || value.startsWith(\"ssh-rsa \") || value.startsWith(\"ecdsa-\")\n\t);\n}\n\nfunction backupInvalidKeyFile(path: string, stamp: string): void {\n\tif (!existsSync(path)) return;\n\tconst backupPath = path.replace(/([^/]+)$/, `$1.invalid-${stamp}`);\n\trenameSync(path, backupPath);\n}\n\n/**\n * Generate an Ed25519 keypair using Node's native crypto.\n * Stores private key as PEM (PKCS8), public key as SSH format for compatibility.\n */\nexport function generateKeypair(privatePath: string, publicPath: string): void {\n\tmkdirSync(dirname(privatePath), { recursive: true });\n\tif (existsSync(privatePath) && existsSync(publicPath)) return;\n\n\tif (existsSync(privatePath) && !existsSync(publicPath)) {\n\t\t// Private key exists but public is missing — derive public from private\n\t\ttry {\n\t\t\tconst privKeyObj = loadPrivateKeyObject(privatePath);\n\t\t\tconst pubKeyObj = createPublicKey(privKeyObj);\n\t\t\tconst sshPub = pubKeyObj.export({ type: \"spki\", format: \"der\" });\n\t\t\tconst sshPubStr = derToSshEd25519(sshPub);\n\t\t\tif (sshPubStr && publicKeyLooksValid(sshPubStr)) {\n\t\t\t\twriteFileSync(publicPath, `${sshPubStr}\\n`, { mode: 0o644 });\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Derivation failed — fall through to full regeneration with backup\n\t\t}\n\t\t// Back up the orphaned private key before regenerating\n\t\tconst stamp = new Date().toISOString().replace(/[:.]/g, \"\");\n\t\trenameSync(privatePath, `${privatePath}.orphan-${stamp}`);\n\t}\n\n\tconst { publicKey, privateKey } = generateKeyPairSync(\"ed25519\");\n\n\t// Export private key as PEM (PKCS8)\n\tconst privatePem = privateKey.export({ type: \"pkcs8\", format: \"pem\" }) as string;\n\twriteFileSync(privatePath, privatePem, { mode: 0o600 });\n\n\t// Export public key as SSH wire format: \"ssh-ed25519 <base64>\"\n\tconst pubDer = publicKey.export({ type: \"spki\", format: \"der\" });\n\tconst sshPub = derToSshEd25519(pubDer);\n\tif (!sshPub) {\n\t\tthrow new Error(\"failed to convert public key to SSH format\");\n\t}\n\twriteFileSync(publicPath, `${sshPub}\\n`, { mode: 0o644 });\n}\n\n/**\n * Convert a DER-encoded SPKI Ed25519 public key to SSH wire format.\n * The last 32 bytes of the DER are the raw Ed25519 public key.\n */\nfunction derToSshEd25519(spkiDer: Buffer): string | null {\n\t// Ed25519 SPKI DER is 44 bytes: 12-byte header + 32-byte key\n\tif (spkiDer.length < 32) return null;\n\tconst rawKey = spkiDer.subarray(spkiDer.length - 32);\n\n\t// SSH wire format: string \"ssh-ed25519\" + string <32-byte key>\n\tconst keyType = Buffer.from(\"ssh-ed25519\");\n\tconst buf = Buffer.alloc(4 + keyType.length + 4 + rawKey.length);\n\tlet offset = 0;\n\tbuf.writeUInt32BE(keyType.length, offset);\n\toffset += 4;\n\tkeyType.copy(buf, offset);\n\toffset += keyType.length;\n\tbuf.writeUInt32BE(rawKey.length, offset);\n\toffset += 4;\n\trawKey.copy(buf, offset);\n\n\treturn `ssh-ed25519 ${buf.toString(\"base64\")}`;\n}\n\n/**\n * Load a private key that may be in OpenSSH format (existing keys) or\n * PKCS8 PEM format (newly generated keys). Node's createPrivateKey\n * handles both transparently.\n */\nfunction loadPrivateKeyObject(privatePath: string): ReturnType<typeof createPrivateKey> {\n\tconst raw = readFileSync(privatePath);\n\t// Try PKCS8 PEM first (our generated format), then OpenSSH\n\ttry {\n\t\treturn createPrivateKey(raw);\n\t} catch {\n\t\treturn createPrivateKey({ key: raw, format: \"pem\", type: \"pkcs8\" });\n\t}\n}\n\n/** Validate that an existing keypair is consistent. */\nexport function validateExistingKeypair(privatePath: string, publicPath: string): boolean {\n\tif (!existsSync(privatePath) || !existsSync(publicPath)) return false;\n\tconst publicKey = readFileSync(publicPath, \"utf-8\").trim();\n\tif (!publicKey || !publicKeyLooksValid(publicKey)) return false;\n\ttry {\n\t\tconst privKeyObj = loadPrivateKeyObject(privatePath);\n\t\tconst pubKeyObj = createPublicKey(privKeyObj);\n\t\tconst pubDer = pubKeyObj.export({ type: \"spki\", format: \"der\" });\n\t\tconst derived = derToSshEd25519(pubDer);\n\t\tif (!derived || !publicKeyLooksValid(derived)) return false;\n\t\t// Auto-fix mismatched public key file\n\t\tif (derived !== publicKey) {\n\t\t\twriteFileSync(publicPath, `${derived}\\n`, \"utf-8\");\n\t\t}\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Main entry: ensure device identity\n// ---------------------------------------------------------------------------\n\nexport interface EnsureDeviceIdentityOptions {\n\tkeysDir?: string;\n\tdeviceId?: string;\n}\n\n/**\n * Ensure this device has a keypair and a row in the sync_device table.\n *\n * Returns [deviceId, fingerprint]. Creates keys and DB row if missing,\n * validates and repairs if existing.\n */\nexport function ensureDeviceIdentity(\n\tdb: Database,\n\toptions?: EnsureDeviceIdentityOptions,\n): [string, string] {\n\tconst keysDir = options?.keysDir ?? DEFAULT_KEYS_DIR;\n\tconst [privatePath, publicPath] = resolveKeyPaths(keysDir);\n\twarnKeychainLimitations();\n\n\t// Check for existing device row\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({\n\t\t\tdevice_id: schema.syncDevice.device_id,\n\t\t\tpublic_key: schema.syncDevice.public_key,\n\t\t\tfingerprint: schema.syncDevice.fingerprint,\n\t\t})\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst existingDeviceId = row?.device_id ?? \"\";\n\tconst existingPublicKey = row?.public_key ?? \"\";\n\tconst existingFingerprint = row?.fingerprint ?? \"\";\n\n\t// Validate or regenerate keys\n\tlet keysReady = existsSync(privatePath) && existsSync(publicPath);\n\tif (keysReady && !validateExistingKeypair(privatePath, publicPath)) {\n\t\tconst stamp = new Date()\n\t\t\t.toISOString()\n\t\t\t.replace(/[-:T.Z]/g, \"\")\n\t\t\t.slice(0, 20);\n\t\tbackupInvalidKeyFile(privatePath, stamp);\n\t\tbackupInvalidKeyFile(publicPath, stamp);\n\t\tkeysReady = false;\n\t}\n\tif (!keysReady) {\n\t\tgenerateKeypair(privatePath, publicPath);\n\t}\n\n\tconst publicKey = readFileSync(publicPath, \"utf-8\").trim();\n\tif (!publicKey) {\n\t\tthrow new Error(\"public key missing\");\n\t}\n\tconst fingerprint = fingerprintPublicKey(publicKey);\n\tconst now = new Date().toISOString();\n\n\t// Update existing device row if keys changed\n\tif (existingDeviceId) {\n\t\tif (existingPublicKey !== publicKey || existingFingerprint !== fingerprint) {\n\t\t\td.update(schema.syncDevice)\n\t\t\t\t.set({ public_key: publicKey, fingerprint })\n\t\t\t\t.where(eq(schema.syncDevice.device_id, existingDeviceId))\n\t\t\t\t.run();\n\t\t}\n\t\tif (keyStoreMode() === \"keychain\") {\n\t\t\t// Read key from file directly — don't go through loadPrivateKey\n\t\t\t// which would resolve device ID from the default DB\n\t\t\tconst privateKey = existsSync(privatePath)\n\t\t\t\t? readFileSync(privatePath)\n\t\t\t\t: loadPrivateKeyKeychain(existingDeviceId);\n\t\t\tif (privateKey) {\n\t\t\t\tstorePrivateKeyKeychain(privateKey, existingDeviceId);\n\t\t\t}\n\t\t}\n\t\treturn [existingDeviceId, fingerprint];\n\t}\n\n\t// Insert new device row\n\tconst resolvedDeviceId = options?.deviceId ?? randomUUID();\n\td.insert(schema.syncDevice)\n\t\t.values({ device_id: resolvedDeviceId, public_key: publicKey, fingerprint, created_at: now })\n\t\t.run();\n\tif (keyStoreMode() === \"keychain\") {\n\t\tconst privateKey = existsSync(privatePath) ? readFileSync(privatePath) : null;\n\t\tif (privateKey) {\n\t\t\tstorePrivateKeyKeychain(privateKey, resolvedDeviceId);\n\t\t}\n\t}\n\treturn [resolvedDeviceId, fingerprint];\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n\tdecodeInvitePayload,\n\tencodeInvitePayload,\n\textractInvitePayload,\n\ttype InvitePayload,\n\tinviteLink,\n} from \"./coordinator-invites.js\";\nimport { CoordinatorStore, DEFAULT_COORDINATOR_DB_PATH } from \"./coordinator-store.js\";\nimport { connect } from \"./db.js\";\nimport { initDatabase } from \"./maintenance.js\";\nimport { readCodememConfigFile, writeCodememConfigFile } from \"./observer-config.js\";\nimport { requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity, loadPublicKey } from \"./sync-identity.js\";\n\nconst VALID_INVITE_POLICIES = new Set([\"auto_admit\", \"approval_required\"]);\n\nfunction coordinatorRemoteTarget(config = readCodememConfigFile()): {\n\tremoteUrl: string | null;\n\tadminSecret: string | null;\n} {\n\tconst remoteUrl = String(config.sync_coordinator_url ?? \"\").trim() || null;\n\tconst adminSecret = remoteUrl\n\t\t? String(\n\t\t\t\tprocess.env.CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET ??\n\t\t\t\t\tconfig.sync_coordinator_admin_secret ??\n\t\t\t\t\t\"\",\n\t\t\t).trim() || null\n\t\t: null;\n\treturn { remoteUrl, adminSecret };\n}\n\nasync function remoteRequest(\n\tmethod: string,\n\turl: string,\n\tadminSecret: string,\n\tbody?: Record<string, unknown>,\n): Promise<Record<string, unknown> | null> {\n\tconst [status, payload] = await requestJson(method, url, {\n\t\theaders: { \"X-Codemem-Coordinator-Admin\": adminSecret },\n\t\tbody,\n\t\ttimeoutS: 3,\n\t});\n\tif (status < 200 || status >= 300) {\n\t\tconst detail = typeof payload?.error === \"string\" ? payload.error : \"unknown\";\n\t\tthrow new Error(`Remote coordinator request failed (${status}): ${detail}`);\n\t}\n\treturn payload;\n}\n\nexport async function coordinatorCreateInviteAction(opts: {\n\tgroupId: string;\n\tcoordinatorUrl?: string | null;\n\tpolicy: string;\n\tttlHours: number;\n\tcreatedBy?: string | null;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown>> {\n\tif (!VALID_INVITE_POLICIES.has(opts.policy)) throw new Error(`Invalid policy: ${opts.policy}`);\n\tconst expiresAt = new Date(Date.now() + opts.ttlHours * 3600 * 1000).toISOString();\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret)\n\t\t\tthrow new Error(\"Admin secret required to create invites via the coordinator API.\");\n\t\tconst payload = await remoteRequest(\n\t\t\t\"POST\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}/v1/admin/invites`,\n\t\t\tadminSecret,\n\t\t\t{\n\t\t\t\tgroup_id: opts.groupId,\n\t\t\t\tpolicy: opts.policy,\n\t\t\t\texpires_at: expiresAt,\n\t\t\t\tcreated_by: opts.createdBy ?? null,\n\t\t\t\tcoordinator_url: opts.coordinatorUrl || remote,\n\t\t\t},\n\t\t);\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tencoded: payload?.encoded,\n\t\t\tlink: payload?.link,\n\t\t\tpayload: payload?.payload,\n\t\t\tmode: \"remote\",\n\t\t};\n\t}\n\tconst resolvedCoordinatorUrl = String(\n\t\topts.coordinatorUrl ?? readCodememConfigFile().sync_coordinator_url ?? \"\",\n\t).trim();\n\tif (!resolvedCoordinatorUrl) throw new Error(\"Coordinator URL required.\");\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\tconst group = store.getGroup(opts.groupId);\n\t\tif (!group) throw new Error(`Group not found: ${opts.groupId}`);\n\t\tconst invite = store.createInvite({\n\t\t\tgroupId: opts.groupId,\n\t\t\tpolicy: opts.policy,\n\t\t\texpiresAt,\n\t\t\tcreatedBy: opts.createdBy ?? null,\n\t\t});\n\t\tconst payload: InvitePayload = {\n\t\t\tv: 1,\n\t\t\tkind: \"coordinator_team_invite\",\n\t\t\tcoordinator_url: resolvedCoordinatorUrl,\n\t\t\tgroup_id: opts.groupId,\n\t\t\tpolicy: opts.policy,\n\t\t\ttoken: String(invite.token ?? \"\"),\n\t\t\texpires_at: expiresAt,\n\t\t\tteam_name: (invite.team_name_snapshot as string) ?? null,\n\t\t};\n\t\tconst encoded = encodeInvitePayload(payload);\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tencoded,\n\t\t\tlink: inviteLink(encoded),\n\t\t\tpayload,\n\t\t\tmode: \"local\",\n\t\t};\n\t} finally {\n\t\tstore.close();\n\t}\n}\n\nexport async function coordinatorImportInviteAction(opts: {\n\tinviteValue: string;\n\tdbPath?: string | null;\n\tkeysDir?: string | null;\n\tconfigPath?: string | null;\n}): Promise<Record<string, unknown>> {\n\tconst payload = decodeInvitePayload(extractInvitePayload(opts.inviteValue));\n\tconst resolvedDbPath = opts.dbPath ?? join(homedir(), \".codemem\", \"mem.sqlite\");\n\tinitDatabase(resolvedDbPath);\n\tconst conn = connect(resolvedDbPath);\n\tlet deviceId = \"\";\n\tlet fingerprint = \"\";\n\ttry {\n\t\t[deviceId, fingerprint] = ensureDeviceIdentity(conn, { keysDir: opts.keysDir ?? undefined });\n\t} finally {\n\t\tconn.close();\n\t}\n\tconst publicKey = loadPublicKey(opts.keysDir ?? undefined);\n\tif (!publicKey) throw new Error(\"public key missing\");\n\tconst coordinatorUrl = String(payload.coordinator_url ?? \"\").trim();\n\tif (!coordinatorUrl) throw new Error(\"Invite is missing a coordinator URL.\");\n\tconst config = readCodememConfigFile();\n\tconst displayName = String(config.actor_display_name ?? deviceId).trim() || deviceId;\n\tconst [status, response] = await requestJson(\n\t\t\"POST\",\n\t\t`${coordinatorUrl.replace(/\\/+$/, \"\")}/v1/join`,\n\t\t{\n\t\t\tbody: {\n\t\t\t\ttoken: String(payload.token),\n\t\t\t\tdevice_id: deviceId,\n\t\t\t\tpublic_key: publicKey,\n\t\t\t\tfingerprint,\n\t\t\t\tdisplay_name: displayName,\n\t\t\t},\n\t\t\ttimeoutS: 3,\n\t\t},\n\t);\n\tif (status < 200 || status >= 300) {\n\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\tthrow new Error(`Invite import failed (${status}): ${detail}`);\n\t}\n\tconst nextConfig = readCodememConfigFile();\n\tnextConfig.sync_coordinator_url = coordinatorUrl;\n\tnextConfig.sync_coordinator_group = String(payload.group_id);\n\tconst configPath = writeCodememConfigFile(nextConfig, opts.configPath ?? undefined);\n\treturn {\n\t\tgroup_id: payload.group_id,\n\t\tcoordinator_url: payload.coordinator_url,\n\t\tstatus: response?.status ?? null,\n\t\tconfig_path: configPath,\n\t};\n}\n\nexport async function coordinatorListJoinRequestsAction(opts: {\n\tgroupId: string;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown>[]> {\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret) throw new Error(\"Admin secret required.\");\n\t\tconst payload = await remoteRequest(\n\t\t\t\"GET\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}/v1/admin/join-requests?group_id=${encodeURIComponent(opts.groupId)}`,\n\t\t\tadminSecret,\n\t\t);\n\t\treturn Array.isArray(payload?.items)\n\t\t\t? payload.items.filter(\n\t\t\t\t\t(row): row is Record<string, unknown> => Boolean(row) && typeof row === \"object\",\n\t\t\t\t)\n\t\t\t: [];\n\t}\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\treturn store.listJoinRequests(opts.groupId);\n\t} finally {\n\t\tstore.close();\n\t}\n}\n\nexport async function coordinatorReviewJoinRequestAction(opts: {\n\trequestId: string;\n\tapprove: boolean;\n\treviewedBy?: string | null;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown> | null> {\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret) throw new Error(\"Admin secret required.\");\n\t\tconst endpoint = opts.approve\n\t\t\t? \"/v1/admin/join-requests/approve\"\n\t\t\t: \"/v1/admin/join-requests/deny\";\n\t\tconst payload = await remoteRequest(\n\t\t\t\"POST\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}${endpoint}`,\n\t\t\tadminSecret,\n\t\t\t{\n\t\t\t\trequest_id: opts.requestId,\n\t\t\t\treviewed_by: opts.reviewedBy ?? null,\n\t\t\t},\n\t\t);\n\t\tconst request = payload?.request;\n\t\treturn request && typeof request === \"object\" ? (request as Record<string, unknown>) : null;\n\t}\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\treturn store.reviewJoinRequest({\n\t\t\trequestId: opts.requestId,\n\t\t\tapproved: opts.approve,\n\t\t\treviewedBy: opts.reviewedBy ?? null,\n\t\t});\n\t} finally {\n\t\tstore.close();\n\t}\n}\n","// src/compose.ts\nvar compose = (middleware, onError, onNotFound) => {\n return (context, next) => {\n let index = -1;\n return dispatch(0);\n async function dispatch(i) {\n if (i <= index) {\n throw new Error(\"next() called multiple times\");\n }\n index = i;\n let res;\n let isError = false;\n let handler;\n if (middleware[i]) {\n handler = middleware[i][0][0];\n context.req.routeIndex = i;\n } else {\n handler = i === middleware.length && next || void 0;\n }\n if (handler) {\n try {\n res = await handler(context, () => dispatch(i + 1));\n } catch (err) {\n if (err instanceof Error && onError) {\n context.error = err;\n res = await onError(err, context);\n isError = true;\n } else {\n throw err;\n }\n }\n } else {\n if (context.finalized === false && onNotFound) {\n res = await onNotFound(context);\n }\n }\n if (res && (context.finalized === false || isError)) {\n context.res = res;\n }\n return context;\n }\n };\n};\nexport {\n compose\n};\n","// src/request/constants.ts\nvar GET_MATCH_RESULT = /* @__PURE__ */ Symbol();\nexport {\n GET_MATCH_RESULT\n};\n","// src/utils/body.ts\nimport { HonoRequest } from \"../request.js\";\nvar parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {\n const { all = false, dot = false } = options;\n const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;\n const contentType = headers.get(\"Content-Type\");\n if (contentType?.startsWith(\"multipart/form-data\") || contentType?.startsWith(\"application/x-www-form-urlencoded\")) {\n return parseFormData(request, { all, dot });\n }\n return {};\n};\nasync function parseFormData(request, options) {\n const formData = await request.formData();\n if (formData) {\n return convertFormDataToBodyData(formData, options);\n }\n return {};\n}\nfunction convertFormDataToBodyData(formData, options) {\n const form = /* @__PURE__ */ Object.create(null);\n formData.forEach((value, key) => {\n const shouldParseAllValues = options.all || key.endsWith(\"[]\");\n if (!shouldParseAllValues) {\n form[key] = value;\n } else {\n handleParsingAllValues(form, key, value);\n }\n });\n if (options.dot) {\n Object.entries(form).forEach(([key, value]) => {\n const shouldParseDotValues = key.includes(\".\");\n if (shouldParseDotValues) {\n handleParsingNestedValues(form, key, value);\n delete form[key];\n }\n });\n }\n return form;\n}\nvar handleParsingAllValues = (form, key, value) => {\n if (form[key] !== void 0) {\n if (Array.isArray(form[key])) {\n ;\n form[key].push(value);\n } else {\n form[key] = [form[key], value];\n }\n } else {\n if (!key.endsWith(\"[]\")) {\n form[key] = value;\n } else {\n form[key] = [value];\n }\n }\n};\nvar handleParsingNestedValues = (form, key, value) => {\n if (/(?:^|\\.)__proto__\\./.test(key)) {\n return;\n }\n let nestedForm = form;\n const keys = key.split(\".\");\n keys.forEach((key2, index) => {\n if (index === keys.length - 1) {\n nestedForm[key2] = value;\n } else {\n if (!nestedForm[key2] || typeof nestedForm[key2] !== \"object\" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {\n nestedForm[key2] = /* @__PURE__ */ Object.create(null);\n }\n nestedForm = nestedForm[key2];\n }\n });\n};\nexport {\n parseBody\n};\n","// src/utils/url.ts\nvar splitPath = (path) => {\n const paths = path.split(\"/\");\n if (paths[0] === \"\") {\n paths.shift();\n }\n return paths;\n};\nvar splitRoutingPath = (routePath) => {\n const { groups, path } = extractGroupsFromPath(routePath);\n const paths = splitPath(path);\n return replaceGroupMarks(paths, groups);\n};\nvar extractGroupsFromPath = (path) => {\n const groups = [];\n path = path.replace(/\\{[^}]+\\}/g, (match, index) => {\n const mark = `@${index}`;\n groups.push([mark, match]);\n return mark;\n });\n return { groups, path };\n};\nvar replaceGroupMarks = (paths, groups) => {\n for (let i = groups.length - 1; i >= 0; i--) {\n const [mark] = groups[i];\n for (let j = paths.length - 1; j >= 0; j--) {\n if (paths[j].includes(mark)) {\n paths[j] = paths[j].replace(mark, groups[i][1]);\n break;\n }\n }\n }\n return paths;\n};\nvar patternCache = {};\nvar getPattern = (label, next) => {\n if (label === \"*\") {\n return \"*\";\n }\n const match = label.match(/^\\:([^\\{\\}]+)(?:\\{(.+)\\})?$/);\n if (match) {\n const cacheKey = `${label}#${next}`;\n if (!patternCache[cacheKey]) {\n if (match[2]) {\n patternCache[cacheKey] = next && next[0] !== \":\" && next[0] !== \"*\" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];\n } else {\n patternCache[cacheKey] = [label, match[1], true];\n }\n }\n return patternCache[cacheKey];\n }\n return null;\n};\nvar tryDecode = (str, decoder) => {\n try {\n return decoder(str);\n } catch {\n return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {\n try {\n return decoder(match);\n } catch {\n return match;\n }\n });\n }\n};\nvar tryDecodeURI = (str) => tryDecode(str, decodeURI);\nvar getPath = (request) => {\n const url = request.url;\n const start = url.indexOf(\"/\", url.indexOf(\":\") + 4);\n let i = start;\n for (; i < url.length; i++) {\n const charCode = url.charCodeAt(i);\n if (charCode === 37) {\n const queryIndex = url.indexOf(\"?\", i);\n const hashIndex = url.indexOf(\"#\", i);\n const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);\n const path = url.slice(start, end);\n return tryDecodeURI(path.includes(\"%25\") ? path.replace(/%25/g, \"%2525\") : path);\n } else if (charCode === 63 || charCode === 35) {\n break;\n }\n }\n return url.slice(start, i);\n};\nvar getQueryStrings = (url) => {\n const queryIndex = url.indexOf(\"?\", 8);\n return queryIndex === -1 ? \"\" : \"?\" + url.slice(queryIndex + 1);\n};\nvar getPathNoStrict = (request) => {\n const result = getPath(request);\n return result.length > 1 && result.at(-1) === \"/\" ? result.slice(0, -1) : result;\n};\nvar mergePath = (base, sub, ...rest) => {\n if (rest.length) {\n sub = mergePath(sub, ...rest);\n }\n return `${base?.[0] === \"/\" ? \"\" : \"/\"}${base}${sub === \"/\" ? \"\" : `${base?.at(-1) === \"/\" ? \"\" : \"/\"}${sub?.[0] === \"/\" ? sub.slice(1) : sub}`}`;\n};\nvar checkOptionalParameter = (path) => {\n if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(\":\")) {\n return null;\n }\n const segments = path.split(\"/\");\n const results = [];\n let basePath = \"\";\n segments.forEach((segment) => {\n if (segment !== \"\" && !/\\:/.test(segment)) {\n basePath += \"/\" + segment;\n } else if (/\\:/.test(segment)) {\n if (/\\?/.test(segment)) {\n if (results.length === 0 && basePath === \"\") {\n results.push(\"/\");\n } else {\n results.push(basePath);\n }\n const optionalSegment = segment.replace(\"?\", \"\");\n basePath += \"/\" + optionalSegment;\n results.push(basePath);\n } else {\n basePath += \"/\" + segment;\n }\n }\n });\n return results.filter((v, i, a) => a.indexOf(v) === i);\n};\nvar _decodeURI = (value) => {\n if (!/[%+]/.test(value)) {\n return value;\n }\n if (value.indexOf(\"+\") !== -1) {\n value = value.replace(/\\+/g, \" \");\n }\n return value.indexOf(\"%\") !== -1 ? tryDecode(value, decodeURIComponent_) : value;\n};\nvar _getQueryParam = (url, key, multiple) => {\n let encoded;\n if (!multiple && key && !/[%+]/.test(key)) {\n let keyIndex2 = url.indexOf(\"?\", 8);\n if (keyIndex2 === -1) {\n return void 0;\n }\n if (!url.startsWith(key, keyIndex2 + 1)) {\n keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);\n }\n while (keyIndex2 !== -1) {\n const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);\n if (trailingKeyCode === 61) {\n const valueIndex = keyIndex2 + key.length + 2;\n const endIndex = url.indexOf(\"&\", valueIndex);\n return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex));\n } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {\n return \"\";\n }\n keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);\n }\n encoded = /[%+]/.test(url);\n if (!encoded) {\n return void 0;\n }\n }\n const results = {};\n encoded ??= /[%+]/.test(url);\n let keyIndex = url.indexOf(\"?\", 8);\n while (keyIndex !== -1) {\n const nextKeyIndex = url.indexOf(\"&\", keyIndex + 1);\n let valueIndex = url.indexOf(\"=\", keyIndex);\n if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {\n valueIndex = -1;\n }\n let name = url.slice(\n keyIndex + 1,\n valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex\n );\n if (encoded) {\n name = _decodeURI(name);\n }\n keyIndex = nextKeyIndex;\n if (name === \"\") {\n continue;\n }\n let value;\n if (valueIndex === -1) {\n value = \"\";\n } else {\n value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex);\n if (encoded) {\n value = _decodeURI(value);\n }\n }\n if (multiple) {\n if (!(results[name] && Array.isArray(results[name]))) {\n results[name] = [];\n }\n ;\n results[name].push(value);\n } else {\n results[name] ??= value;\n }\n }\n return key ? results[key] : results;\n};\nvar getQueryParam = _getQueryParam;\nvar getQueryParams = (url, key) => {\n return _getQueryParam(url, key, true);\n};\nvar decodeURIComponent_ = decodeURIComponent;\nexport {\n checkOptionalParameter,\n decodeURIComponent_,\n getPath,\n getPathNoStrict,\n getPattern,\n getQueryParam,\n getQueryParams,\n getQueryStrings,\n mergePath,\n splitPath,\n splitRoutingPath,\n tryDecode,\n tryDecodeURI\n};\n","// src/request.ts\nimport { HTTPException } from \"./http-exception.js\";\nimport { GET_MATCH_RESULT } from \"./request/constants.js\";\nimport { parseBody } from \"./utils/body.js\";\nimport { decodeURIComponent_, getQueryParam, getQueryParams, tryDecode } from \"./utils/url.js\";\nvar tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);\nvar HonoRequest = class {\n /**\n * `.raw` can get the raw Request object.\n *\n * @see {@link https://hono.dev/docs/api/request#raw}\n *\n * @example\n * ```ts\n * // For Cloudflare Workers\n * app.post('/', async (c) => {\n * const metadata = c.req.raw.cf?.hostMetadata?\n * ...\n * })\n * ```\n */\n raw;\n #validatedData;\n // Short name of validatedData\n #matchResult;\n routeIndex = 0;\n /**\n * `.path` can get the pathname of the request.\n *\n * @see {@link https://hono.dev/docs/api/request#path}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const pathname = c.req.path // `/about/me`\n * })\n * ```\n */\n path;\n bodyCache = {};\n constructor(request, path = \"/\", matchResult = [[]]) {\n this.raw = request;\n this.path = path;\n this.#matchResult = matchResult;\n this.#validatedData = {};\n }\n param(key) {\n return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();\n }\n #getDecodedParam(key) {\n const paramKey = this.#matchResult[0][this.routeIndex][1][key];\n const param = this.#getParamValue(paramKey);\n return param && /\\%/.test(param) ? tryDecodeURIComponent(param) : param;\n }\n #getAllDecodedParams() {\n const decoded = {};\n const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);\n for (const key of keys) {\n const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);\n if (value !== void 0) {\n decoded[key] = /\\%/.test(value) ? tryDecodeURIComponent(value) : value;\n }\n }\n return decoded;\n }\n #getParamValue(paramKey) {\n return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;\n }\n query(key) {\n return getQueryParam(this.url, key);\n }\n queries(key) {\n return getQueryParams(this.url, key);\n }\n header(name) {\n if (name) {\n return this.raw.headers.get(name) ?? void 0;\n }\n const headerData = {};\n this.raw.headers.forEach((value, key) => {\n headerData[key] = value;\n });\n return headerData;\n }\n async parseBody(options) {\n return this.bodyCache.parsedBody ??= await parseBody(this, options);\n }\n #cachedBody = (key) => {\n const { bodyCache, raw } = this;\n const cachedBody = bodyCache[key];\n if (cachedBody) {\n return cachedBody;\n }\n const anyCachedKey = Object.keys(bodyCache)[0];\n if (anyCachedKey) {\n return bodyCache[anyCachedKey].then((body) => {\n if (anyCachedKey === \"json\") {\n body = JSON.stringify(body);\n }\n return new Response(body)[key]();\n });\n }\n return bodyCache[key] = raw[key]();\n };\n /**\n * `.json()` can parse Request body of type `application/json`\n *\n * @see {@link https://hono.dev/docs/api/request#json}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.json()\n * })\n * ```\n */\n json() {\n return this.#cachedBody(\"text\").then((text) => JSON.parse(text));\n }\n /**\n * `.text()` can parse Request body of type `text/plain`\n *\n * @see {@link https://hono.dev/docs/api/request#text}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.text()\n * })\n * ```\n */\n text() {\n return this.#cachedBody(\"text\");\n }\n /**\n * `.arrayBuffer()` parse Request body as an `ArrayBuffer`\n *\n * @see {@link https://hono.dev/docs/api/request#arraybuffer}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.arrayBuffer()\n * })\n * ```\n */\n arrayBuffer() {\n return this.#cachedBody(\"arrayBuffer\");\n }\n /**\n * Parses the request body as a `Blob`.\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.blob();\n * });\n * ```\n * @see https://hono.dev/docs/api/request#blob\n */\n blob() {\n return this.#cachedBody(\"blob\");\n }\n /**\n * Parses the request body as `FormData`.\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.formData();\n * });\n * ```\n * @see https://hono.dev/docs/api/request#formdata\n */\n formData() {\n return this.#cachedBody(\"formData\");\n }\n /**\n * Adds validated data to the request.\n *\n * @param target - The target of the validation.\n * @param data - The validated data to add.\n */\n addValidatedData(target, data) {\n this.#validatedData[target] = data;\n }\n valid(target) {\n return this.#validatedData[target];\n }\n /**\n * `.url()` can get the request url strings.\n *\n * @see {@link https://hono.dev/docs/api/request#url}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const url = c.req.url // `http://localhost:8787/about/me`\n * ...\n * })\n * ```\n */\n get url() {\n return this.raw.url;\n }\n /**\n * `.method()` can get the method name of the request.\n *\n * @see {@link https://hono.dev/docs/api/request#method}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const method = c.req.method // `GET`\n * })\n * ```\n */\n get method() {\n return this.raw.method;\n }\n get [GET_MATCH_RESULT]() {\n return this.#matchResult;\n }\n /**\n * `.matchedRoutes()` can return a matched route in the handler\n *\n * @deprecated\n *\n * Use matchedRoutes helper defined in \"hono/route\" instead.\n *\n * @see {@link https://hono.dev/docs/api/request#matchedroutes}\n *\n * @example\n * ```ts\n * app.use('*', async function logger(c, next) {\n * await next()\n * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {\n * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')\n * console.log(\n * method,\n * ' ',\n * path,\n * ' '.repeat(Math.max(10 - path.length, 0)),\n * name,\n * i === c.req.routeIndex ? '<- respond from here' : ''\n * )\n * })\n * })\n * ```\n */\n get matchedRoutes() {\n return this.#matchResult[0].map(([[, route]]) => route);\n }\n /**\n * `routePath()` can retrieve the path registered within the handler\n *\n * @deprecated\n *\n * Use routePath helper defined in \"hono/route\" instead.\n *\n * @see {@link https://hono.dev/docs/api/request#routepath}\n *\n * @example\n * ```ts\n * app.get('/posts/:id', (c) => {\n * return c.json({ path: c.req.routePath })\n * })\n * ```\n */\n get routePath() {\n return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;\n }\n};\nvar cloneRawRequest = async (req) => {\n if (!req.raw.bodyUsed) {\n return req.raw.clone();\n }\n const cacheKey = Object.keys(req.bodyCache)[0];\n if (!cacheKey) {\n throw new HTTPException(500, {\n message: \"Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly.\"\n });\n }\n const requestInit = {\n body: await req[cacheKey](),\n cache: req.raw.cache,\n credentials: req.raw.credentials,\n headers: req.header(),\n integrity: req.raw.integrity,\n keepalive: req.raw.keepalive,\n method: req.method,\n mode: req.raw.mode,\n redirect: req.raw.redirect,\n referrer: req.raw.referrer,\n referrerPolicy: req.raw.referrerPolicy,\n signal: req.raw.signal\n };\n return new Request(req.url, requestInit);\n};\nexport {\n HonoRequest,\n cloneRawRequest\n};\n","// src/utils/html.ts\nvar HtmlEscapedCallbackPhase = {\n Stringify: 1,\n BeforeStream: 2,\n Stream: 3\n};\nvar raw = (value, callbacks) => {\n const escapedString = new String(value);\n escapedString.isEscaped = true;\n escapedString.callbacks = callbacks;\n return escapedString;\n};\nvar escapeRe = /[&<>'\"]/;\nvar stringBufferToString = async (buffer, callbacks) => {\n let str = \"\";\n callbacks ||= [];\n const resolvedBuffer = await Promise.all(buffer);\n for (let i = resolvedBuffer.length - 1; ; i--) {\n str += resolvedBuffer[i];\n i--;\n if (i < 0) {\n break;\n }\n let r = resolvedBuffer[i];\n if (typeof r === \"object\") {\n callbacks.push(...r.callbacks || []);\n }\n const isEscaped = r.isEscaped;\n r = await (typeof r === \"object\" ? r.toString() : r);\n if (typeof r === \"object\") {\n callbacks.push(...r.callbacks || []);\n }\n if (r.isEscaped ?? isEscaped) {\n str += r;\n } else {\n const buf = [str];\n escapeToBuffer(r, buf);\n str = buf[0];\n }\n }\n return raw(str, callbacks);\n};\nvar escapeToBuffer = (str, buffer) => {\n const match = str.search(escapeRe);\n if (match === -1) {\n buffer[0] += str;\n return;\n }\n let escape;\n let index;\n let lastIndex = 0;\n for (index = match; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34:\n escape = \"&quot;\";\n break;\n case 39:\n escape = \"&#39;\";\n break;\n case 38:\n escape = \"&amp;\";\n break;\n case 60:\n escape = \"&lt;\";\n break;\n case 62:\n escape = \"&gt;\";\n break;\n default:\n continue;\n }\n buffer[0] += str.substring(lastIndex, index) + escape;\n lastIndex = index + 1;\n }\n buffer[0] += str.substring(lastIndex, index);\n};\nvar resolveCallbackSync = (str) => {\n const callbacks = str.callbacks;\n if (!callbacks?.length) {\n return str;\n }\n const buffer = [str];\n const context = {};\n callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));\n return buffer[0];\n};\nvar resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {\n if (typeof str === \"object\" && !(str instanceof String)) {\n if (!(str instanceof Promise)) {\n str = str.toString();\n }\n if (str instanceof Promise) {\n str = await str;\n }\n }\n const callbacks = str.callbacks;\n if (!callbacks?.length) {\n return Promise.resolve(str);\n }\n if (buffer) {\n buffer[0] += str;\n } else {\n buffer = [str];\n }\n const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(\n (res) => Promise.all(\n res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))\n ).then(() => buffer[0])\n );\n if (preserveCallbacks) {\n return raw(await resStr, callbacks);\n } else {\n return resStr;\n }\n};\nexport {\n HtmlEscapedCallbackPhase,\n escapeToBuffer,\n raw,\n resolveCallback,\n resolveCallbackSync,\n stringBufferToString\n};\n","// src/context.ts\nimport { HonoRequest } from \"./request.js\";\nimport { HtmlEscapedCallbackPhase, resolveCallback } from \"./utils/html.js\";\nvar TEXT_PLAIN = \"text/plain; charset=UTF-8\";\nvar setDefaultContentType = (contentType, headers) => {\n return {\n \"Content-Type\": contentType,\n ...headers\n };\n};\nvar createResponseInstance = (body, init) => new Response(body, init);\nvar Context = class {\n #rawRequest;\n #req;\n /**\n * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.\n *\n * @see {@link https://hono.dev/docs/api/context#env}\n *\n * @example\n * ```ts\n * // Environment object for Cloudflare Workers\n * app.get('*', async c => {\n * const counter = c.env.COUNTER\n * })\n * ```\n */\n env = {};\n #var;\n finalized = false;\n /**\n * `.error` can get the error object from the middleware if the Handler throws an error.\n *\n * @see {@link https://hono.dev/docs/api/context#error}\n *\n * @example\n * ```ts\n * app.use('*', async (c, next) => {\n * await next()\n * if (c.error) {\n * // do something...\n * }\n * })\n * ```\n */\n error;\n #status;\n #executionCtx;\n #res;\n #layout;\n #renderer;\n #notFoundHandler;\n #preparedHeaders;\n #matchResult;\n #path;\n /**\n * Creates an instance of the Context class.\n *\n * @param req - The Request object.\n * @param options - Optional configuration options for the context.\n */\n constructor(req, options) {\n this.#rawRequest = req;\n if (options) {\n this.#executionCtx = options.executionCtx;\n this.env = options.env;\n this.#notFoundHandler = options.notFoundHandler;\n this.#path = options.path;\n this.#matchResult = options.matchResult;\n }\n }\n /**\n * `.req` is the instance of {@link HonoRequest}.\n */\n get req() {\n this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult);\n return this.#req;\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#event}\n * The FetchEvent associated with the current request.\n *\n * @throws Will throw an error if the context does not have a FetchEvent.\n */\n get event() {\n if (this.#executionCtx && \"respondWith\" in this.#executionCtx) {\n return this.#executionCtx;\n } else {\n throw Error(\"This context has no FetchEvent\");\n }\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#executionctx}\n * The ExecutionContext associated with the current request.\n *\n * @throws Will throw an error if the context does not have an ExecutionContext.\n */\n get executionCtx() {\n if (this.#executionCtx) {\n return this.#executionCtx;\n } else {\n throw Error(\"This context has no ExecutionContext\");\n }\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#res}\n * The Response object for the current request.\n */\n get res() {\n return this.#res ||= createResponseInstance(null, {\n headers: this.#preparedHeaders ??= new Headers()\n });\n }\n /**\n * Sets the Response object for the current request.\n *\n * @param _res - The Response object to set.\n */\n set res(_res) {\n if (this.#res && _res) {\n _res = createResponseInstance(_res.body, _res);\n for (const [k, v] of this.#res.headers.entries()) {\n if (k === \"content-type\") {\n continue;\n }\n if (k === \"set-cookie\") {\n const cookies = this.#res.headers.getSetCookie();\n _res.headers.delete(\"set-cookie\");\n for (const cookie of cookies) {\n _res.headers.append(\"set-cookie\", cookie);\n }\n } else {\n _res.headers.set(k, v);\n }\n }\n }\n this.#res = _res;\n this.finalized = true;\n }\n /**\n * `.render()` can create a response within a layout.\n *\n * @see {@link https://hono.dev/docs/api/context#render-setrenderer}\n *\n * @example\n * ```ts\n * app.get('/', (c) => {\n * return c.render('Hello!')\n * })\n * ```\n */\n render = (...args) => {\n this.#renderer ??= (content) => this.html(content);\n return this.#renderer(...args);\n };\n /**\n * Sets the layout for the response.\n *\n * @param layout - The layout to set.\n * @returns The layout function.\n */\n setLayout = (layout) => this.#layout = layout;\n /**\n * Gets the current layout for the response.\n *\n * @returns The current layout function.\n */\n getLayout = () => this.#layout;\n /**\n * `.setRenderer()` can set the layout in the custom middleware.\n *\n * @see {@link https://hono.dev/docs/api/context#render-setrenderer}\n *\n * @example\n * ```tsx\n * app.use('*', async (c, next) => {\n * c.setRenderer((content) => {\n * return c.html(\n * <html>\n * <body>\n * <p>{content}</p>\n * </body>\n * </html>\n * )\n * })\n * await next()\n * })\n * ```\n */\n setRenderer = (renderer) => {\n this.#renderer = renderer;\n };\n /**\n * `.header()` can set headers.\n *\n * @see {@link https://hono.dev/docs/api/context#header}\n *\n * @example\n * ```ts\n * app.get('/welcome', (c) => {\n * // Set headers\n * c.header('X-Message', 'Hello!')\n * c.header('Content-Type', 'text/plain')\n *\n * return c.body('Thank you for coming')\n * })\n * ```\n */\n header = (name, value, options) => {\n if (this.finalized) {\n this.#res = createResponseInstance(this.#res.body, this.#res);\n }\n const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers();\n if (value === void 0) {\n headers.delete(name);\n } else if (options?.append) {\n headers.append(name, value);\n } else {\n headers.set(name, value);\n }\n };\n status = (status) => {\n this.#status = status;\n };\n /**\n * `.set()` can set the value specified by the key.\n *\n * @see {@link https://hono.dev/docs/api/context#set-get}\n *\n * @example\n * ```ts\n * app.use('*', async (c, next) => {\n * c.set('message', 'Hono is hot!!')\n * await next()\n * })\n * ```\n */\n set = (key, value) => {\n this.#var ??= /* @__PURE__ */ new Map();\n this.#var.set(key, value);\n };\n /**\n * `.get()` can use the value specified by the key.\n *\n * @see {@link https://hono.dev/docs/api/context#set-get}\n *\n * @example\n * ```ts\n * app.get('/', (c) => {\n * const message = c.get('message')\n * return c.text(`The message is \"${message}\"`)\n * })\n * ```\n */\n get = (key) => {\n return this.#var ? this.#var.get(key) : void 0;\n };\n /**\n * `.var` can access the value of a variable.\n *\n * @see {@link https://hono.dev/docs/api/context#var}\n *\n * @example\n * ```ts\n * const result = c.var.client.oneMethod()\n * ```\n */\n // c.var.propName is a read-only\n get var() {\n if (!this.#var) {\n return {};\n }\n return Object.fromEntries(this.#var);\n }\n #newResponse(data, arg, headers) {\n const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();\n if (typeof arg === \"object\" && \"headers\" in arg) {\n const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);\n for (const [key, value] of argHeaders) {\n if (key.toLowerCase() === \"set-cookie\") {\n responseHeaders.append(key, value);\n } else {\n responseHeaders.set(key, value);\n }\n }\n }\n if (headers) {\n for (const [k, v] of Object.entries(headers)) {\n if (typeof v === \"string\") {\n responseHeaders.set(k, v);\n } else {\n responseHeaders.delete(k);\n for (const v2 of v) {\n responseHeaders.append(k, v2);\n }\n }\n }\n }\n const status = typeof arg === \"number\" ? arg : arg?.status ?? this.#status;\n return createResponseInstance(data, { status, headers: responseHeaders });\n }\n newResponse = (...args) => this.#newResponse(...args);\n /**\n * `.body()` can return the HTTP response.\n * You can set headers with `.header()` and set HTTP status code with `.status`.\n * This can also be set in `.text()`, `.json()` and so on.\n *\n * @see {@link https://hono.dev/docs/api/context#body}\n *\n * @example\n * ```ts\n * app.get('/welcome', (c) => {\n * // Set headers\n * c.header('X-Message', 'Hello!')\n * c.header('Content-Type', 'text/plain')\n * // Set HTTP status code\n * c.status(201)\n *\n * // Return the response body\n * return c.body('Thank you for coming')\n * })\n * ```\n */\n body = (data, arg, headers) => this.#newResponse(data, arg, headers);\n /**\n * `.text()` can render text as `Content-Type:text/plain`.\n *\n * @see {@link https://hono.dev/docs/api/context#text}\n *\n * @example\n * ```ts\n * app.get('/say', (c) => {\n * return c.text('Hello!')\n * })\n * ```\n */\n text = (text, arg, headers) => {\n return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(\n text,\n arg,\n setDefaultContentType(TEXT_PLAIN, headers)\n );\n };\n /**\n * `.json()` can render JSON as `Content-Type:application/json`.\n *\n * @see {@link https://hono.dev/docs/api/context#json}\n *\n * @example\n * ```ts\n * app.get('/api', (c) => {\n * return c.json({ message: 'Hello!' })\n * })\n * ```\n */\n json = (object, arg, headers) => {\n return this.#newResponse(\n JSON.stringify(object),\n arg,\n setDefaultContentType(\"application/json\", headers)\n );\n };\n html = (html, arg, headers) => {\n const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType(\"text/html; charset=UTF-8\", headers));\n return typeof html === \"object\" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);\n };\n /**\n * `.redirect()` can Redirect, default status code is 302.\n *\n * @see {@link https://hono.dev/docs/api/context#redirect}\n *\n * @example\n * ```ts\n * app.get('/redirect', (c) => {\n * return c.redirect('/')\n * })\n * app.get('/redirect-permanently', (c) => {\n * return c.redirect('/', 301)\n * })\n * ```\n */\n redirect = (location, status) => {\n const locationString = String(location);\n this.header(\n \"Location\",\n // Multibyes should be encoded\n // eslint-disable-next-line no-control-regex\n !/[^\\x00-\\xFF]/.test(locationString) ? locationString : encodeURI(locationString)\n );\n return this.newResponse(null, status ?? 302);\n };\n /**\n * `.notFound()` can return the Not Found Response.\n *\n * @see {@link https://hono.dev/docs/api/context#notfound}\n *\n * @example\n * ```ts\n * app.get('/notfound', (c) => {\n * return c.notFound()\n * })\n * ```\n */\n notFound = () => {\n this.#notFoundHandler ??= () => createResponseInstance();\n return this.#notFoundHandler(this);\n };\n};\nexport {\n Context,\n TEXT_PLAIN\n};\n","// src/router.ts\nvar METHOD_NAME_ALL = \"ALL\";\nvar METHOD_NAME_ALL_LOWERCASE = \"all\";\nvar METHODS = [\"get\", \"post\", \"put\", \"delete\", \"options\", \"patch\"];\nvar MESSAGE_MATCHER_IS_ALREADY_BUILT = \"Can not add a route since the matcher is already built.\";\nvar UnsupportedPathError = class extends Error {\n};\nexport {\n MESSAGE_MATCHER_IS_ALREADY_BUILT,\n METHODS,\n METHOD_NAME_ALL,\n METHOD_NAME_ALL_LOWERCASE,\n UnsupportedPathError\n};\n","// src/utils/constants.ts\nvar COMPOSED_HANDLER = \"__COMPOSED_HANDLER\";\nexport {\n COMPOSED_HANDLER\n};\n","// src/hono-base.ts\nimport { compose } from \"./compose.js\";\nimport { Context } from \"./context.js\";\nimport { METHODS, METHOD_NAME_ALL, METHOD_NAME_ALL_LOWERCASE } from \"./router.js\";\nimport { COMPOSED_HANDLER } from \"./utils/constants.js\";\nimport { getPath, getPathNoStrict, mergePath } from \"./utils/url.js\";\nvar notFoundHandler = (c) => {\n return c.text(\"404 Not Found\", 404);\n};\nvar errorHandler = (err, c) => {\n if (\"getResponse\" in err) {\n const res = err.getResponse();\n return c.newResponse(res.body, res);\n }\n console.error(err);\n return c.text(\"Internal Server Error\", 500);\n};\nvar Hono = class _Hono {\n get;\n post;\n put;\n delete;\n options;\n patch;\n all;\n on;\n use;\n /*\n This class is like an abstract class and does not have a router.\n To use it, inherit the class and implement router in the constructor.\n */\n router;\n getPath;\n // Cannot use `#` because it requires visibility at JavaScript runtime.\n _basePath = \"/\";\n #path = \"/\";\n routes = [];\n constructor(options = {}) {\n const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];\n allMethods.forEach((method) => {\n this[method] = (args1, ...args) => {\n if (typeof args1 === \"string\") {\n this.#path = args1;\n } else {\n this.#addRoute(method, this.#path, args1);\n }\n args.forEach((handler) => {\n this.#addRoute(method, this.#path, handler);\n });\n return this;\n };\n });\n this.on = (method, path, ...handlers) => {\n for (const p of [path].flat()) {\n this.#path = p;\n for (const m of [method].flat()) {\n handlers.map((handler) => {\n this.#addRoute(m.toUpperCase(), this.#path, handler);\n });\n }\n }\n return this;\n };\n this.use = (arg1, ...handlers) => {\n if (typeof arg1 === \"string\") {\n this.#path = arg1;\n } else {\n this.#path = \"*\";\n handlers.unshift(arg1);\n }\n handlers.forEach((handler) => {\n this.#addRoute(METHOD_NAME_ALL, this.#path, handler);\n });\n return this;\n };\n const { strict, ...optionsWithoutStrict } = options;\n Object.assign(this, optionsWithoutStrict);\n this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict;\n }\n #clone() {\n const clone = new _Hono({\n router: this.router,\n getPath: this.getPath\n });\n clone.errorHandler = this.errorHandler;\n clone.#notFoundHandler = this.#notFoundHandler;\n clone.routes = this.routes;\n return clone;\n }\n #notFoundHandler = notFoundHandler;\n // Cannot use `#` because it requires visibility at JavaScript runtime.\n errorHandler = errorHandler;\n /**\n * `.route()` allows grouping other Hono instance in routes.\n *\n * @see {@link https://hono.dev/docs/api/routing#grouping}\n *\n * @param {string} path - base Path\n * @param {Hono} app - other Hono instance\n * @returns {Hono} routed Hono instance\n *\n * @example\n * ```ts\n * const app = new Hono()\n * const app2 = new Hono()\n *\n * app2.get(\"/user\", (c) => c.text(\"user\"))\n * app.route(\"/api\", app2) // GET /api/user\n * ```\n */\n route(path, app) {\n const subApp = this.basePath(path);\n app.routes.map((r) => {\n let handler;\n if (app.errorHandler === errorHandler) {\n handler = r.handler;\n } else {\n handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;\n handler[COMPOSED_HANDLER] = r.handler;\n }\n subApp.#addRoute(r.method, r.path, handler);\n });\n return this;\n }\n /**\n * `.basePath()` allows base paths to be specified.\n *\n * @see {@link https://hono.dev/docs/api/routing#base-path}\n *\n * @param {string} path - base Path\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * const api = new Hono().basePath('/api')\n * ```\n */\n basePath(path) {\n const subApp = this.#clone();\n subApp._basePath = mergePath(this._basePath, path);\n return subApp;\n }\n /**\n * `.onError()` handles an error and returns a customized Response.\n *\n * @see {@link https://hono.dev/docs/api/hono#error-handling}\n *\n * @param {ErrorHandler} handler - request Handler for error\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * app.onError((err, c) => {\n * console.error(`${err}`)\n * return c.text('Custom Error Message', 500)\n * })\n * ```\n */\n onError = (handler) => {\n this.errorHandler = handler;\n return this;\n };\n /**\n * `.notFound()` allows you to customize a Not Found Response.\n *\n * @see {@link https://hono.dev/docs/api/hono#not-found}\n *\n * @param {NotFoundHandler} handler - request handler for not-found\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * app.notFound((c) => {\n * return c.text('Custom 404 Message', 404)\n * })\n * ```\n */\n notFound = (handler) => {\n this.#notFoundHandler = handler;\n return this;\n };\n /**\n * `.mount()` allows you to mount applications built with other frameworks into your Hono application.\n *\n * @see {@link https://hono.dev/docs/api/hono#mount}\n *\n * @param {string} path - base Path\n * @param {Function} applicationHandler - other Request Handler\n * @param {MountOptions} [options] - options of `.mount()`\n * @returns {Hono} mounted Hono instance\n *\n * @example\n * ```ts\n * import { Router as IttyRouter } from 'itty-router'\n * import { Hono } from 'hono'\n * // Create itty-router application\n * const ittyRouter = IttyRouter()\n * // GET /itty-router/hello\n * ittyRouter.get('/hello', () => new Response('Hello from itty-router'))\n *\n * const app = new Hono()\n * app.mount('/itty-router', ittyRouter.handle)\n * ```\n *\n * @example\n * ```ts\n * const app = new Hono()\n * // Send the request to another application without modification.\n * app.mount('/app', anotherApp, {\n * replaceRequest: (req) => req,\n * })\n * ```\n */\n mount(path, applicationHandler, options) {\n let replaceRequest;\n let optionHandler;\n if (options) {\n if (typeof options === \"function\") {\n optionHandler = options;\n } else {\n optionHandler = options.optionHandler;\n if (options.replaceRequest === false) {\n replaceRequest = (request) => request;\n } else {\n replaceRequest = options.replaceRequest;\n }\n }\n }\n const getOptions = optionHandler ? (c) => {\n const options2 = optionHandler(c);\n return Array.isArray(options2) ? options2 : [options2];\n } : (c) => {\n let executionContext = void 0;\n try {\n executionContext = c.executionCtx;\n } catch {\n }\n return [c.env, executionContext];\n };\n replaceRequest ||= (() => {\n const mergedPath = mergePath(this._basePath, path);\n const pathPrefixLength = mergedPath === \"/\" ? 0 : mergedPath.length;\n return (request) => {\n const url = new URL(request.url);\n url.pathname = url.pathname.slice(pathPrefixLength) || \"/\";\n return new Request(url, request);\n };\n })();\n const handler = async (c, next) => {\n const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));\n if (res) {\n return res;\n }\n await next();\n };\n this.#addRoute(METHOD_NAME_ALL, mergePath(path, \"*\"), handler);\n return this;\n }\n #addRoute(method, path, handler) {\n method = method.toUpperCase();\n path = mergePath(this._basePath, path);\n const r = { basePath: this._basePath, path, method, handler };\n this.router.add(method, path, [handler, r]);\n this.routes.push(r);\n }\n #handleError(err, c) {\n if (err instanceof Error) {\n return this.errorHandler(err, c);\n }\n throw err;\n }\n #dispatch(request, executionCtx, env, method) {\n if (method === \"HEAD\") {\n return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, \"GET\")))();\n }\n const path = this.getPath(request, { env });\n const matchResult = this.router.match(method, path);\n const c = new Context(request, {\n path,\n matchResult,\n env,\n executionCtx,\n notFoundHandler: this.#notFoundHandler\n });\n if (matchResult[0].length === 1) {\n let res;\n try {\n res = matchResult[0][0][0][0](c, async () => {\n c.res = await this.#notFoundHandler(c);\n });\n } catch (err) {\n return this.#handleError(err, c);\n }\n return res instanceof Promise ? res.then(\n (resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))\n ).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);\n }\n const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);\n return (async () => {\n try {\n const context = await composed(c);\n if (!context.finalized) {\n throw new Error(\n \"Context is not finalized. Did you forget to return a Response object or `await next()`?\"\n );\n }\n return context.res;\n } catch (err) {\n return this.#handleError(err, c);\n }\n })();\n }\n /**\n * `.fetch()` will be entry point of your app.\n *\n * @see {@link https://hono.dev/docs/api/hono#fetch}\n *\n * @param {Request} request - request Object of request\n * @param {Env} Env - env Object\n * @param {ExecutionContext} - context of execution\n * @returns {Response | Promise<Response>} response of request\n *\n */\n fetch = (request, ...rest) => {\n return this.#dispatch(request, rest[1], rest[0], request.method);\n };\n /**\n * `.request()` is a useful method for testing.\n * You can pass a URL or pathname to send a GET request.\n * app will return a Response object.\n * ```ts\n * test('GET /hello is ok', async () => {\n * const res = await app.request('/hello')\n * expect(res.status).toBe(200)\n * })\n * ```\n * @see https://hono.dev/docs/api/hono#request\n */\n request = (input, requestInit, Env, executionCtx) => {\n if (input instanceof Request) {\n return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);\n }\n input = input.toString();\n return this.fetch(\n new Request(\n /^https?:\\/\\//.test(input) ? input : `http://localhost${mergePath(\"/\", input)}`,\n requestInit\n ),\n Env,\n executionCtx\n );\n };\n /**\n * `.fire()` automatically adds a global fetch event listener.\n * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.\n * @deprecated\n * Use `fire` from `hono/service-worker` instead.\n * ```ts\n * import { Hono } from 'hono'\n * import { fire } from 'hono/service-worker'\n *\n * const app = new Hono()\n * // ...\n * fire(app)\n * ```\n * @see https://hono.dev/docs/api/hono#fire\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API\n * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/\n */\n fire = () => {\n addEventListener(\"fetch\", (event) => {\n event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method));\n });\n };\n};\nexport {\n Hono as HonoBase\n};\n","// src/router/reg-exp-router/matcher.ts\nimport { METHOD_NAME_ALL } from \"../../router.js\";\nvar emptyParam = [];\nfunction match(method, path) {\n const matchers = this.buildAllMatchers();\n const match2 = ((method2, path2) => {\n const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];\n const staticMatch = matcher[2][path2];\n if (staticMatch) {\n return staticMatch;\n }\n const match3 = path2.match(matcher[0]);\n if (!match3) {\n return [[], emptyParam];\n }\n const index = match3.indexOf(\"\", 1);\n return [matcher[1][index], match3];\n });\n this.match = match2;\n return match2(method, path);\n}\nexport {\n emptyParam,\n match\n};\n","// src/router/reg-exp-router/node.ts\nvar LABEL_REG_EXP_STR = \"[^/]+\";\nvar ONLY_WILDCARD_REG_EXP_STR = \".*\";\nvar TAIL_WILDCARD_REG_EXP_STR = \"(?:|/.*)\";\nvar PATH_ERROR = /* @__PURE__ */ Symbol();\nvar regExpMetaChars = new Set(\".\\\\+*[^]$()\");\nfunction compareKey(a, b) {\n if (a.length === 1) {\n return b.length === 1 ? a < b ? -1 : 1 : -1;\n }\n if (b.length === 1) {\n return 1;\n }\n if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {\n return 1;\n } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {\n return -1;\n }\n if (a === LABEL_REG_EXP_STR) {\n return 1;\n } else if (b === LABEL_REG_EXP_STR) {\n return -1;\n }\n return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;\n}\nvar Node = class _Node {\n #index;\n #varIndex;\n #children = /* @__PURE__ */ Object.create(null);\n insert(tokens, index, paramMap, context, pathErrorCheckOnly) {\n if (tokens.length === 0) {\n if (this.#index !== void 0) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n this.#index = index;\n return;\n }\n const [token, ...restTokens] = tokens;\n const pattern = token === \"*\" ? restTokens.length === 0 ? [\"\", \"\", ONLY_WILDCARD_REG_EXP_STR] : [\"\", \"\", LABEL_REG_EXP_STR] : token === \"/*\" ? [\"\", \"\", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\\:([^\\{\\}]+)(?:\\{(.+)\\})?$/);\n let node;\n if (pattern) {\n const name = pattern[1];\n let regexpStr = pattern[2] || LABEL_REG_EXP_STR;\n if (name && pattern[2]) {\n if (regexpStr === \".*\") {\n throw PATH_ERROR;\n }\n regexpStr = regexpStr.replace(/^\\((?!\\?:)(?=[^)]+\\)$)/, \"(?:\");\n if (/\\((?!\\?:)/.test(regexpStr)) {\n throw PATH_ERROR;\n }\n }\n node = this.#children[regexpStr];\n if (!node) {\n if (Object.keys(this.#children).some(\n (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR\n )) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n node = this.#children[regexpStr] = new _Node();\n if (name !== \"\") {\n node.#varIndex = context.varIndex++;\n }\n }\n if (!pathErrorCheckOnly && name !== \"\") {\n paramMap.push([name, node.#varIndex]);\n }\n } else {\n node = this.#children[token];\n if (!node) {\n if (Object.keys(this.#children).some(\n (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR\n )) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n node = this.#children[token] = new _Node();\n }\n }\n node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);\n }\n buildRegExpStr() {\n const childKeys = Object.keys(this.#children).sort(compareKey);\n const strList = childKeys.map((k) => {\n const c = this.#children[k];\n return (typeof c.#varIndex === \"number\" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\\\${k}` : k) + c.buildRegExpStr();\n });\n if (typeof this.#index === \"number\") {\n strList.unshift(`#${this.#index}`);\n }\n if (strList.length === 0) {\n return \"\";\n }\n if (strList.length === 1) {\n return strList[0];\n }\n return \"(?:\" + strList.join(\"|\") + \")\";\n }\n};\nexport {\n Node,\n PATH_ERROR\n};\n","// src/router/reg-exp-router/trie.ts\nimport { Node } from \"./node.js\";\nvar Trie = class {\n #context = { varIndex: 0 };\n #root = new Node();\n insert(path, index, pathErrorCheckOnly) {\n const paramAssoc = [];\n const groups = [];\n for (let i = 0; ; ) {\n let replaced = false;\n path = path.replace(/\\{[^}]+\\}/g, (m) => {\n const mark = `@\\\\${i}`;\n groups[i] = [mark, m];\n i++;\n replaced = true;\n return mark;\n });\n if (!replaced) {\n break;\n }\n }\n const tokens = path.match(/(?::[^\\/]+)|(?:\\/\\*$)|./g) || [];\n for (let i = groups.length - 1; i >= 0; i--) {\n const [mark] = groups[i];\n for (let j = tokens.length - 1; j >= 0; j--) {\n if (tokens[j].indexOf(mark) !== -1) {\n tokens[j] = tokens[j].replace(mark, groups[i][1]);\n break;\n }\n }\n }\n this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);\n return paramAssoc;\n }\n buildRegExp() {\n let regexp = this.#root.buildRegExpStr();\n if (regexp === \"\") {\n return [/^$/, [], []];\n }\n let captureIndex = 0;\n const indexReplacementMap = [];\n const paramReplacementMap = [];\n regexp = regexp.replace(/#(\\d+)|@(\\d+)|\\.\\*\\$/g, (_, handlerIndex, paramIndex) => {\n if (handlerIndex !== void 0) {\n indexReplacementMap[++captureIndex] = Number(handlerIndex);\n return \"$()\";\n }\n if (paramIndex !== void 0) {\n paramReplacementMap[Number(paramIndex)] = ++captureIndex;\n return \"\";\n }\n return \"\";\n });\n return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];\n }\n};\nexport {\n Trie\n};\n","// src/router/reg-exp-router/router.ts\nimport {\n MESSAGE_MATCHER_IS_ALREADY_BUILT,\n METHOD_NAME_ALL,\n UnsupportedPathError\n} from \"../../router.js\";\nimport { checkOptionalParameter } from \"../../utils/url.js\";\nimport { match, emptyParam } from \"./matcher.js\";\nimport { PATH_ERROR } from \"./node.js\";\nimport { Trie } from \"./trie.js\";\nvar nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];\nvar wildcardRegExpCache = /* @__PURE__ */ Object.create(null);\nfunction buildWildcardRegExp(path) {\n return wildcardRegExpCache[path] ??= new RegExp(\n path === \"*\" ? \"\" : `^${path.replace(\n /\\/\\*$|([.\\\\+*[^\\]$()])/g,\n (_, metaChar) => metaChar ? `\\\\${metaChar}` : \"(?:|/.*)\"\n )}$`\n );\n}\nfunction clearWildcardRegExpCache() {\n wildcardRegExpCache = /* @__PURE__ */ Object.create(null);\n}\nfunction buildMatcherFromPreprocessedRoutes(routes) {\n const trie = new Trie();\n const handlerData = [];\n if (routes.length === 0) {\n return nullMatcher;\n }\n const routesWithStaticPathFlag = routes.map(\n (route) => [!/\\*|\\/:/.test(route[0]), ...route]\n ).sort(\n ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length\n );\n const staticMap = /* @__PURE__ */ Object.create(null);\n for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {\n const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];\n if (pathErrorCheckOnly) {\n staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];\n } else {\n j++;\n }\n let paramAssoc;\n try {\n paramAssoc = trie.insert(path, j, pathErrorCheckOnly);\n } catch (e) {\n throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;\n }\n if (pathErrorCheckOnly) {\n continue;\n }\n handlerData[j] = handlers.map(([h, paramCount]) => {\n const paramIndexMap = /* @__PURE__ */ Object.create(null);\n paramCount -= 1;\n for (; paramCount >= 0; paramCount--) {\n const [key, value] = paramAssoc[paramCount];\n paramIndexMap[key] = value;\n }\n return [h, paramIndexMap];\n });\n }\n const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();\n for (let i = 0, len = handlerData.length; i < len; i++) {\n for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {\n const map = handlerData[i][j]?.[1];\n if (!map) {\n continue;\n }\n const keys = Object.keys(map);\n for (let k = 0, len3 = keys.length; k < len3; k++) {\n map[keys[k]] = paramReplacementMap[map[keys[k]]];\n }\n }\n }\n const handlerMap = [];\n for (const i in indexReplacementMap) {\n handlerMap[i] = handlerData[indexReplacementMap[i]];\n }\n return [regexp, handlerMap, staticMap];\n}\nfunction findMiddleware(middleware, path) {\n if (!middleware) {\n return void 0;\n }\n for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {\n if (buildWildcardRegExp(k).test(path)) {\n return [...middleware[k]];\n }\n }\n return void 0;\n}\nvar RegExpRouter = class {\n name = \"RegExpRouter\";\n #middleware;\n #routes;\n constructor() {\n this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };\n this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };\n }\n add(method, path, handler) {\n const middleware = this.#middleware;\n const routes = this.#routes;\n if (!middleware || !routes) {\n throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);\n }\n if (!middleware[method]) {\n ;\n [middleware, routes].forEach((handlerMap) => {\n handlerMap[method] = /* @__PURE__ */ Object.create(null);\n Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {\n handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];\n });\n });\n }\n if (path === \"/*\") {\n path = \"*\";\n }\n const paramCount = (path.match(/\\/:/g) || []).length;\n if (/\\*$/.test(path)) {\n const re = buildWildcardRegExp(path);\n if (method === METHOD_NAME_ALL) {\n Object.keys(middleware).forEach((m) => {\n middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];\n });\n } else {\n middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];\n }\n Object.keys(middleware).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n Object.keys(middleware[m]).forEach((p) => {\n re.test(p) && middleware[m][p].push([handler, paramCount]);\n });\n }\n });\n Object.keys(routes).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n Object.keys(routes[m]).forEach(\n (p) => re.test(p) && routes[m][p].push([handler, paramCount])\n );\n }\n });\n return;\n }\n const paths = checkOptionalParameter(path) || [path];\n for (let i = 0, len = paths.length; i < len; i++) {\n const path2 = paths[i];\n Object.keys(routes).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n routes[m][path2] ||= [\n ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []\n ];\n routes[m][path2].push([handler, paramCount - len + i + 1]);\n }\n });\n }\n }\n match = match;\n buildAllMatchers() {\n const matchers = /* @__PURE__ */ Object.create(null);\n Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {\n matchers[method] ||= this.#buildMatcher(method);\n });\n this.#middleware = this.#routes = void 0;\n clearWildcardRegExpCache();\n return matchers;\n }\n #buildMatcher(method) {\n const routes = [];\n let hasOwnRoute = method === METHOD_NAME_ALL;\n [this.#middleware, this.#routes].forEach((r) => {\n const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];\n if (ownRoute.length !== 0) {\n hasOwnRoute ||= true;\n routes.push(...ownRoute);\n } else if (method !== METHOD_NAME_ALL) {\n routes.push(\n ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])\n );\n }\n });\n if (!hasOwnRoute) {\n return null;\n } else {\n return buildMatcherFromPreprocessedRoutes(routes);\n }\n }\n};\nexport {\n RegExpRouter\n};\n","// src/router/smart-router/router.ts\nimport { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from \"../../router.js\";\nvar SmartRouter = class {\n name = \"SmartRouter\";\n #routers = [];\n #routes = [];\n constructor(init) {\n this.#routers = init.routers;\n }\n add(method, path, handler) {\n if (!this.#routes) {\n throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);\n }\n this.#routes.push([method, path, handler]);\n }\n match(method, path) {\n if (!this.#routes) {\n throw new Error(\"Fatal error\");\n }\n const routers = this.#routers;\n const routes = this.#routes;\n const len = routers.length;\n let i = 0;\n let res;\n for (; i < len; i++) {\n const router = routers[i];\n try {\n for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {\n router.add(...routes[i2]);\n }\n res = router.match(method, path);\n } catch (e) {\n if (e instanceof UnsupportedPathError) {\n continue;\n }\n throw e;\n }\n this.match = router.match.bind(router);\n this.#routers = [router];\n this.#routes = void 0;\n break;\n }\n if (i === len) {\n throw new Error(\"Fatal error\");\n }\n this.name = `SmartRouter + ${this.activeRouter.name}`;\n return res;\n }\n get activeRouter() {\n if (this.#routes || this.#routers.length !== 1) {\n throw new Error(\"No active router has been determined yet.\");\n }\n return this.#routers[0];\n }\n};\nexport {\n SmartRouter\n};\n","// src/router/trie-router/node.ts\nimport { METHOD_NAME_ALL } from \"../../router.js\";\nimport { getPattern, splitPath, splitRoutingPath } from \"../../utils/url.js\";\nvar emptyParams = /* @__PURE__ */ Object.create(null);\nvar hasChildren = (children) => {\n for (const _ in children) {\n return true;\n }\n return false;\n};\nvar Node = class _Node {\n #methods;\n #children;\n #patterns;\n #order = 0;\n #params = emptyParams;\n constructor(method, handler, children) {\n this.#children = children || /* @__PURE__ */ Object.create(null);\n this.#methods = [];\n if (method && handler) {\n const m = /* @__PURE__ */ Object.create(null);\n m[method] = { handler, possibleKeys: [], score: 0 };\n this.#methods = [m];\n }\n this.#patterns = [];\n }\n insert(method, path, handler) {\n this.#order = ++this.#order;\n let curNode = this;\n const parts = splitRoutingPath(path);\n const possibleKeys = [];\n for (let i = 0, len = parts.length; i < len; i++) {\n const p = parts[i];\n const nextP = parts[i + 1];\n const pattern = getPattern(p, nextP);\n const key = Array.isArray(pattern) ? pattern[0] : p;\n if (key in curNode.#children) {\n curNode = curNode.#children[key];\n if (pattern) {\n possibleKeys.push(pattern[1]);\n }\n continue;\n }\n curNode.#children[key] = new _Node();\n if (pattern) {\n curNode.#patterns.push(pattern);\n possibleKeys.push(pattern[1]);\n }\n curNode = curNode.#children[key];\n }\n curNode.#methods.push({\n [method]: {\n handler,\n possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),\n score: this.#order\n }\n });\n return curNode;\n }\n #pushHandlerSets(handlerSets, node, method, nodeParams, params) {\n for (let i = 0, len = node.#methods.length; i < len; i++) {\n const m = node.#methods[i];\n const handlerSet = m[method] || m[METHOD_NAME_ALL];\n const processedSet = {};\n if (handlerSet !== void 0) {\n handlerSet.params = /* @__PURE__ */ Object.create(null);\n handlerSets.push(handlerSet);\n if (nodeParams !== emptyParams || params && params !== emptyParams) {\n for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {\n const key = handlerSet.possibleKeys[i2];\n const processed = processedSet[handlerSet.score];\n handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];\n processedSet[handlerSet.score] = true;\n }\n }\n }\n }\n }\n search(method, path) {\n const handlerSets = [];\n this.#params = emptyParams;\n const curNode = this;\n let curNodes = [curNode];\n const parts = splitPath(path);\n const curNodesQueue = [];\n const len = parts.length;\n let partOffsets = null;\n for (let i = 0; i < len; i++) {\n const part = parts[i];\n const isLast = i === len - 1;\n const tempNodes = [];\n for (let j = 0, len2 = curNodes.length; j < len2; j++) {\n const node = curNodes[j];\n const nextNode = node.#children[part];\n if (nextNode) {\n nextNode.#params = node.#params;\n if (isLast) {\n if (nextNode.#children[\"*\"]) {\n this.#pushHandlerSets(handlerSets, nextNode.#children[\"*\"], method, node.#params);\n }\n this.#pushHandlerSets(handlerSets, nextNode, method, node.#params);\n } else {\n tempNodes.push(nextNode);\n }\n }\n for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {\n const pattern = node.#patterns[k];\n const params = node.#params === emptyParams ? {} : { ...node.#params };\n if (pattern === \"*\") {\n const astNode = node.#children[\"*\"];\n if (astNode) {\n this.#pushHandlerSets(handlerSets, astNode, method, node.#params);\n astNode.#params = params;\n tempNodes.push(astNode);\n }\n continue;\n }\n const [key, name, matcher] = pattern;\n if (!part && !(matcher instanceof RegExp)) {\n continue;\n }\n const child = node.#children[key];\n if (matcher instanceof RegExp) {\n if (partOffsets === null) {\n partOffsets = new Array(len);\n let offset = path[0] === \"/\" ? 1 : 0;\n for (let p = 0; p < len; p++) {\n partOffsets[p] = offset;\n offset += parts[p].length + 1;\n }\n }\n const restPathString = path.substring(partOffsets[i]);\n const m = matcher.exec(restPathString);\n if (m) {\n params[name] = m[0];\n this.#pushHandlerSets(handlerSets, child, method, node.#params, params);\n if (hasChildren(child.#children)) {\n child.#params = params;\n const componentCount = m[0].match(/\\//)?.length ?? 0;\n const targetCurNodes = curNodesQueue[componentCount] ||= [];\n targetCurNodes.push(child);\n }\n continue;\n }\n }\n if (matcher === true || matcher.test(part)) {\n params[name] = part;\n if (isLast) {\n this.#pushHandlerSets(handlerSets, child, method, params, node.#params);\n if (child.#children[\"*\"]) {\n this.#pushHandlerSets(\n handlerSets,\n child.#children[\"*\"],\n method,\n params,\n node.#params\n );\n }\n } else {\n child.#params = params;\n tempNodes.push(child);\n }\n }\n }\n }\n const shifted = curNodesQueue.shift();\n curNodes = shifted ? tempNodes.concat(shifted) : tempNodes;\n }\n if (handlerSets.length > 1) {\n handlerSets.sort((a, b) => {\n return a.score - b.score;\n });\n }\n return [handlerSets.map(({ handler, params }) => [handler, params])];\n }\n};\nexport {\n Node\n};\n","// src/router/trie-router/router.ts\nimport { checkOptionalParameter } from \"../../utils/url.js\";\nimport { Node } from \"./node.js\";\nvar TrieRouter = class {\n name = \"TrieRouter\";\n #node;\n constructor() {\n this.#node = new Node();\n }\n add(method, path, handler) {\n const results = checkOptionalParameter(path);\n if (results) {\n for (let i = 0, len = results.length; i < len; i++) {\n this.#node.insert(method, results[i], handler);\n }\n return;\n }\n this.#node.insert(method, path, handler);\n }\n match(method, path) {\n return this.#node.search(method, path);\n }\n};\nexport {\n TrieRouter\n};\n","// src/hono.ts\nimport { HonoBase } from \"./hono-base.js\";\nimport { RegExpRouter } from \"./router/reg-exp-router/index.js\";\nimport { SmartRouter } from \"./router/smart-router/index.js\";\nimport { TrieRouter } from \"./router/trie-router/index.js\";\nvar Hono = class extends HonoBase {\n /**\n * Creates an instance of the Hono class.\n *\n * @param options - Optional configuration options for the Hono instance.\n */\n constructor(options = {}) {\n super(options);\n this.router = options.router ?? new SmartRouter({\n routers: [new RegExpRouter(), new TrieRouter()]\n });\n }\n};\nexport {\n Hono\n};\n","/**\n * Sync authentication: request signing, verification, and nonce management.\n *\n * Uses Node's native Ed25519 crypto for sign/verify and SHA-256 canonical\n * request hashing. No ssh-keygen shelling.\n */\n\nimport {\n\tcreateHash,\n\tcreatePrivateKey,\n\tcreatePublicKey,\n\trandomBytes,\n\tsign,\n\tverify,\n} from \"node:crypto\";\nimport { lt } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { loadPrivateKey } from \"./sync-identity.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Signature version. v1 = SSHSIG format (ssh-keygen era), v2 = raw Ed25519.\n *\n * v2 is a breaking change: Python peers using `ssh-keygen -Y verify` cannot\n * verify v2 signatures. Once all peers run the TS runtime, v1 support can\n * be removed. During migration, the verifier accepts both versions.\n */\nexport const SIGNATURE_VERSION = \"v2\";\nexport const DEFAULT_TIME_WINDOW_S = 300;\n\n// ---------------------------------------------------------------------------\n// Canonical request\n// ---------------------------------------------------------------------------\n\n/**\n * Build a canonical request buffer for signing/verification.\n *\n * SHA-256 hashes the body, then joins method/path/timestamp/nonce/bodyHash\n * with newlines and returns the UTF-8 encoded result.\n */\nexport function buildCanonicalRequest(\n\tmethod: string,\n\tpathWithQuery: string,\n\ttimestamp: string,\n\tnonce: string,\n\tbodyBytes: Buffer,\n): Buffer {\n\tconst bodyHash = createHash(\"sha256\").update(bodyBytes).digest(\"hex\");\n\tconst canonical = [method.toUpperCase(), pathWithQuery, timestamp, nonce, bodyHash].join(\"\\n\");\n\treturn Buffer.from(canonical, \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Sign\n// ---------------------------------------------------------------------------\n\nexport interface SignRequestOptions {\n\tmethod: string;\n\turl: string;\n\tbodyBytes: Buffer;\n\tkeysDir?: string;\n\ttimestamp?: string;\n\tnonce?: string;\n}\n\n/**\n * Sign an HTTP request and return the auth headers.\n *\n * Uses Node's native Ed25519 crypto.sign() — no ssh-keygen shelling.\n * Returns X-Opencode-Timestamp, X-Opencode-Nonce, X-Opencode-Signature headers.\n */\nexport function signRequest(options: SignRequestOptions): Record<string, string> {\n\tconst ts = options.timestamp ?? String(Math.floor(Date.now() / 1000));\n\tconst nonceValue = options.nonce ?? randomBytes(16).toString(\"hex\");\n\n\tconst parsed = new URL(options.url);\n\tlet path = parsed.pathname || \"/\";\n\tif (parsed.search) {\n\t\tpath = `${path}${parsed.search}`;\n\t}\n\n\tconst canonical = buildCanonicalRequest(options.method, path, ts, nonceValue, options.bodyBytes);\n\n\tconst keyData = loadPrivateKey(options.keysDir);\n\tif (!keyData) {\n\t\tthrow new Error(\"private key missing\");\n\t}\n\n\t// Handle both OpenSSH format (existing keys) and PKCS8 PEM (newly generated)\n\tlet privateKeyObj: ReturnType<typeof createPrivateKey>;\n\ttry {\n\t\tprivateKeyObj = createPrivateKey(keyData);\n\t} catch {\n\t\tprivateKeyObj = createPrivateKey({ key: keyData, format: \"pem\", type: \"pkcs8\" });\n\t}\n\tconst signatureBytes = sign(null, canonical, privateKeyObj);\n\tconst signature = signatureBytes.toString(\"base64\");\n\n\treturn {\n\t\t\"X-Opencode-Timestamp\": ts,\n\t\t\"X-Opencode-Nonce\": nonceValue,\n\t\t\"X-Opencode-Signature\": `${SIGNATURE_VERSION}:${signature}`,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Verify\n// ---------------------------------------------------------------------------\n\nexport interface VerifySignatureOptions {\n\tmethod: string;\n\tpathWithQuery: string;\n\tbodyBytes: Buffer;\n\ttimestamp: string;\n\tnonce: string;\n\tsignature: string;\n\tpublicKey: string;\n\tdeviceId: string;\n\ttimeWindowS?: number;\n}\n\n/**\n * Verify a signed request.\n *\n * Checks timestamp freshness, signature version prefix, then uses\n * Node's native crypto.verify() with the sender's Ed25519 public key.\n */\nexport function verifySignature(options: VerifySignatureOptions): boolean {\n\tconst timeWindow = options.timeWindowS ?? DEFAULT_TIME_WINDOW_S;\n\n\t// Parse and validate timestamp — reject non-numeric strings (matches Python's int())\n\tif (!/^\\d+$/.test(options.timestamp)) return false;\n\tconst tsInt = Number.parseInt(options.timestamp, 10);\n\tif (Number.isNaN(tsInt)) return false;\n\n\tconst now = Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - tsInt) > timeWindow) return false;\n\n\t// Validate signature version prefix — accept both v1 and v2 during migration\n\tconst ACCEPTED_VERSIONS = [\"v1\", \"v2\"];\n\tconst colonIdx = options.signature.indexOf(\":\");\n\tif (colonIdx < 1) return false;\n\tconst sigVersion = options.signature.slice(0, colonIdx);\n\tif (!ACCEPTED_VERSIONS.includes(sigVersion)) return false;\n\n\tconst encoded = options.signature.slice(colonIdx + 1);\n\tif (!encoded) return false;\n\n\tlet signatureBytes: Buffer;\n\ttry {\n\t\tsignatureBytes = Buffer.from(encoded, \"base64\");\n\t\t// Verify it was valid base64 by round-tripping\n\t\tif (signatureBytes.toString(\"base64\") !== encoded) return false;\n\t} catch {\n\t\treturn false;\n\t}\n\n\tconst canonical = buildCanonicalRequest(\n\t\toptions.method,\n\t\toptions.pathWithQuery,\n\t\toptions.timestamp,\n\t\toptions.nonce,\n\t\toptions.bodyBytes,\n\t);\n\n\ttry {\n\t\tconst publicKeyObj = sshEd25519ToPublicKey(options.publicKey);\n\t\treturn verify(null, canonical, publicKeyObj, signatureBytes);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Parse an SSH ed25519 public key string into a Node crypto KeyObject.\n *\n * SSH format: \"ssh-ed25519 <base64-wire-format>\"\n * Wire format: uint32 key-type-len + key-type + uint32 key-data-len + key-data\n */\nfunction sshEd25519ToPublicKey(sshPub: string): ReturnType<typeof createPublicKey> {\n\tconst parts = sshPub.trim().split(/\\s+/);\n\tif (parts.length < 2 || parts[0] !== \"ssh-ed25519\") {\n\t\tthrow new Error(\"not an ssh-ed25519 key\");\n\t}\n\tconst wireFormat = Buffer.from(parts[1]!, \"base64\");\n\n\t// Read key type length\n\tif (wireFormat.length < 4) throw new Error(\"truncated wire format\");\n\tconst typeLen = wireFormat.readUInt32BE(0);\n\tconst typeEnd = 4 + typeLen;\n\tif (wireFormat.length < typeEnd + 4) throw new Error(\"truncated wire format\");\n\n\t// Read key data length\n\tconst keyLen = wireFormat.readUInt32BE(typeEnd);\n\tconst keyStart = typeEnd + 4;\n\tif (wireFormat.length < keyStart + keyLen) throw new Error(\"truncated wire format\");\n\tconst rawKey = wireFormat.subarray(keyStart, keyStart + keyLen);\n\n\tif (rawKey.length !== 32) throw new Error(`unexpected Ed25519 key length: ${rawKey.length}`);\n\n\t// Wrap raw 32-byte key in SPKI DER: 12-byte Ed25519 header + 32-byte key\n\tconst ed25519SpkiPrefix = Buffer.from(\"302a300506032b6570032100\", \"hex\");\n\tconst spkiDer = Buffer.concat([ed25519SpkiPrefix, rawKey]);\n\n\treturn createPublicKey({ key: spkiDer, format: \"der\", type: \"spki\" });\n}\n\n// ---------------------------------------------------------------------------\n// Auth headers (convenience)\n// ---------------------------------------------------------------------------\n\nexport interface BuildAuthHeadersOptions {\n\tdeviceId: string;\n\tmethod: string;\n\turl: string;\n\tbodyBytes: Buffer;\n\tkeysDir?: string;\n\ttimestamp?: string;\n\tnonce?: string;\n}\n\n/**\n * Build full auth headers including device ID and request signature.\n */\nexport function buildAuthHeaders(options: BuildAuthHeadersOptions): Record<string, string> {\n\treturn {\n\t\t\"X-Opencode-Device\": options.deviceId,\n\t\t...signRequest({\n\t\t\tmethod: options.method,\n\t\t\turl: options.url,\n\t\t\tbodyBytes: options.bodyBytes,\n\t\t\tkeysDir: options.keysDir,\n\t\t\ttimestamp: options.timestamp,\n\t\t\tnonce: options.nonce,\n\t\t}),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Nonce management\n// ---------------------------------------------------------------------------\n\n/**\n * Record a nonce to prevent replay attacks.\n *\n * Returns true on success, false if the nonce was already recorded\n * (duplicate = potential replay).\n */\nexport function recordNonce(\n\tdb: Database,\n\tdeviceId: string,\n\tnonce: string,\n\tcreatedAt: string,\n): boolean {\n\tconst d = drizzle(db, { schema });\n\ttry {\n\t\td.insert(schema.syncNonces).values({ nonce, device_id: deviceId, created_at: createdAt }).run();\n\t\treturn true;\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && err.message.includes(\"UNIQUE constraint failed\")) {\n\t\t\treturn false;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n/**\n * Remove nonces older than the given cutoff timestamp.\n */\nexport function cleanupNonces(db: Database, cutoff: string): void {\n\tconst d = drizzle(db, { schema });\n\td.delete(schema.syncNonces).where(lt(schema.syncNonces.created_at, cutoff)).run();\n}\n","/**\n * Coordinator API — Hono-based HTTP server for the coordinator relay.\n *\n * Manages device enrollment, presence, invites, and join requests.\n * Ported from codemem/coordinator_api.py.\n */\n\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport type { InvitePayload } from \"./coordinator-invites.js\";\nimport { encodeInvitePayload, inviteLink } from \"./coordinator-invites.js\";\nimport { CoordinatorStore } from \"./coordinator-store.js\";\nimport { DEFAULT_TIME_WINDOW_S, verifySignature } from \"./sync-auth.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst ADMIN_HEADER = \"X-Codemem-Coordinator-Admin\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction adminSecret(): string | null {\n\tconst value = (process.env.CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET ?? \"\").trim();\n\treturn value || null;\n}\n\nfunction authorizeAdmin(headerValue: string | null | undefined): { ok: boolean; error: string } {\n\tconst expected = adminSecret();\n\tif (!expected) return { ok: false, error: \"admin_not_configured\" };\n\tconst provided = (headerValue ?? \"\").trim();\n\tif (!provided) return { ok: false, error: \"missing_admin_header\" };\n\tif (provided !== expected) return { ok: false, error: \"invalid_admin_secret\" };\n\treturn { ok: true, error: \"ok\" };\n}\n\n/** Extract path + query string from a full URL for signature verification. */\nfunction pathWithQuery(url: string): string {\n\tconst parsed = new URL(url);\n\treturn parsed.search ? `${parsed.pathname}${parsed.search}` : parsed.pathname;\n}\n\nfunction recordNonce(\n\tstore: CoordinatorStore,\n\tdeviceId: string,\n\tnonce: string,\n\tcreatedAt: string,\n): boolean {\n\ttry {\n\t\tstore.db\n\t\t\t.prepare(\"INSERT INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)\")\n\t\t\t.run(deviceId, nonce, createdAt);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction cleanupNonces(store: CoordinatorStore, cutoff: string): void {\n\tstore.db.prepare(\"DELETE FROM request_nonces WHERE created_at < ?\").run(cutoff);\n}\n\ninterface AuthResult {\n\tok: boolean;\n\terror: string;\n\tenrollment: Record<string, unknown> | null;\n}\n\nfunction authorizeRequest(\n\tstore: CoordinatorStore,\n\topts: {\n\t\tmethod: string;\n\t\turl: string;\n\t\tgroupId: string;\n\t\tbody: Buffer;\n\t\tdeviceId: string | null;\n\t\tsignature: string | null;\n\t\ttimestamp: string | null;\n\t\tnonce: string | null;\n\t},\n): AuthResult {\n\tconst { deviceId, signature, timestamp, nonce } = opts;\n\tif (!deviceId || !signature || !timestamp || !nonce) {\n\t\treturn { ok: false, error: \"missing_headers\", enrollment: null };\n\t}\n\n\tconst enrollment = store.getEnrollment(opts.groupId, deviceId);\n\tif (!enrollment) {\n\t\treturn { ok: false, error: \"unknown_device\", enrollment: null };\n\t}\n\n\tlet valid: boolean;\n\ttry {\n\t\tvalid = verifySignature({\n\t\t\tmethod: opts.method,\n\t\t\tpathWithQuery: pathWithQuery(opts.url),\n\t\t\tbodyBytes: opts.body,\n\t\t\ttimestamp,\n\t\t\tnonce,\n\t\t\tsignature,\n\t\t\tpublicKey: String(enrollment.public_key),\n\t\t\tdeviceId,\n\t\t});\n\t} catch {\n\t\treturn { ok: false, error: \"signature_verification_error\", enrollment: null };\n\t}\n\n\tif (!valid) {\n\t\treturn { ok: false, error: \"invalid_signature\", enrollment: null };\n\t}\n\n\tconst createdAt = new Date().toISOString();\n\tif (!recordNonce(store, deviceId, nonce, createdAt)) {\n\t\treturn { ok: false, error: \"nonce_replay\", enrollment: null };\n\t}\n\n\tconst cutoff = new Date(Date.now() - DEFAULT_TIME_WINDOW_S * 2 * 1000).toISOString();\n\tcleanupNonces(store, cutoff);\n\n\treturn { ok: true, error: \"ok\", enrollment };\n}\n\n// ---------------------------------------------------------------------------\n// App factory\n// ---------------------------------------------------------------------------\n\nexport function createCoordinatorApp(opts?: { dbPath?: string }): InstanceType<typeof Hono> {\n\tconst dbPath = opts?.dbPath;\n\tconst app = new Hono();\n\n\t// -----------------------------------------------------------------------\n\t// POST /v1/presence — upsert device presence (authenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.post(\"/v1/presence\", async (c) => {\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) {\n\t\t\treturn c.json({ error: \"body_too_large\" }, 413);\n\t\t}\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tif (!groupId) {\n\t\t\treturn c.json({ error: \"group_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst auth = authorizeRequest(store, {\n\t\t\t\tmethod: c.req.method,\n\t\t\t\turl: c.req.url,\n\t\t\t\tgroupId,\n\t\t\t\tbody: raw,\n\t\t\t\tdeviceId: c.req.header(\"X-Opencode-Device\") ?? null,\n\t\t\t\tsignature: c.req.header(\"X-Opencode-Signature\") ?? null,\n\t\t\t\ttimestamp: c.req.header(\"X-Opencode-Timestamp\") ?? null,\n\t\t\t\tnonce: c.req.header(\"X-Opencode-Nonce\") ?? null,\n\t\t\t});\n\t\t\tif (!auth.ok || !auth.enrollment) {\n\t\t\t\treturn c.json({ error: auth.error }, 401);\n\t\t\t}\n\n\t\t\tif (data.fingerprint && String(data.fingerprint) !== String(auth.enrollment.fingerprint)) {\n\t\t\t\treturn c.json({ error: \"fingerprint_mismatch\" }, 401);\n\t\t\t}\n\n\t\t\tconst rawAddresses = data.addresses ?? [];\n\t\t\tif (!Array.isArray(rawAddresses) || !rawAddresses.every((item) => typeof item === \"string\")) {\n\t\t\t\treturn c.json({ error: \"addresses_must_be_list_of_strings\" }, 400);\n\t\t\t}\n\n\t\t\tlet ttlS: number;\n\t\t\ttry {\n\t\t\t\tttlS = Math.max(1, Number.parseInt(String(data.ttl_s ?? 180), 10));\n\t\t\t\tif (Number.isNaN(ttlS)) {\n\t\t\t\t\treturn c.json({ error: \"ttl_s_must_be_int\" }, 400);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn c.json({ error: \"ttl_s_must_be_int\" }, 400);\n\t\t\t}\n\n\t\t\tconst response = store.upsertPresence({\n\t\t\t\tgroupId,\n\t\t\t\tdeviceId: String(auth.enrollment.device_id),\n\t\t\t\taddresses: rawAddresses as string[],\n\t\t\t\tttlS,\n\t\t\t\tcapabilities:\n\t\t\t\t\ttypeof data.capabilities === \"object\" &&\n\t\t\t\t\tdata.capabilities !== null &&\n\t\t\t\t\t!Array.isArray(data.capabilities)\n\t\t\t\t\t\t? (data.capabilities as Record<string, unknown>)\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\n\t\t\treturn c.json({ ok: true, ...response });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// GET /v1/peers — list group peers (authenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.get(\"/v1/peers\", (c) => {\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) {\n\t\t\treturn c.json({ error: \"group_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst auth = authorizeRequest(store, {\n\t\t\t\tmethod: c.req.method,\n\t\t\t\turl: c.req.url,\n\t\t\t\tgroupId,\n\t\t\t\tbody: Buffer.alloc(0),\n\t\t\t\tdeviceId: c.req.header(\"X-Opencode-Device\") ?? null,\n\t\t\t\tsignature: c.req.header(\"X-Opencode-Signature\") ?? null,\n\t\t\t\ttimestamp: c.req.header(\"X-Opencode-Timestamp\") ?? null,\n\t\t\t\tnonce: c.req.header(\"X-Opencode-Nonce\") ?? null,\n\t\t\t});\n\t\t\tif (!auth.ok || !auth.enrollment) {\n\t\t\t\treturn c.json({ error: auth.error }, 401);\n\t\t\t}\n\n\t\t\tconst items = store.listGroupPeers(groupId, String(auth.enrollment.device_id));\n\t\t\treturn c.json({ items });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// Admin routes\n\t// -----------------------------------------------------------------------\n\n\t// POST /v1/admin/devices — enroll a device\n\tapp.post(\"/v1/admin/devices\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst fingerprint = String(data.fingerprint ?? \"\").trim();\n\t\tconst publicKey = String(data.public_key ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim() || null;\n\n\t\tif (!groupId || !deviceId || !fingerprint || !publicKey) {\n\t\t\treturn c.json({ error: \"group_id_device_id_fingerprint_public_key_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tstore.createGroup(groupId);\n\t\t\tstore.enrollDevice(groupId, {\n\t\t\t\tdeviceId,\n\t\t\t\tfingerprint,\n\t\t\t\tpublicKey,\n\t\t\t\tdisplayName,\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\n\t\treturn c.json({ ok: true });\n\t});\n\n\t// GET /v1/admin/devices — list enrolled devices\n\tapp.get(\"/v1/admin/devices\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst includeDisabled = [\"1\", \"true\", \"yes\"].includes(\n\t\t\t(c.req.query(\"include_disabled\") ?? \"0\").trim().toLowerCase(),\n\t\t);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\treturn c.json({ items: store.listEnrolledDevices(groupId, includeDisabled) });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/rename\n\tapp.post(\"/v1/admin/devices/rename\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\t\tif (!displayName) {\n\t\t\treturn c.json({ error: \"display_name_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.renameDevice(groupId, deviceId, displayName);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/disable\n\tapp.post(\"/v1/admin/devices/disable\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.setDeviceEnabled(groupId, deviceId, false);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/remove\n\tapp.post(\"/v1/admin/devices/remove\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.removeDevice(groupId, deviceId);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/invites — create an invite\n\tapp.post(\"/v1/admin/invites\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst policy = String(data.policy ?? \"auto_admit\").trim();\n\t\tconst expiresAt = String(data.expires_at ?? \"\").trim();\n\t\tconst createdBy = String(data.created_by ?? \"\").trim() || null;\n\n\t\tif (!groupId || ![\"auto_admit\", \"approval_required\"].includes(policy) || !expiresAt) {\n\t\t\treturn c.json({ error: \"group_id_policy_and_expires_at_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst group = store.getGroup(groupId);\n\t\t\tif (!group) return c.json({ error: \"group_not_found\" }, 404);\n\n\t\t\tconst invite = store.createInvite({\n\t\t\t\tgroupId,\n\t\t\t\tpolicy,\n\t\t\t\texpiresAt,\n\t\t\t\tcreatedBy,\n\t\t\t});\n\n\t\t\tconst payload: InvitePayload = {\n\t\t\t\tv: 1,\n\t\t\t\tkind: \"coordinator_team_invite\",\n\t\t\t\tcoordinator_url: String(data.coordinator_url ?? \"\").trim(),\n\t\t\t\tgroup_id: groupId,\n\t\t\t\tpolicy,\n\t\t\t\ttoken: String(invite.token ?? \"\"),\n\t\t\t\texpires_at: expiresAt,\n\t\t\t\tteam_name: (invite.team_name_snapshot as string) ?? null,\n\t\t\t};\n\t\t\tconst encoded = encodeInvitePayload(payload);\n\n\t\t\t// Omit token from the returned invite object (matches Python)\n\t\t\tconst { token: _token, ...inviteWithoutToken } = invite;\n\n\t\t\treturn c.json({\n\t\t\t\tok: true,\n\t\t\t\tinvite: inviteWithoutToken,\n\t\t\t\tpayload,\n\t\t\t\tencoded,\n\t\t\t\tlink: inviteLink(encoded),\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// GET /v1/admin/invites — list invites\n\tapp.get(\"/v1/admin/invites\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst rows = store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT invite_id, group_id, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites\n\t\t\t\t\t WHERE group_id = ?\n\t\t\t\t\t ORDER BY created_at DESC`,\n\t\t\t\t)\n\t\t\t\t.all(groupId) as Record<string, unknown>[];\n\t\t\treturn c.json({ items: rows });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/join-requests/approve\n\tapp.post(\"/v1/admin/join-requests/approve\", async (c) => {\n\t\treturn handleJoinRequestReview(c, true, dbPath);\n\t});\n\n\t// POST /v1/admin/join-requests/deny\n\tapp.post(\"/v1/admin/join-requests/deny\", async (c) => {\n\t\treturn handleJoinRequestReview(c, false, dbPath);\n\t});\n\n\t// GET /v1/admin/join-requests — list join requests\n\tapp.get(\"/v1/admin/join-requests\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\treturn c.json({ items: store.listJoinRequests(groupId) });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// POST /v1/join — join via invite token (unauthenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.post(\"/v1/join\", async (c) => {\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst token = String(data.token ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst fingerprint = String(data.fingerprint ?? \"\").trim();\n\t\tconst publicKey = String(data.public_key ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim() || null;\n\n\t\tif (!token || !deviceId || !fingerprint || !publicKey) {\n\t\t\treturn c.json({ error: \"token_device_id_fingerprint_public_key_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst invite = store.getInviteByToken(token);\n\t\t\tif (!invite) return c.json({ error: \"invalid_token\" }, 404);\n\n\t\t\tif (invite.revoked_at) return c.json({ error: \"revoked_token\" }, 400);\n\n\t\t\tconst expiresAtStr = String(invite.expires_at ?? \"\");\n\t\t\tif (expiresAtStr) {\n\t\t\t\tconst expiresAt = new Date(expiresAtStr.replace(\"Z\", \"+00:00\"));\n\t\t\t\tif (expiresAt <= new Date()) {\n\t\t\t\t\treturn c.json({ error: \"expired_token\" }, 400);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst inviteGroupId = String(invite.group_id);\n\t\t\tconst existing = store.getEnrollment(inviteGroupId, deviceId);\n\t\t\tif (existing) {\n\t\t\t\treturn c.json({\n\t\t\t\t\tok: true,\n\t\t\t\t\tstatus: \"already_enrolled\",\n\t\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\t\tpolicy: invite.policy,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst invitePolicy = String(invite.policy);\n\t\t\tif (![\"auto_admit\", \"approval_required\"].includes(invitePolicy)) {\n\t\t\t\treturn c.json({ error: `unknown invite policy: ${invitePolicy}` }, 400);\n\t\t\t}\n\n\t\t\tif (invitePolicy === \"approval_required\") {\n\t\t\t\tconst request = store.createJoinRequest({\n\t\t\t\t\tgroupId: inviteGroupId,\n\t\t\t\t\tdeviceId,\n\t\t\t\t\tpublicKey,\n\t\t\t\t\tfingerprint,\n\t\t\t\t\tdisplayName,\n\t\t\t\t\ttoken,\n\t\t\t\t});\n\t\t\t\treturn c.json({\n\t\t\t\t\tok: true,\n\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\t\tpolicy: invite.policy,\n\t\t\t\t\trequest_id: request.request_id,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tstore.enrollDevice(inviteGroupId, {\n\t\t\t\tdeviceId,\n\t\t\t\tfingerprint,\n\t\t\t\tpublicKey,\n\t\t\t\tdisplayName,\n\t\t\t});\n\n\t\t\treturn c.json({\n\t\t\t\tok: true,\n\t\t\t\tstatus: \"enrolled\",\n\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\tpolicy: invite.policy,\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\treturn app;\n}\n\n// ---------------------------------------------------------------------------\n// Shared handler for approve/deny\n// ---------------------------------------------------------------------------\n\nasync function handleJoinRequestReview(c: Context, approved: boolean, dbPath: string | undefined) {\n\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\tlet data: Record<string, unknown>;\n\ttry {\n\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t}\n\n\tconst requestId = String(data.request_id ?? \"\").trim();\n\tconst reviewedBy = String(data.reviewed_by ?? \"\").trim() || null;\n\n\tif (!requestId) return c.json({ error: \"request_id_required\" }, 400);\n\n\tconst store = new CoordinatorStore(dbPath);\n\ttry {\n\t\tconst request = store.reviewJoinRequest({\n\t\t\trequestId,\n\t\t\tapproved,\n\t\t\treviewedBy,\n\t\t});\n\n\t\tif (!request) return c.json({ error: \"request_not_found\" }, 404);\n\n\t\tif (request._no_transition) {\n\t\t\treturn c.json({ error: \"request_not_pending\", status: request.status }, 409);\n\t\t}\n\n\t\treturn c.json({ ok: true, request });\n\t} finally {\n\t\tstore.close();\n\t}\n}\n","/**\n * Shared address normalization utilities for the sync system.\n *\n * Used by both coordinator-store and sync-discovery to ensure\n * consistent address handling across the sync pipeline.\n */\n\n/**\n * Normalize an address to a consistent URL form.\n *\n * Uses the built-in URL constructor for consistent normalization:\n * lowercases host, strips default ports (80/443), trims trailing slashes.\n * Returns empty string for invalid/empty input.\n */\nexport function normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\n/**\n * Produce a dedup key for an address (strips http scheme for comparison).\n */\nexport function addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst url = new URL(address);\n\t\tconst host = url.hostname.toLowerCase();\n\t\tif (host && url.port) return `${host}:${url.port}`;\n\t\tif (host) return host;\n\t} catch {\n\t\t// Not parseable\n\t}\n\treturn address;\n}\n\n/**\n * Merge two address lists, normalizing and deduplicating.\n * Existing addresses come first, then new candidates.\n */\nexport function mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n","import { networkInterfaces } from \"node:os\";\nimport { mergeAddresses } from \"./address-utils.js\";\nimport { getCodememEnvOverrides, readCodememConfigFile } from \"./observer-config.js\";\nimport type { MemoryStore } from \"./store.js\";\nimport { buildAuthHeaders } from \"./sync-auth.js\";\nimport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity, loadPublicKey } from \"./sync-identity.js\";\n\ntype ConfigRecord = Record<string, unknown>;\n\nfunction clean(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction parseIntOr(value: unknown, fallback: number): number {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return Math.trunc(value);\n\tif (typeof value === \"string\" && /^-?\\d+$/.test(value.trim()))\n\t\treturn Number.parseInt(value.trim(), 10);\n\treturn fallback;\n}\n\nfunction parseBoolOr(value: unknown, fallback: boolean): boolean {\n\tif (typeof value === \"boolean\") return value;\n\tif (typeof value === \"string\") {\n\t\tconst normalized = value.trim().toLowerCase();\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(normalized)) return true;\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(normalized)) return false;\n\t}\n\treturn fallback;\n}\n\nfunction parseStringList(value: unknown): string[] {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean);\n\t}\n\tif (typeof value === \"string\") {\n\t\treturn value\n\t\t\t.split(\",\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn [];\n}\n\nexport interface CoordinatorSyncConfig {\n\tsyncEnabled: boolean;\n\tsyncHost: string;\n\tsyncPort: number;\n\tsyncIntervalS: number;\n\tsyncAdvertise: string;\n\tsyncProjectsInclude: string[];\n\tsyncProjectsExclude: string[];\n\tsyncCoordinatorUrl: string;\n\tsyncCoordinatorGroup: string;\n\tsyncCoordinatorGroups: string[];\n\tsyncCoordinatorTimeoutS: number;\n\tsyncCoordinatorPresenceTtlS: number;\n\tsyncCoordinatorAdminSecret: string;\n}\n\ntype PresenceStatus = \"posted\" | \"not_enrolled\" | \"error\";\n\ninterface PresenceSnapshot {\n\tstatus: PresenceStatus;\n\terror: string | null;\n\tadvertisedAddresses: unknown;\n\tnextRefreshAtMs: number;\n}\n\nconst coordinatorPresenceCache = new Map<string, PresenceSnapshot>();\n\nfunction presenceCacheKey(store: MemoryStore, config: CoordinatorSyncConfig): string {\n\tconst groups = [...config.syncCoordinatorGroups].sort().join(\",\");\n\treturn `${store.dbPath}|${config.syncCoordinatorUrl}|${groups}`;\n}\n\nfunction presenceRefreshIntervalMs(config: CoordinatorSyncConfig): number {\n\tconst ttl = Math.max(1, config.syncCoordinatorPresenceTtlS);\n\tconst halfTtl = Math.floor(ttl / 2);\n\tconst refreshS = Math.max(5, Math.min(60, halfTtl > 0 ? halfTtl : 1));\n\treturn refreshS * 1000;\n}\n\nfunction presenceRetryIntervalMs(): number {\n\treturn 30_000;\n}\n\nexport function readCoordinatorSyncConfig(config?: ConfigRecord): CoordinatorSyncConfig {\n\tconst raw = { ...(config ?? readCodememConfigFile()) } as ConfigRecord;\n\tconst envOverrides = getCodememEnvOverrides();\n\tfor (const key of Object.keys(envOverrides)) {\n\t\tconst value = process.env[envOverrides[key] as string];\n\t\tif (value != null) raw[key] = value;\n\t}\n\tconst syncCoordinatorGroup = clean(raw.sync_coordinator_group);\n\tconst syncCoordinatorGroups = parseStringList(raw.sync_coordinator_groups);\n\treturn {\n\t\tsyncEnabled: parseBoolOr(raw.sync_enabled, false),\n\t\tsyncHost: clean(raw.sync_host) || \"0.0.0.0\",\n\t\tsyncPort: parseIntOr(raw.sync_port, 7337),\n\t\tsyncIntervalS: parseIntOr(raw.sync_interval_s, 120),\n\t\tsyncAdvertise: clean(raw.sync_advertise) || \"auto\",\n\t\tsyncProjectsInclude: parseStringList(raw.sync_projects_include),\n\t\tsyncProjectsExclude: parseStringList(raw.sync_projects_exclude),\n\t\tsyncCoordinatorUrl: clean(raw.sync_coordinator_url),\n\t\tsyncCoordinatorGroup,\n\t\tsyncCoordinatorGroups:\n\t\t\tsyncCoordinatorGroups.length > 0\n\t\t\t\t? syncCoordinatorGroups\n\t\t\t\t: syncCoordinatorGroup\n\t\t\t\t\t? [syncCoordinatorGroup]\n\t\t\t\t\t: [],\n\t\tsyncCoordinatorTimeoutS: parseIntOr(raw.sync_coordinator_timeout_s, 3),\n\t\tsyncCoordinatorPresenceTtlS: parseIntOr(raw.sync_coordinator_presence_ttl_s, 180),\n\t\tsyncCoordinatorAdminSecret: clean(raw.sync_coordinator_admin_secret),\n\t};\n}\n\nexport function coordinatorEnabled(config: CoordinatorSyncConfig): boolean {\n\treturn Boolean(config.syncCoordinatorUrl && config.syncCoordinatorGroups.length > 0);\n}\n\nfunction advertisedSyncAddresses(config: CoordinatorSyncConfig): string[] {\n\tconst advertise = config.syncAdvertise.toLowerCase();\n\tif (advertise && advertise !== \"auto\" && advertise !== \"default\") {\n\t\treturn mergeAddresses(\n\t\t\t[],\n\t\t\tadvertise\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((item) => item.trim())\n\t\t\t\t.filter(Boolean),\n\t\t);\n\t}\n\tif (config.syncHost && config.syncHost !== \"0.0.0.0\") {\n\t\treturn [`${config.syncHost}:${config.syncPort}`];\n\t}\n\tconst addresses = Object.values(networkInterfaces())\n\t\t.flatMap((entries) => entries ?? [])\n\t\t.filter((entry) => !entry.internal)\n\t\t.map((entry) => entry.address)\n\t\t.filter((address) => address && address !== \"127.0.0.1\" && address !== \"::1\")\n\t\t.map((address) => `${address}:${config.syncPort}`);\n\treturn [...new Set(addresses)];\n}\n\nexport async function registerCoordinatorPresence(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<{ groups: string[]; responses: Record<string, unknown>[] } | null> {\n\tif (!coordinatorEnabled(config)) return null;\n\tconst keysDir = process.env.CODEMEM_KEYS_DIR?.trim() || undefined;\n\tconst [deviceId, fingerprint] = ensureDeviceIdentity(store.db, { keysDir });\n\tconst publicKey = loadPublicKey(keysDir);\n\tif (!publicKey) throw new Error(\"public key missing\");\n\tconst baseUrl = buildBaseUrl(config.syncCoordinatorUrl);\n\tconst payload = {\n\t\tfingerprint,\n\t\tpublic_key: publicKey,\n\t\taddresses: advertisedSyncAddresses(config),\n\t\tttl_s: Math.max(1, config.syncCoordinatorPresenceTtlS),\n\t};\n\tconst responses: Record<string, unknown>[] = [];\n\tfor (const groupId of config.syncCoordinatorGroups) {\n\t\tconst groupPayload = { ...payload, group_id: groupId };\n\t\tconst bodyBytes = Buffer.from(JSON.stringify(groupPayload), \"utf8\");\n\t\tconst url = `${baseUrl}/v1/presence`;\n\t\tconst headers = buildAuthHeaders({ deviceId, method: \"POST\", url, bodyBytes, keysDir });\n\t\tconst [status, response] = await requestJson(\"POST\", url, {\n\t\t\theaders,\n\t\t\tbody: groupPayload,\n\t\t\tbodyBytes,\n\t\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t\t});\n\t\tif (status !== 200 || !response) {\n\t\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\t\tthrow new Error(`coordinator presence failed (${status}: ${detail})`);\n\t\t}\n\t\tresponses.push(response);\n\t}\n\treturn { groups: config.syncCoordinatorGroups, responses };\n}\n\nexport async function lookupCoordinatorPeers(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>[]> {\n\tif (!coordinatorEnabled(config)) return [];\n\tconst keysDir = process.env.CODEMEM_KEYS_DIR?.trim() || undefined;\n\tconst [deviceId] = ensureDeviceIdentity(store.db, { keysDir });\n\tconst baseUrl = buildBaseUrl(config.syncCoordinatorUrl);\n\tconst merged = new Map<string, Record<string, unknown>>();\n\tfor (const groupId of config.syncCoordinatorGroups) {\n\t\tconst url = `${baseUrl}/v1/peers?group_id=${encodeURIComponent(groupId)}`;\n\t\tconst headers = buildAuthHeaders({\n\t\t\tdeviceId,\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\tkeysDir,\n\t\t});\n\t\tconst [status, response] = await requestJson(\"GET\", url, {\n\t\t\theaders,\n\t\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t\t});\n\t\tif (status !== 200 || !response) {\n\t\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\t\tthrow new Error(`coordinator lookup failed (${status}: ${detail})`);\n\t\t}\n\t\tconst items = Array.isArray(response.items) ? response.items : [];\n\t\tfor (const item of items) {\n\t\t\tif (!item || typeof item !== \"object\") continue;\n\t\t\tconst record = item as Record<string, unknown>;\n\t\t\tconst device = clean(record.device_id);\n\t\t\tconst fingerprint = clean(record.fingerprint);\n\t\t\tif (!device) continue;\n\t\t\tconst key = `${device}:${fingerprint}`;\n\t\t\tconst existing = merged.get(key);\n\t\t\tif (!existing) {\n\t\t\t\tmerged.set(key, {\n\t\t\t\t\t...record,\n\t\t\t\t\taddresses: mergeAddresses(\n\t\t\t\t\t\t[],\n\t\t\t\t\t\tArray.isArray(record.addresses)\n\t\t\t\t\t\t\t? record.addresses.filter((x): x is string => typeof x === \"string\")\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t),\n\t\t\t\t\tgroups: [groupId],\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\texisting.addresses = mergeAddresses(\n\t\t\t\t(Array.isArray(existing.addresses) ? existing.addresses : []) as string[],\n\t\t\t\tArray.isArray(record.addresses)\n\t\t\t\t\t? record.addresses.filter((x): x is string => typeof x === \"string\")\n\t\t\t\t\t: [],\n\t\t\t);\n\t\t\texisting.groups = mergeAddresses(\n\t\t\t\t(Array.isArray(existing.groups) ? existing.groups : []) as string[],\n\t\t\t\t[groupId],\n\t\t\t);\n\t\t\texisting.stale = Boolean(existing.stale) && Boolean(record.stale);\n\t\t\tif (clean(record.last_seen_at) > clean(existing.last_seen_at)) {\n\t\t\t\texisting.last_seen_at = record.last_seen_at;\n\t\t\t\texisting.expires_at = record.expires_at;\n\t\t\t}\n\t\t}\n\t}\n\treturn [...merged.values()];\n}\n\nexport async function coordinatorStatusSnapshot(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>> {\n\tconst pairedPeerCount = Number(\n\t\t(\n\t\t\tstore.db.prepare(\"SELECT COUNT(1) AS total FROM sync_peers\").get() as\n\t\t\t\t| { total?: number }\n\t\t\t\t| undefined\n\t\t)?.total ?? 0,\n\t);\n\tif (!coordinatorEnabled(config)) {\n\t\treturn {\n\t\t\tenabled: false,\n\t\t\tconfigured: false,\n\t\t\tgroups: config.syncCoordinatorGroups,\n\t\t\tpaired_peer_count: pairedPeerCount,\n\t\t};\n\t}\n\tconst snapshot: Record<string, unknown> = {\n\t\tenabled: true,\n\t\tconfigured: true,\n\t\tcoordinator_url: config.syncCoordinatorUrl,\n\t\tgroups: config.syncCoordinatorGroups,\n\t\tpaired_peer_count: pairedPeerCount,\n\t\tpresence_status: \"unknown\",\n\t\tpresence_error: null,\n\t\tadvertised_addresses: [],\n\t\tfresh_peer_count: 0,\n\t\tstale_peer_count: 0,\n\t\tdiscovered_peer_count: 0,\n\t};\n\tconst cacheKey = presenceCacheKey(store, config);\n\tconst now = Date.now();\n\tconst cachedPresence = coordinatorPresenceCache.get(cacheKey);\n\tif (cachedPresence && now < cachedPresence.nextRefreshAtMs) {\n\t\tsnapshot.presence_status = cachedPresence.status;\n\t\tsnapshot.presence_error = cachedPresence.error;\n\t\tsnapshot.advertised_addresses = cachedPresence.advertisedAddresses;\n\t} else {\n\t\ttry {\n\t\t\tconst registration = await registerCoordinatorPresence(store, config);\n\t\t\tconst first = registration?.responses?.[0];\n\t\t\tconst advertisedAddresses =\n\t\t\t\tfirst && typeof first === \"object\"\n\t\t\t\t\t? ((first as Record<string, unknown>).addresses ?? [])\n\t\t\t\t\t: [];\n\t\t\tsnapshot.presence_status = \"posted\";\n\t\t\tsnapshot.advertised_addresses = advertisedAddresses;\n\t\t\tcoordinatorPresenceCache.set(cacheKey, {\n\t\t\t\tstatus: \"posted\",\n\t\t\t\terror: null,\n\t\t\t\tadvertisedAddresses,\n\t\t\t\tnextRefreshAtMs: now + presenceRefreshIntervalMs(config),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconst status: PresenceStatus = message.includes(\"unknown_device\") ? \"not_enrolled\" : \"error\";\n\t\t\tconst nextRefreshAtMs = status === \"not_enrolled\" ? now : now + presenceRetryIntervalMs();\n\t\t\tsnapshot.presence_status = status;\n\t\t\tsnapshot.presence_error = message;\n\t\t\tcoordinatorPresenceCache.set(cacheKey, {\n\t\t\t\tstatus,\n\t\t\t\terror: message,\n\t\t\t\tadvertisedAddresses: [],\n\t\t\t\tnextRefreshAtMs,\n\t\t\t});\n\t\t}\n\t}\n\ttry {\n\t\tconst peers = await lookupCoordinatorPeers(store, config);\n\t\tsnapshot.discovered_peer_count = peers.length;\n\t\tsnapshot.fresh_peer_count = peers.filter((peer) => !peer.stale).length;\n\t\tsnapshot.stale_peer_count = peers.filter((peer) => Boolean(peer.stale)).length;\n\t} catch (error) {\n\t\tsnapshot.lookup_error = error instanceof Error ? error.message : String(error);\n\t}\n\treturn snapshot;\n}\n\nexport async function listCoordinatorJoinRequests(\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>[]> {\n\tconst groupId = config.syncCoordinatorGroup || config.syncCoordinatorGroups[0] || \"\";\n\tif (!groupId || !config.syncCoordinatorUrl || !config.syncCoordinatorAdminSecret) return [];\n\tconst url = `${buildBaseUrl(config.syncCoordinatorUrl)}/v1/admin/join-requests?group_id=${encodeURIComponent(groupId)}`;\n\tconst [status, response] = await requestJson(\"GET\", url, {\n\t\theaders: { \"X-Codemem-Coordinator-Admin\": config.syncCoordinatorAdminSecret },\n\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t});\n\tif (status !== 200 || !response) return [];\n\treturn Array.isArray(response.items)\n\t\t? response.items.filter(\n\t\t\t\t(item): item is Record<string, unknown> => Boolean(item) && typeof item === \"object\",\n\t\t\t)\n\t\t: [];\n}\n","/**\n * Embedding primitives for semantic search.\n *\n * Ports codemem/semantic.py — text chunking, hashing, and embedding via a\n * pluggable client interface. The default client uses `@xenova/transformers`\n * (same BAAI/bge-small-en-v1.5 model as Python's fastembed). When the\n * embedding runtime is unavailable the helpers return empty arrays and callers\n * fall back to FTS-only retrieval.\n *\n * Embeddings are always disabled when CODEMEM_EMBEDDING_DISABLED=1.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { isEmbeddingDisabled } from \"./db.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Minimal interface a concrete embedding backend must satisfy. */\nexport interface EmbeddingClient {\n\treadonly model: string;\n\treadonly dimensions: number;\n\tembed(texts: string[]): Promise<Float32Array[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Text helpers (ports of semantic.py)\n// ---------------------------------------------------------------------------\n\n/** SHA-256 hex digest of UTF-8 encoded text. */\nexport function hashText(text: string): string {\n\treturn createHash(\"sha256\").update(text, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Split long text into ≤ `maxChars` chunks, preferring paragraph then\n * sentence boundaries. Matches Python's `chunk_text()`.\n */\nexport function chunkText(text: string, maxChars = 1200): string[] {\n\tconst cleaned = text.trim();\n\tif (!cleaned) return [];\n\tif (cleaned.length <= maxChars) return [cleaned];\n\n\tconst paragraphs = cleaned\n\t\t.split(/\\n{2,}/)\n\t\t.map((p) => p.trim())\n\t\t.filter(Boolean);\n\tconst chunks: string[] = [];\n\tlet buffer: string[] = [];\n\tlet bufferLen = 0;\n\n\tfor (const paragraph of paragraphs) {\n\t\tif (bufferLen + paragraph.length + 2 <= maxChars) {\n\t\t\tbuffer.push(paragraph);\n\t\t\tbufferLen += paragraph.length + 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (buffer.length > 0) {\n\t\t\tchunks.push(buffer.join(\"\\n\\n\"));\n\t\t\tbuffer = [];\n\t\t\tbufferLen = 0;\n\t\t}\n\t\tif (paragraph.length <= maxChars) {\n\t\t\tchunks.push(paragraph);\n\t\t\tcontinue;\n\t\t}\n\t\t// Split long paragraph by sentence\n\t\tconst sentences = paragraph\n\t\t\t.split(/(?<=[.!?])\\s+/)\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t\tconst sentBuf: string[] = [];\n\t\tlet sentLen = 0;\n\t\tfor (const sentence of sentences) {\n\t\t\t// Hard-split sentences that exceed maxChars on their own\n\t\t\tif (sentence.length > maxChars) {\n\t\t\t\tif (sentBuf.length > 0) {\n\t\t\t\t\tchunks.push(sentBuf.join(\" \"));\n\t\t\t\t\tsentBuf.length = 0;\n\t\t\t\t\tsentLen = 0;\n\t\t\t\t}\n\t\t\t\tfor (let i = 0; i < sentence.length; i += maxChars) {\n\t\t\t\t\tchunks.push(sentence.slice(i, i + maxChars));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (sentLen + sentence.length + 1 <= maxChars) {\n\t\t\t\tsentBuf.push(sentence);\n\t\t\t\tsentLen += sentence.length + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (sentBuf.length > 0) chunks.push(sentBuf.join(\" \"));\n\t\t\tsentBuf.length = 0;\n\t\t\tsentBuf.push(sentence);\n\t\t\tsentLen = sentence.length;\n\t\t}\n\t\tif (sentBuf.length > 0) chunks.push(sentBuf.join(\" \"));\n\t}\n\tif (buffer.length > 0) chunks.push(buffer.join(\"\\n\\n\"));\n\treturn chunks;\n}\n\n// ---------------------------------------------------------------------------\n// Lazy singleton client\n// ---------------------------------------------------------------------------\n\nlet _client: EmbeddingClient | null | undefined;\n\n/** Reset the singleton (for tests). */\nexport function _resetEmbeddingClient(): void {\n\t_client = undefined;\n}\n\n/**\n * Get the shared embedding client, creating it lazily on first call.\n * Returns null when embeddings are disabled or the runtime is unavailable.\n */\nexport async function getEmbeddingClient(): Promise<EmbeddingClient | null> {\n\tif (_client !== undefined) return _client;\n\tif (isEmbeddingDisabled()) {\n\t\t_client = null;\n\t\treturn null;\n\t}\n\tconst model = process.env.CODEMEM_EMBEDDING_MODEL || \"BAAI/bge-small-en-v1.5\";\n\ttry {\n\t\t_client = await createTransformersClient(model);\n\t} catch {\n\t\t_client = null;\n\t}\n\treturn _client;\n}\n\n/**\n * Embed texts using the shared client.\n * Returns an empty array when embeddings are unavailable.\n */\nexport async function embedTexts(texts: string[]): Promise<Float32Array[]> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return [];\n\treturn client.embed(texts);\n}\n\n// ---------------------------------------------------------------------------\n// @xenova/transformers backend (runtime-detected)\n// ---------------------------------------------------------------------------\n\nasync function createTransformersClient(model: string): Promise<EmbeddingClient> {\n\t// Dynamic import so the package is optional at install time\n\tconst { pipeline } = await import(\"@xenova/transformers\");\n\tconst extractor = await pipeline(\"feature-extraction\", model, {\n\t\tquantized: true,\n\t});\n\n\t// Infer dimensions from a probe embedding\n\tconst probe = await extractor(\"probe\", { pooling: \"mean\", normalize: true });\n\tconst dims = probe.dims?.at(-1) ?? 384;\n\n\treturn {\n\t\tmodel,\n\t\tdimensions: dims,\n\t\tasync embed(texts: string[]): Promise<Float32Array[]> {\n\t\t\tconst results: Float32Array[] = [];\n\t\t\tfor (const text of texts) {\n\t\t\t\tconst output = await extractor(text, { pooling: \"mean\", normalize: true });\n\t\t\t\t// output.data is a Float32Array of shape [1, dims]\n\t\t\t\tresults.push(new Float32Array(output.data));\n\t\t\t}\n\t\t\treturn results;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Serialization helpers (sqlite-vec wire format)\n// ---------------------------------------------------------------------------\n\n/** Serialize a Float32Array to a little-endian Buffer for sqlite-vec. */\nexport function serializeFloat32(vector: Float32Array): Buffer {\n\tconst buf = Buffer.alloc(vector.length * 4);\n\tfor (let i = 0; i < vector.length; i++) {\n\t\tbuf.writeFloatLE(vector[i] ?? 0, i * 4);\n\t}\n\treturn buf;\n}\n","import { readFileSync } from \"node:fs\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport {\n\tassertSchemaReady,\n\tconnect,\n\ttype Database,\n\tfromJson,\n\tresolveDbPath,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nimport { expandUserPath } from \"./observer-config.js\";\nimport { projectColumnClause, resolveProject as resolveProjectName } from \"./project.js\";\nimport * as schema from \"./schema.js\";\n\ntype JsonObject = Record<string, unknown>;\ntype MemoryInsert = typeof schema.memoryItems.$inferInsert;\n\nexport interface ExportOptions {\n\tdbPath?: string;\n\tproject?: string | null;\n\tallProjects?: boolean;\n\tincludeInactive?: boolean;\n\tsince?: string | null;\n\tcwd?: string;\n}\n\nexport interface ImportOptions {\n\tdbPath?: string;\n\tremapProject?: string | null;\n\tdryRun?: boolean;\n}\n\nexport interface ExportPayload {\n\tversion: \"1.0\";\n\texported_at: string;\n\texport_metadata: {\n\t\ttool_version: \"codemem\";\n\t\tprojects: string[];\n\t\ttotal_memories: number;\n\t\ttotal_sessions: number;\n\t\tinclude_inactive: boolean;\n\t\tfilters: JsonObject;\n\t};\n\tsessions: JsonObject[];\n\tmemory_items: JsonObject[];\n\tsession_summaries: JsonObject[];\n\tuser_prompts: JsonObject[];\n}\n\nexport interface ImportResult {\n\tsessions: number;\n\tuser_prompts: number;\n\tmemory_items: number;\n\tsession_summaries: number;\n\tdryRun: boolean;\n}\n\nconst SUMMARY_METADATA_KEYS = [\n\t\"request\",\n\t\"investigated\",\n\t\"learned\",\n\t\"completed\",\n\t\"next_steps\",\n\t\"notes\",\n\t\"files_read\",\n\t\"files_modified\",\n\t\"prompt_number\",\n\t\"request_original\",\n\t\"discovery_tokens\",\n\t\"discovery_source\",\n] as const;\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction nowEpochMs(): number {\n\treturn Date.now();\n}\n\nfunction parseDbObject(raw: unknown): unknown {\n\tif (typeof raw !== \"string\" || raw.trim().length === 0) return null;\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn fromJson(raw);\n\t}\n}\n\nfunction parseRowJsonFields<T extends JsonObject>(row: T, fields: string[]): JsonObject {\n\tconst parsed: JsonObject = { ...row };\n\tfor (const field of fields) {\n\t\tparsed[field] = parseDbObject(row[field]);\n\t}\n\treturn parsed;\n}\n\nfunction normalizeImportMetadata(importMetadata: unknown): JsonObject | null {\n\tif (importMetadata == null) return null;\n\tif (typeof importMetadata === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(importMetadata) as unknown;\n\t\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t\t? (parsed as JsonObject)\n\t\t\t\t: null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn typeof importMetadata === \"object\" && !Array.isArray(importMetadata)\n\t\t? ({ ...(importMetadata as JsonObject) } as JsonObject)\n\t\t: null;\n}\n\nexport function buildImportKey(\n\tsource: string,\n\trecordType: string,\n\toriginalId: unknown,\n\tparts?: { project?: string | null; createdAt?: string | null; sourceDb?: string | null },\n): string {\n\tconst values = [source, recordType, String(originalId ?? \"unknown\")];\n\tif (parts?.project) values.push(parts.project);\n\tif (parts?.createdAt) values.push(parts.createdAt);\n\tif (parts?.sourceDb) values.push(parts.sourceDb);\n\treturn values.join(\"|\");\n}\n\nexport function mergeSummaryMetadata(metadata: JsonObject, importMetadata: unknown): JsonObject {\n\tconst parsed = normalizeImportMetadata(importMetadata);\n\tif (!parsed) return metadata;\n\tconst merged: JsonObject = { ...metadata };\n\tfor (const key of SUMMARY_METADATA_KEYS) {\n\t\tif (!(key in parsed)) continue;\n\t\tconst current = merged[key];\n\t\tlet shouldFill = !(key in merged);\n\t\tif (!shouldFill) {\n\t\t\tif (key === \"discovery_tokens\" || key === \"prompt_number\") {\n\t\t\t\tshouldFill = current == null;\n\t\t\t} else if (typeof current === \"string\") {\n\t\t\t\tshouldFill = current.trim().length === 0;\n\t\t\t} else if (Array.isArray(current)) {\n\t\t\t\tshouldFill = current.length === 0;\n\t\t\t} else {\n\t\t\t\tshouldFill = current == null;\n\t\t\t}\n\t\t}\n\t\tif (shouldFill) merged[key] = parsed[key];\n\t}\n\tmerged.import_metadata = importMetadata;\n\treturn merged;\n}\n\nfunction normalizeImportedProject(project: unknown): string | null {\n\tif (typeof project !== \"string\") return null;\n\tconst trimmed = project.trim();\n\tif (!trimmed) return null;\n\tif (/[\\\\/]/.test(trimmed)) {\n\t\tconst normalized = trimmed.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\t\tconst parts = normalized.split(\"/\");\n\t\treturn parts[parts.length - 1] || null;\n\t}\n\treturn trimmed;\n}\n\nfunction resolveExportProject(opts: ExportOptions): string | null {\n\tif (opts.allProjects) return null;\n\treturn resolveProjectName(opts.cwd ?? process.cwd(), opts.project ?? null);\n}\n\nfunction querySessions(db: Database, project: string | null, since: string | null): JsonObject[] {\n\tlet sql = \"SELECT * FROM sessions\";\n\tconst params: unknown[] = [];\n\tconst clauses: string[] = [];\n\tif (project) {\n\t\tconst clause = projectColumnClause(\"project\", project);\n\t\tif (clause.clause) {\n\t\t\tclauses.push(clause.clause);\n\t\t\tparams.push(...clause.params);\n\t\t}\n\t}\n\tif (since) {\n\t\tclauses.push(\"started_at >= ?\");\n\t\tparams.push(since);\n\t}\n\tif (clauses.length > 0) sql += ` WHERE ${clauses.join(\" AND \")}`;\n\tsql += \" ORDER BY started_at ASC\";\n\tconst rows = db.prepare(sql).all(...params) as JsonObject[];\n\treturn rows.map((row) => parseRowJsonFields(row, [\"metadata_json\"]));\n}\n\nfunction fetchBySessionIds(\n\tdb: Database,\n\ttable: string,\n\tsessionIds: number[],\n\torderBy: string,\n\textraWhere = \"\",\n): JsonObject[] {\n\tif (sessionIds.length === 0) return [];\n\tconst placeholders = sessionIds.map(() => \"?\").join(\",\");\n\tconst sql = `SELECT * FROM ${table} WHERE session_id IN (${placeholders})${extraWhere} ORDER BY ${orderBy}`;\n\treturn db.prepare(sql).all(...sessionIds) as JsonObject[];\n}\n\nexport function exportMemories(opts: ExportOptions = {}): ExportPayload {\n\tconst db = connect(resolveDbPath(opts.dbPath));\n\ttry {\n\t\tassertSchemaReady(db);\n\t\tconst resolvedProject = resolveExportProject(opts);\n\t\tconst filters: JsonObject = {};\n\t\tif (resolvedProject) filters.project = resolvedProject;\n\t\tif (opts.since) filters.since = opts.since;\n\n\t\tconst sessions = querySessions(db, resolvedProject, opts.since ?? null);\n\t\tconst sessionIds = sessions.map((row) => Number(row.id)).filter(Number.isFinite);\n\n\t\tconst memories = fetchBySessionIds(\n\t\t\tdb,\n\t\t\t\"memory_items\",\n\t\t\tsessionIds,\n\t\t\t\"created_at ASC\",\n\t\t\topts.includeInactive ? \"\" : \" AND active = 1\",\n\t\t).map((row) =>\n\t\t\tparseRowJsonFields(row, [\n\t\t\t\t\"metadata_json\",\n\t\t\t\t\"facts\",\n\t\t\t\t\"concepts\",\n\t\t\t\t\"files_read\",\n\t\t\t\t\"files_modified\",\n\t\t\t]),\n\t\t);\n\n\t\tconst summaries = fetchBySessionIds(\n\t\t\tdb,\n\t\t\t\"session_summaries\",\n\t\t\tsessionIds,\n\t\t\t\"created_at_epoch ASC\",\n\t\t).map((row) => parseRowJsonFields(row, [\"metadata_json\", \"files_read\", \"files_edited\"]));\n\n\t\tconst prompts = fetchBySessionIds(db, \"user_prompts\", sessionIds, \"created_at_epoch ASC\").map(\n\t\t\t(row) => parseRowJsonFields(row, [\"metadata_json\"]),\n\t\t);\n\n\t\tconst promptImportKeys = new Map<number, string>();\n\t\tfor (const prompt of prompts) {\n\t\t\tif (typeof prompt.id === \"number\" && typeof prompt.import_key === \"string\") {\n\t\t\t\tpromptImportKeys.set(prompt.id, prompt.import_key);\n\t\t\t}\n\t\t}\n\t\tfor (const memory of memories) {\n\t\t\tif (typeof memory.user_prompt_id === \"number\") {\n\t\t\t\tmemory.user_prompt_import_key = promptImportKeys.get(memory.user_prompt_id) ?? null;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tversion: \"1.0\",\n\t\t\texported_at: nowIso(),\n\t\t\texport_metadata: {\n\t\t\t\ttool_version: \"codemem\",\n\t\t\t\tprojects: [...new Set(sessions.map((s) => String(s.project ?? \"\")).filter(Boolean))],\n\t\t\t\ttotal_memories: memories.length,\n\t\t\t\ttotal_sessions: sessions.length,\n\t\t\t\tinclude_inactive: Boolean(opts.includeInactive),\n\t\t\t\tfilters,\n\t\t\t},\n\t\t\tsessions,\n\t\t\tmemory_items: memories,\n\t\t\tsession_summaries: summaries,\n\t\t\tuser_prompts: prompts,\n\t\t};\n\t} finally {\n\t\tdb.close();\n\t}\n}\n\nexport function readImportPayload(inputFile: string): ExportPayload {\n\tconst raw =\n\t\tinputFile === \"-\" ? readFileSync(0, \"utf8\") : readFileSync(expandUserPath(inputFile), \"utf8\");\n\tconst parsed = JSON.parse(raw) as unknown;\n\tif (parsed == null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Import payload must be a JSON object\");\n\t}\n\tconst payload = parsed as ExportPayload;\n\tif (payload.version !== \"1.0\") {\n\t\tthrow new Error(\n\t\t\t`Unsupported export version: ${String((parsed as JsonObject).version ?? \"unknown\")}`,\n\t\t);\n\t}\n\treturn payload;\n}\n\nfunction findImportedId(db: Database, table: string, importKey: string): number | null {\n\tconst row = db.prepare(`SELECT id FROM ${table} WHERE import_key = ? LIMIT 1`).get(importKey) as\n\t\t| { id: number }\n\t\t| undefined;\n\treturn row?.id ?? null;\n}\n\nfunction nextUserName(): string {\n\treturn process.env.USER?.trim() || process.env.USERNAME?.trim() || \"import\";\n}\n\ntype DrizzleDb = ReturnType<typeof drizzle>;\n\nfunction insertSession(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: typeof row.started_at === \"string\" ? row.started_at : nowIso(),\n\t\t\tended_at: typeof row.ended_at === \"string\" ? row.ended_at : null,\n\t\t\tcwd: String(row.cwd ?? process.cwd()),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\tgit_remote: row.git_remote == null ? null : String(row.git_remote),\n\t\t\tgit_branch: row.git_branch == null ? null : String(row.git_branch),\n\t\t\tuser: String(row.user ?? nextUserName()),\n\t\t\ttool_version: String(row.tool_version ?? \"import\"),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"session insert returned no id\");\n\treturn id;\n}\n\nfunction insertPrompt(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.userPrompts)\n\t\t.values({\n\t\t\tsession_id: Number(row.session_id),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\tprompt_text: String(row.prompt_text ?? \"\"),\n\t\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : nowIso(),\n\t\t\tcreated_at_epoch:\n\t\t\t\ttypeof row.created_at_epoch === \"number\" ? row.created_at_epoch : nowEpochMs(),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.userPrompts.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"prompt insert returned no id\");\n\treturn id;\n}\n\nfunction insertMemory(d: DrizzleDb, row: JsonObject): number {\n\tconst now = nowIso();\n\tconst parsedActive = row.active == null ? 1 : Number(row.active);\n\tconst active = Number.isFinite(parsedActive) ? parsedActive : 1;\n\tconst deletedAt =\n\t\ttypeof row.deleted_at === \"string\" && row.deleted_at.trim().length > 0 ? row.deleted_at : null;\n\tconst values: MemoryInsert = {\n\t\tsession_id: Number(row.session_id),\n\t\tkind: String(row.kind ?? \"observation\"),\n\t\ttitle: String(row.title ?? \"Untitled\"),\n\t\tsubtitle: row.subtitle == null ? null : String(row.subtitle),\n\t\tbody_text: String(row.body_text ?? row.narrative ?? \"\"),\n\t\tconfidence: Number(row.confidence ?? 0.5),\n\t\ttags_text: String(row.tags_text ?? \"\"),\n\t\tactive,\n\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : now,\n\t\tupdated_at: typeof row.updated_at === \"string\" ? row.updated_at : now,\n\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\tactor_id: row.actor_id == null ? null : String(row.actor_id),\n\t\tactor_display_name: row.actor_display_name == null ? null : String(row.actor_display_name),\n\t\tvisibility: row.visibility == null ? null : String(row.visibility),\n\t\tworkspace_id: row.workspace_id == null ? null : String(row.workspace_id),\n\t\tworkspace_kind: row.workspace_kind == null ? null : String(row.workspace_kind),\n\t\torigin_device_id: row.origin_device_id == null ? null : String(row.origin_device_id),\n\t\torigin_source: row.origin_source == null ? null : String(row.origin_source),\n\t\ttrust_state: row.trust_state == null ? null : String(row.trust_state),\n\t\tfacts: toJsonNullable(row.facts),\n\t\tnarrative: row.narrative == null ? null : String(row.narrative),\n\t\tconcepts: toJsonNullable(row.concepts),\n\t\tfiles_read: toJsonNullable(row.files_read),\n\t\tfiles_modified: toJsonNullable(row.files_modified),\n\t\tuser_prompt_id: row.user_prompt_id == null ? null : Number(row.user_prompt_id),\n\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\tdeleted_at: deletedAt,\n\t\trev: Number(row.rev ?? 1),\n\t\timport_key: String(row.import_key),\n\t};\n\tconst rows = d\n\t\t.insert(schema.memoryItems)\n\t\t.values(values)\n\t\t.returning({ id: schema.memoryItems.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"memory insert returned no id\");\n\treturn id;\n}\n\nfunction insertSummary(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.sessionSummaries)\n\t\t.values({\n\t\t\tsession_id: Number(row.session_id),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\trequest: String(row.request ?? \"\"),\n\t\t\tinvestigated: String(row.investigated ?? \"\"),\n\t\t\tlearned: String(row.learned ?? \"\"),\n\t\t\tcompleted: String(row.completed ?? \"\"),\n\t\t\tnext_steps: String(row.next_steps ?? \"\"),\n\t\t\tnotes: String(row.notes ?? \"\"),\n\t\t\tfiles_read: toJsonNullable(row.files_read),\n\t\t\tfiles_edited: toJsonNullable(row.files_edited),\n\t\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : nowIso(),\n\t\t\tcreated_at_epoch:\n\t\t\t\ttypeof row.created_at_epoch === \"number\" ? row.created_at_epoch : nowEpochMs(),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.sessionSummaries.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"summary insert returned no id\");\n\treturn id;\n}\n\nexport function importMemories(payload: ExportPayload, opts: ImportOptions = {}): ImportResult {\n\tconst sessionsData = Array.isArray(payload.sessions) ? payload.sessions : [];\n\tconst memoriesData = Array.isArray(payload.memory_items) ? payload.memory_items : [];\n\tconst summariesData = Array.isArray(payload.session_summaries) ? payload.session_summaries : [];\n\tconst promptsData = Array.isArray(payload.user_prompts) ? payload.user_prompts : [];\n\n\tif (opts.dryRun) {\n\t\treturn {\n\t\t\tsessions: sessionsData.length,\n\t\t\tuser_prompts: promptsData.length,\n\t\t\tmemory_items: memoriesData.length,\n\t\t\tsession_summaries: summariesData.length,\n\t\t\tdryRun: true,\n\t\t};\n\t}\n\n\tconst db = connect(resolveDbPath(opts.dbPath));\n\ttry {\n\t\tassertSchemaReady(db);\n\t\tconst d = drizzle(db, { schema });\n\t\treturn db.transaction(() => {\n\t\t\tconst sessionMapping = new Map<number, number>();\n\t\t\tconst promptMapping = new Map<number, number>();\n\t\t\tconst promptImportKeyMapping = new Map<string, number>();\n\t\t\tlet importedSessions = 0;\n\t\t\tlet importedPrompts = 0;\n\t\t\tlet importedMemories = 0;\n\t\t\tlet importedSummaries = 0;\n\n\t\t\tfor (const session of sessionsData) {\n\t\t\t\tconst oldSessionId = Number(session.id);\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(session.project);\n\t\t\t\tconst importKey = buildImportKey(\"export\", \"session\", session.id, {\n\t\t\t\t\tproject,\n\t\t\t\t\tcreatedAt: typeof session.started_at === \"string\" ? session.started_at : null,\n\t\t\t\t});\n\t\t\t\tconst existingId = findImportedId(db, \"sessions\", importKey);\n\t\t\t\tif (existingId != null) {\n\t\t\t\t\tsessionMapping.set(oldSessionId, existingId);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_session_id: session.id,\n\t\t\t\t\toriginal_started_at: session.started_at ?? null,\n\t\t\t\t\toriginal_ended_at: session.ended_at ?? null,\n\t\t\t\t\timport_metadata: session.metadata_json ?? null,\n\t\t\t\t\timport_key: importKey,\n\t\t\t\t};\n\t\t\t\tconst newId = insertSession(d, {\n\t\t\t\t\t...session,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: importKey,\n\t\t\t\t});\n\t\t\t\tsessionMapping.set(oldSessionId, newId);\n\t\t\t\timportedSessions += 1;\n\t\t\t}\n\n\t\t\tfor (const prompt of promptsData) {\n\t\t\t\tconst oldSessionId = Number(prompt.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(prompt.project);\n\t\t\t\tconst promptImportKey =\n\t\t\t\t\ttypeof prompt.import_key === \"string\" && prompt.import_key.trim()\n\t\t\t\t\t\t? prompt.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"prompt\", prompt.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof prompt.created_at === \"string\" ? prompt.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tconst existingId = findImportedId(db, \"user_prompts\", promptImportKey);\n\t\t\t\tif (existingId != null) {\n\t\t\t\t\tif (typeof prompt.id === \"number\") promptMapping.set(prompt.id, existingId);\n\t\t\t\t\tpromptImportKeyMapping.set(promptImportKey, existingId);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_prompt_id: prompt.id ?? null,\n\t\t\t\t\toriginal_created_at: prompt.created_at ?? null,\n\t\t\t\t\timport_metadata: prompt.metadata_json ?? null,\n\t\t\t\t\timport_key: promptImportKey,\n\t\t\t\t};\n\t\t\t\tconst newId = insertPrompt(d, {\n\t\t\t\t\t...prompt,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: promptImportKey,\n\t\t\t\t});\n\t\t\t\tif (typeof prompt.id === \"number\") promptMapping.set(prompt.id, newId);\n\t\t\t\tpromptImportKeyMapping.set(promptImportKey, newId);\n\t\t\t\timportedPrompts += 1;\n\t\t\t}\n\n\t\t\tfor (const memory of memoriesData) {\n\t\t\t\tconst oldSessionId = Number(memory.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(memory.project);\n\t\t\t\tconst memoryImportKey =\n\t\t\t\t\ttypeof memory.import_key === \"string\" && memory.import_key.trim()\n\t\t\t\t\t\t? memory.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"memory\", memory.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof memory.created_at === \"string\" ? memory.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tif (findImportedId(db, \"memory_items\", memoryImportKey) != null) continue;\n\n\t\t\t\tlet linkedPromptId: number | null = null;\n\t\t\t\tif (\n\t\t\t\t\ttypeof memory.user_prompt_import_key === \"string\" &&\n\t\t\t\t\tmemory.user_prompt_import_key.trim()\n\t\t\t\t) {\n\t\t\t\t\tlinkedPromptId =\n\t\t\t\t\t\tpromptImportKeyMapping.get(memory.user_prompt_import_key.trim()) ??\n\t\t\t\t\t\tfindImportedId(db, \"user_prompts\", memory.user_prompt_import_key.trim());\n\t\t\t\t} else if (typeof memory.user_prompt_id === \"number\") {\n\t\t\t\t\tlinkedPromptId = promptMapping.get(memory.user_prompt_id) ?? null;\n\t\t\t\t}\n\n\t\t\t\tconst baseMetadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_memory_id: memory.id ?? null,\n\t\t\t\t\toriginal_created_at: memory.created_at ?? null,\n\t\t\t\t\timport_metadata: memory.metadata_json ?? null,\n\t\t\t\t\timport_key: memoryImportKey,\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\ttypeof memory.user_prompt_import_key === \"string\" &&\n\t\t\t\t\tmemory.user_prompt_import_key.trim()\n\t\t\t\t) {\n\t\t\t\t\tbaseMetadata.user_prompt_import_key = memory.user_prompt_import_key.trim();\n\t\t\t\t}\n\t\t\t\tconst metadata =\n\t\t\t\t\tmemory.kind === \"session_summary\"\n\t\t\t\t\t\t? mergeSummaryMetadata(baseMetadata, memory.metadata_json ?? null)\n\t\t\t\t\t\t: baseMetadata;\n\n\t\t\t\tinsertMemory(d, {\n\t\t\t\t\t...memory,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tuser_prompt_id: linkedPromptId,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: memoryImportKey,\n\t\t\t\t});\n\t\t\t\timportedMemories += 1;\n\t\t\t}\n\n\t\t\tfor (const summary of summariesData) {\n\t\t\t\tconst oldSessionId = Number(summary.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(summary.project);\n\t\t\t\tconst summaryImportKey =\n\t\t\t\t\ttypeof summary.import_key === \"string\" && summary.import_key.trim()\n\t\t\t\t\t\t? summary.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"summary\", summary.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof summary.created_at === \"string\" ? summary.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tif (findImportedId(db, \"session_summaries\", summaryImportKey) != null) continue;\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_summary_id: summary.id ?? null,\n\t\t\t\t\toriginal_created_at: summary.created_at ?? null,\n\t\t\t\t\timport_metadata: summary.metadata_json ?? null,\n\t\t\t\t\timport_key: summaryImportKey,\n\t\t\t\t};\n\t\t\t\tinsertSummary(d, {\n\t\t\t\t\t...summary,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: summaryImportKey,\n\t\t\t\t});\n\t\t\t\timportedSummaries += 1;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsessions: importedSessions,\n\t\t\t\tuser_prompts: importedPrompts,\n\t\t\t\tmemory_items: importedMemories,\n\t\t\t\tsession_summaries: importedSummaries,\n\t\t\t\tdryRun: false,\n\t\t\t};\n\t\t})();\n\t} finally {\n\t\tdb.close();\n\t}\n}\n","/**\n * Filter builder for memory_items queries.\n *\n * Mirrors Python's _extend_memory_filter_clauses in codemem/store/search.py.\n * Builds WHERE clause fragments and parameter arrays from a MemoryFilters object.\n */\n\nimport { projectClause } from \"./project.js\";\nimport type { MemoryFilters } from \"./types.js\";\n\nexport interface OwnershipFilterContext {\n\tactorId: string;\n\tdeviceId: string;\n}\n\nexport interface FilterResult {\n\tclauses: string[];\n\tparams: unknown[];\n\tjoinSessions: boolean;\n}\n\n/**\n * Normalize a filter value that may be a single string or an array of strings\n * into a clean, non-empty array of trimmed strings.\n */\nexport function normalizeFilterStrings(value: string | string[] | undefined | null): string[] {\n\tif (value == null) return [];\n\tconst items = Array.isArray(value) ? value : [value];\n\tconst seen = new Set<string>();\n\tconst normalized: string[] = [];\n\tfor (const item of items) {\n\t\tconst candidate = String(item).trim();\n\t\tif (!candidate || seen.has(candidate)) continue;\n\t\tseen.add(candidate);\n\t\tnormalized.push(candidate);\n\t}\n\treturn normalized;\n}\n\nexport function normalizeWorkspaceKinds(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter(\n\t\t\t(raw, idx, arr) => (raw === \"personal\" || raw === \"shared\") && arr.indexOf(raw) === idx,\n\t\t);\n}\n\nexport function normalizeVisibilityValues(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter((raw, idx, arr) => (raw === \"private\" || raw === \"shared\") && arr.indexOf(raw) === idx);\n}\n\nexport function normalizeTrustStates(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter(\n\t\t\t(raw, idx, arr) =>\n\t\t\t\t(raw === \"trusted\" || raw === \"legacy_unknown\" || raw === \"unreviewed\") &&\n\t\t\t\tarr.indexOf(raw) === idx,\n\t\t);\n}\n\n/**\n * Add IN / NOT IN clauses for a multi-value filter column.\n * Mirrors Python's _add_multi_value_filter.\n */\nfunction addMultiValueFilter(\n\tclauses: string[],\n\tparams: unknown[],\n\tcolumn: string,\n\tincludeValues: string[],\n\texcludeValues: string[],\n): void {\n\tif (includeValues.length > 0) {\n\t\tconst placeholders = includeValues.map(() => \"?\").join(\", \");\n\t\tclauses.push(`${column} IN (${placeholders})`);\n\t\tparams.push(...includeValues);\n\t}\n\tif (excludeValues.length > 0) {\n\t\tconst placeholders = excludeValues.map(() => \"?\").join(\", \");\n\t\tclauses.push(`(${column} IS NULL OR ${column} NOT IN (${placeholders}))`);\n\t\tparams.push(...excludeValues);\n\t}\n}\n\n/**\n * Build WHERE clause fragments from a MemoryFilters object.\n *\n * Returns clauses, params, and whether the sessions table must be joined.\n * The caller is responsible for prepending `memory_items.active = 1` or\n * similar base conditions — this function only appends filter-specific clauses.\n */\nexport function buildFilterClauses(filters: MemoryFilters | undefined | null): FilterResult {\n\treturn buildFilterClausesWithContext(filters);\n}\n\nexport function buildFilterClausesWithContext(\n\tfilters: MemoryFilters | undefined | null,\n\townership?: OwnershipFilterContext,\n): FilterResult {\n\tconst result: FilterResult = { clauses: [], params: [], joinSessions: false };\n\tif (!filters) return result;\n\n\tconst { clauses, params } = result;\n\n\t// Single kind filter\n\tif (filters.kind) {\n\t\tclauses.push(\"memory_items.kind = ?\");\n\t\tparams.push(filters.kind);\n\t}\n\tif (filters.session_id) {\n\t\tclauses.push(\"memory_items.session_id = ?\");\n\t\tparams.push(filters.session_id);\n\t}\n\tif (filters.since) {\n\t\tclauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(filters.since);\n\t}\n\n\tconst ownershipScope = String(filters.ownership_scope ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (ownership && (ownershipScope === \"mine\" || ownershipScope === \"theirs\")) {\n\t\tconst ownedClause =\n\t\t\t\"(COALESCE(memory_items.actor_id, '') = ? OR COALESCE(memory_items.origin_device_id, '') = ?)\";\n\t\tif (ownershipScope === \"mine\") {\n\t\t\tclauses.push(ownedClause);\n\t\t} else {\n\t\t\tclauses.push(\n\t\t\t\t\"(COALESCE(memory_items.actor_id, '') != ? AND COALESCE(memory_items.origin_device_id, '') != ?)\",\n\t\t\t);\n\t\t}\n\t\tparams.push(ownership.actorId, ownership.deviceId);\n\t}\n\n\t// Project scoping — requires sessions JOIN\n\tif (filters.project) {\n\t\tconst { clause, params: projectParams } = projectClause(filters.project);\n\t\tif (clause) {\n\t\t\tclauses.push(clause);\n\t\t\tparams.push(...projectParams);\n\t\t\tresult.joinSessions = true;\n\t\t}\n\t}\n\n\t// Visibility\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.visibility\",\n\t\tnormalizeVisibilityValues(filters.include_visibility ?? filters.visibility),\n\t\tnormalizeVisibilityValues(filters.exclude_visibility),\n\t);\n\n\t// Workspace IDs\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.workspace_id\",\n\t\tnormalizeFilterStrings(filters.include_workspace_ids),\n\t\tnormalizeFilterStrings(filters.exclude_workspace_ids),\n\t);\n\n\t// Workspace kinds\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.workspace_kind\",\n\t\tnormalizeWorkspaceKinds(filters.include_workspace_kinds),\n\t\tnormalizeWorkspaceKinds(filters.exclude_workspace_kinds),\n\t);\n\n\t// Actor IDs\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.actor_id\",\n\t\tnormalizeFilterStrings(filters.include_actor_ids),\n\t\tnormalizeFilterStrings(filters.exclude_actor_ids),\n\t);\n\n\t// Trust states\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.trust_state\",\n\t\tnormalizeTrustStates(filters.include_trust_states),\n\t\tnormalizeTrustStates(filters.exclude_trust_states),\n\t);\n\n\treturn result;\n}\n","/**\n * Text sanitization for the ingest pipeline.\n *\n * Ports codemem/ingest_sanitize.py — strips <private> blocks and redacts\n * sensitive field names from payloads before they reach the observer LLM.\n */\n\n// ---------------------------------------------------------------------------\n// Private content stripping\n// ---------------------------------------------------------------------------\n\nconst PRIVATE_BLOCK_RE = /<private>.*?<\\/private>/gis;\nconst PRIVATE_OPEN_RE = /<private>/i;\nconst PRIVATE_CLOSE_RE = /<\\/private>/gi;\n\n/**\n * Remove `<private>…</private>` blocks from text.\n *\n * Handles matched pairs, orphaned opening tags (truncates at the tag),\n * and stray closing tags (removed).\n */\nexport function stripPrivate(text: string): string {\n\tif (!text) return \"\";\n\t// Remove matched pairs\n\tlet redacted = text.replace(PRIVATE_BLOCK_RE, \"\");\n\t// Orphaned opening tag — truncate everything after it\n\tconst openMatch = PRIVATE_OPEN_RE.exec(redacted);\n\tif (openMatch) {\n\t\tredacted = redacted.slice(0, openMatch.index);\n\t}\n\t// Stray closing tags\n\tredacted = redacted.replace(PRIVATE_CLOSE_RE, \"\");\n\treturn redacted;\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field detection\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_FIELD_RE =\n\t/(?:^|_|-)(?:token|secret|password|passwd|api[_-]?key|authorization|private[_-]?key|cookie)(?:$|_|-)/i;\n\nconst REDACTED_VALUE = \"[REDACTED]\";\n\nexport function isSensitiveFieldName(fieldName: string): boolean {\n\tconst normalized = fieldName.trim().toLowerCase();\n\tif (!normalized) return false;\n\treturn SENSITIVE_FIELD_RE.test(normalized);\n}\n\n// ---------------------------------------------------------------------------\n// Deep sanitization\n// ---------------------------------------------------------------------------\n\nexport function stripPrivateObj(value: unknown): unknown {\n\tif (typeof value === \"string\") return stripPrivate(value);\n\tif (Array.isArray(value)) return value.map(stripPrivateObj);\n\tif (value != null && typeof value === \"object\") {\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n\t\t\tif (isSensitiveFieldName(key)) {\n\t\t\t\tresult[key] = REDACTED_VALUE;\n\t\t\t} else {\n\t\t\t\tresult[key] = stripPrivateObj(item);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\n// ---------------------------------------------------------------------------\n// Payload sanitization (truncation + private stripping)\n// ---------------------------------------------------------------------------\n\nfunction truncateText(text: string, maxChars: number): string {\n\tif (maxChars <= 0) return \"\";\n\tif (text.length <= maxChars) return text;\n\treturn `${text.slice(0, maxChars)}... (truncated)`;\n}\n\nexport function sanitizePayload(value: unknown, maxChars: number): unknown {\n\tif (value == null) return null;\n\tif (typeof value === \"string\") {\n\t\treturn truncateText(stripPrivate(value), maxChars);\n\t}\n\tconst cleaned = stripPrivateObj(value);\n\ttry {\n\t\tconst serialized = JSON.stringify(cleaned);\n\t\tif (maxChars > 0 && serialized.length > maxChars) {\n\t\t\treturn truncateText(serialized, maxChars);\n\t\t}\n\t} catch {\n\t\tconst asStr = String(cleaned);\n\t\tif (maxChars > 0 && asStr.length > maxChars) {\n\t\t\treturn truncateText(asStr, maxChars);\n\t\t}\n\t}\n\treturn cleaned;\n}\n\n// ---------------------------------------------------------------------------\n// Low-signal output detection\n// ---------------------------------------------------------------------------\n\nconst LOW_SIGNAL_OUTPUTS = new Set([\n\t\"wrote file successfully.\",\n\t\"wrote file successfully\",\n\t\"file written successfully.\",\n\t\"read file successfully.\",\n\t\"read file successfully\",\n\t\"<file>\",\n\t\"<image>\",\n]);\n\nfunction isLowSignalOutput(output: string): boolean {\n\tif (!output) return true;\n\tconst lines = output\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.filter(Boolean);\n\tif (lines.length === 0) return true;\n\treturn lines.every((line) => LOW_SIGNAL_OUTPUTS.has(line.toLowerCase()));\n}\n\nexport function sanitizeToolOutput(_tool: string, output: unknown, maxChars: number): unknown {\n\tif (output == null) return null;\n\tconst sanitized = sanitizePayload(output, maxChars);\n\tconst text = String(sanitized ?? \"\");\n\tif (isLowSignalOutput(text)) return \"\";\n\treturn sanitized;\n}\n","/**\n * Event extraction and budgeting for the ingest pipeline.\n *\n * Ports codemem/ingest/events.py + codemem/ingest_tool_events.py —\n * converts raw plugin events into ToolEvent structs, filters low-signal\n * tools, deduplicates, and budgets the list to fit observer token limits.\n */\n\nimport { sanitizePayload, sanitizeToolOutput } from \"./ingest-sanitize.js\";\nimport type { ToolEvent } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Tools whose output is too noisy / low-signal to send to the observer.\n * Matches Python's LOW_SIGNAL_TOOLS set.\n */\nexport const LOW_SIGNAL_TOOLS = new Set([\n\t\"tui\",\n\t\"shell\",\n\t\"cmd\",\n\t\"task\",\n\t\"slashcommand\",\n\t\"skill\",\n\t\"todowrite\",\n\t\"askuserquestion\",\n]);\n\n// ---------------------------------------------------------------------------\n// Tool name helpers\n// ---------------------------------------------------------------------------\n\n/** Return true for codemem's own MCP tools (avoids feedback loops). */\nexport function isInternalMemoryTool(tool: string): boolean {\n\treturn tool.startsWith(\"codemem_memory_\");\n}\n\n/** Extract a clean, lowercase tool name from a raw event. */\nexport function normalizeToolName(event: Record<string, unknown>): string {\n\tlet tool = String(event.tool ?? event.type ?? \"tool\").toLowerCase();\n\tif (tool.includes(\".\")) tool = tool.split(\".\").pop() ?? tool;\n\tif (tool.includes(\":\")) tool = tool.split(\":\").pop() ?? tool;\n\treturn tool;\n}\n\n// ---------------------------------------------------------------------------\n// Output compaction\n// ---------------------------------------------------------------------------\n\nfunction compactReadOutput(text: string, maxLines = 80, maxChars = 2000): string {\n\tif (!text) return \"\";\n\tconst allLines = text.split(\"\\n\");\n\tlet lines = allLines;\n\tif (lines.length > maxLines) {\n\t\tlines = [...lines.slice(0, maxLines), `... (+${allLines.length - maxLines} more lines)`];\n\t}\n\tlet compacted = lines.join(\"\\n\");\n\tif (maxChars > 0 && compacted.length > maxChars) {\n\t\tcompacted = `${compacted.slice(0, maxChars)}\\n... (truncated)`;\n\t}\n\treturn compacted;\n}\n\nfunction compactListOutput(text: string): string {\n\treturn compactReadOutput(text, 120, 2400);\n}\n\n// ---------------------------------------------------------------------------\n// Event → ToolEvent conversion\n// ---------------------------------------------------------------------------\n\n/** Convert a single raw event to a ToolEvent, or null if it should be skipped. */\nexport function eventToToolEvent(\n\tevent: Record<string, unknown>,\n\tmaxChars: number,\n\tlowSignalTools: Set<string> = LOW_SIGNAL_TOOLS,\n): ToolEvent | null {\n\tif (event.type !== \"tool.execute.after\") return null;\n\n\tconst tool = normalizeToolName(event);\n\tif (isInternalMemoryTool(tool)) return null;\n\tif (lowSignalTools.has(tool)) return null;\n\n\tconst rawArgs = event.args;\n\tconst args =\n\t\trawArgs != null && typeof rawArgs === \"object\" && !Array.isArray(rawArgs)\n\t\t\t? (rawArgs as Record<string, unknown>)\n\t\t\t: {};\n\n\tlet result = sanitizeToolOutput(tool, event.result, maxChars);\n\tif (tool === \"read\" && typeof result === \"string\") result = compactReadOutput(result);\n\tif (tool === \"bash\" && typeof result === \"string\") result = compactReadOutput(result);\n\tif ((tool === \"glob\" || tool === \"grep\") && typeof result === \"string\") {\n\t\tresult = compactListOutput(result);\n\t}\n\n\tconst error = sanitizePayload(event.error, maxChars);\n\n\treturn {\n\t\ttoolName: tool,\n\t\ttoolInput: sanitizePayload(args, maxChars),\n\t\ttoolOutput: result,\n\t\ttoolError: error,\n\t\ttimestamp: typeof event.timestamp === \"string\" ? event.timestamp : null,\n\t\tcwd:\n\t\t\t(typeof event.cwd === \"string\" ? event.cwd : null) ??\n\t\t\t(typeof args.cwd === \"string\" ? args.cwd : null),\n\t};\n}\n\n/** Filter and convert all events to ToolEvents. */\nexport function extractToolEvents(\n\tevents: Record<string, unknown>[],\n\tmaxChars: number,\n): ToolEvent[] {\n\tconst result: ToolEvent[] = [];\n\tfor (const event of events) {\n\t\tconst te = eventToToolEvent(event, maxChars);\n\t\tif (te) result.push(te);\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter event extraction (schema v1.0)\n// ---------------------------------------------------------------------------\n\n/** Extract validated _adapter event, or null if not a valid v1.0 adapter. */\nexport function extractAdapterEvent(\n\tevent: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst adapter = event._adapter;\n\tif (adapter == null || typeof adapter !== \"object\" || Array.isArray(adapter)) return null;\n\tconst a = adapter as Record<string, unknown>;\n\n\tif (a.schema_version !== \"1.0\") return null;\n\tif (typeof a.source !== \"string\" || !a.source.trim()) return null;\n\tif (typeof a.session_id !== \"string\" || !a.session_id.trim()) return null;\n\tif (typeof a.event_id !== \"string\" || !a.event_id.trim()) return null;\n\n\tconst eventType = a.event_type;\n\tif (typeof eventType !== \"string\") return null;\n\tconst validTypes = new Set([\n\t\t\"prompt\",\n\t\t\"assistant\",\n\t\t\"tool_call\",\n\t\t\"tool_result\",\n\t\t\"session_start\",\n\t\t\"session_end\",\n\t\t\"error\",\n\t]);\n\tif (!validTypes.has(eventType)) return null;\n\n\tif (a.payload == null || typeof a.payload !== \"object\") return null;\n\tif (typeof a.ts !== \"string\" || !a.ts.trim()) return null;\n\n\t// Validate timestamp parses\n\ttry {\n\t\tconst d = new Date(a.ts as string);\n\t\tif (Number.isNaN(d.getTime())) return null;\n\t} catch {\n\t\treturn null;\n\t}\n\n\treturn a;\n}\n\n/** Project an adapter tool_result into the flat event format expected by eventToToolEvent. */\nexport function projectAdapterToolEvent(\n\tadapter: Record<string, unknown>,\n\tevent: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst eventType = String(adapter.event_type ?? \"\");\n\tconst payload = adapter.payload as Record<string, unknown> | undefined;\n\tif (!payload || typeof payload !== \"object\") return null;\n\tif (eventType !== \"tool_result\") return null;\n\n\tlet toolInput = payload.tool_input;\n\tif (toolInput == null || typeof toolInput !== \"object\") toolInput = {};\n\n\tlet toolError = payload.tool_error ?? null;\n\tif (toolError == null && payload.status === \"error\") toolError = payload.error ?? null;\n\n\tlet toolOutput = payload.tool_output ?? null;\n\tif (toolOutput == null && \"output\" in payload) toolOutput = payload.output ?? null;\n\n\treturn {\n\t\ttype: \"tool.execute.after\",\n\t\ttool: payload.tool_name,\n\t\targs: toolInput,\n\t\tresult: toolOutput,\n\t\terror: toolError,\n\t\ttimestamp: adapter.ts,\n\t\tcwd: event.cwd,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Tool event budgeting\n// ---------------------------------------------------------------------------\n\nfunction toolEventSignature(event: ToolEvent): string {\n\tif (event.toolName === \"bash\" && event.toolInput != null && typeof event.toolInput === \"object\") {\n\t\tconst input = event.toolInput as Record<string, unknown>;\n\t\tconst cmd = String(input.command ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tif ((cmd === \"git status\" || cmd === \"git diff\") && !event.toolError) {\n\t\t\treturn `bash:${cmd}`;\n\t\t}\n\t}\n\tconst parts: string[] = [event.toolName];\n\ttry {\n\t\tparts.push(JSON.stringify(event.toolInput));\n\t} catch {\n\t\tparts.push(String(event.toolInput));\n\t}\n\tif (event.toolError) parts.push(String(event.toolError).slice(0, 200));\n\tif (typeof event.toolOutput === \"string\" && event.toolOutput) {\n\t\tparts.push(event.toolOutput.slice(0, 200));\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction toolEventImportance(event: ToolEvent): number {\n\tlet score = 0;\n\tif (event.toolError) score += 100;\n\tconst tool = (event.toolName || \"\").toLowerCase();\n\tif (tool === \"edit\" || tool === \"write\") score += 50;\n\telse if (tool === \"bash\") score += 30;\n\telse if (tool === \"read\") score += 20;\n\telse score += 10;\n\treturn score;\n}\n\nfunction estimateEventSize(event: ToolEvent): number {\n\ttry {\n\t\treturn JSON.stringify(event).length;\n\t} catch {\n\t\treturn String(event).length;\n\t}\n}\n\n/**\n * Deduplicate, rank, and trim tool events to fit within budget.\n *\n * 1. Deduplicates (keeping last occurrence).\n * 2. If over maxEvents, keeps the most important ones.\n * 3. If over maxTotalChars, keeps the most important that fit.\n */\nexport function budgetToolEvents(\n\ttoolEvents: ToolEvent[],\n\tmaxTotalChars: number,\n\tmaxEvents: number,\n): ToolEvent[] {\n\tif (!toolEvents.length || maxTotalChars <= 0 || maxEvents <= 0) return [];\n\n\t// Deduplicate, preferring last occurrence\n\tconst seen = new Set<string>();\n\tconst deduped: ToolEvent[] = [];\n\tfor (let i = toolEvents.length - 1; i >= 0; i--) {\n\t\tconst evt = toolEvents[i];\n\t\tif (!evt) continue;\n\t\tconst sig = toolEventSignature(evt);\n\t\tif (seen.has(sig)) continue;\n\t\tseen.add(sig);\n\t\tdeduped.push(evt);\n\t}\n\tdeduped.reverse();\n\n\t// Trim to maxEvents by importance\n\tlet result = deduped;\n\tif (result.length > maxEvents) {\n\t\tconst indexed = result.map((e, i) => ({ event: e, idx: i }));\n\t\tindexed.sort((a, b) => {\n\t\t\tconst impDiff = toolEventImportance(b.event) - toolEventImportance(a.event);\n\t\t\tif (impDiff !== 0) return impDiff;\n\t\t\treturn a.idx - b.idx; // prefer earlier for same importance\n\t\t});\n\t\tconst keepIdxs = new Set(indexed.slice(0, maxEvents).map((x) => x.idx));\n\t\tresult = result.filter((_, i) => keepIdxs.has(i));\n\t}\n\n\t// Check total size\n\tconst totalSize = result.reduce((sum, e) => sum + estimateEventSize(e), 0);\n\tif (totalSize <= maxTotalChars) return result;\n\n\t// Budget by size — pick most important that fit\n\tconst indexed = result.map((e, i) => ({ event: e, idx: i }));\n\tindexed.sort((a, b) => {\n\t\tconst impDiff = toolEventImportance(b.event) - toolEventImportance(a.event);\n\t\tif (impDiff !== 0) return impDiff;\n\t\treturn a.idx - b.idx;\n\t});\n\n\tconst kept: { event: ToolEvent; idx: number }[] = [];\n\tlet runningTotal = 0;\n\tfor (const item of indexed) {\n\t\tconst size = estimateEventSize(item.event);\n\t\tif (runningTotal + size > maxTotalChars && kept.length > 0) continue;\n\t\tkept.push(item);\n\t\trunningTotal += size;\n\t\tif (runningTotal >= maxTotalChars) break;\n\t}\n\n\t// Restore original order\n\tkept.sort((a, b) => a.idx - b.idx);\n\treturn kept.map((x) => x.event);\n}\n","/**\n * Observer prompt construction for the ingest pipeline.\n *\n * Ports codemem/observer_prompts.py — builds the system + user prompt\n * sent to the observer LLM that extracts memories from session transcripts.\n */\n\nimport type { ObserverContext, ToolEvent } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants — prompt fragments matching Python's observer_prompts.py\n// ---------------------------------------------------------------------------\n\nconst OBSERVATION_TYPES = \"bugfix, feature, refactor, change, discovery, decision, exploration\";\n\nconst SYSTEM_IDENTITY =\n\t\"You are a memory observer creating searchable records of development work \" +\n\t\"FOR FUTURE SESSIONS. Record what was BUILT/FIXED/DEPLOYED/CONFIGURED/LEARNED, \" +\n\t\"not what you (the observer) are doing. These memories help developers \" +\n\t\"recall past work, decisions, learnings, and investigations.\";\n\nconst RECORDING_FOCUS = `Focus on deliverables, capabilities, AND learnings:\n- What the system NOW DOES differently (new capabilities)\n- What shipped to users/production (features, fixes, configs, docs)\n- What was LEARNED through debugging, investigation, or testing\n- How systems work and why they behave the way they do\n- Changes in technical domains (auth, data, UI, infra, DevOps)\n\nUse outcome-focused verbs: implemented, fixed, deployed, configured, migrated, optimized, added, refactored, discovered, learned, debugged.\nOnly describe actions that are clearly supported by the observed context.\nNever invent file changes, API behavior, or code edits that are not explicitly present in the session evidence.\n\nGOOD examples (describes what was built or learned):\n- \"Authentication now supports OAuth2 with PKCE flow\"\n- \"Deployment pipeline runs canary releases with auto-rollback\"\n- \"Fixed race condition in session handler causing duplicate events\"\n- \"Discovered flush timing strategy needed adaptation for multi-session environment\"\n- \"Learned transcript building was broken - empty strings passed instead of conversation\"\n\nBAD examples (describes observation process - DO NOT DO THIS):\n- \"Analyzed authentication implementation and stored findings in database\"\n- \"Tracked deployment steps and logged outcomes to memory system\"\n- \"Recorded investigation results for later reference\"`;\n\nconst SKIP_GUIDANCE = `Skip routine operations WITHOUT learnings:\n- Empty status checks or listings (unless revealing important state)\n- Package installations with no errors or insights\n- Simple file reads with no discoveries\n- Repetitive operations already documented with no new findings\nIf nothing meaningful happened AND nothing was learned:\n- Output no <observation> blocks\n- Output <skip_summary reason=\"low-signal\"/> instead of a <summary> block.`;\n\nconst NARRATIVE_GUIDANCE = `Create narratives that tell the complete story:\n- Context: What was the problem or goal? What prompted this work?\n- Investigation: What was examined? What was discovered?\n- Learning: How does it work? Why does it exist? Any gotchas?\n- Implementation: What was changed? What does the code do now?\n- Impact: What's better? What does the system do differently?\n- Next steps: What remains? What should future sessions know?\n\nAim for ~120-400 words per significant work item.\nPrefer fewer, higher-signal observations over many small ones.\nInclude specific details when present: file paths, function names, configuration values.`;\n\nconst OUTPUT_GUIDANCE =\n\t\"Output only XML. Do not include commentary outside XML.\\n\\n\" +\n\t\"ALWAYS emit at least one <observation> block for any meaningful work. \" +\n\t\"Observations are the PRIMARY output - they capture what was built, fixed, learned, or decided. \" +\n\t\"Also emit a <summary> block to track session progress.\\n\\n\" +\n\t\"Prefer fewer, more comprehensive observations over many small ones.\";\n\nconst OBSERVATION_SCHEMA = `<observation>\n <type>[ ${OBSERVATION_TYPES} ]</type>\n <!--\n type MUST be EXACTLY one of these 7 options:\n - bugfix: something was broken, now fixed\n - feature: new capability or functionality added\n - refactor: code restructured, behavior unchanged\n - change: generic modification (docs, config, misc)\n - discovery: learning about existing system, debugging insights\n - decision: architectural/design choice with rationale\n - exploration: attempted approach that was tried but NOT shipped\n\n IMPORTANT: Use 'exploration' when:\n - Multiple approaches were tried for the same problem\n - An implementation was attempted but then replaced or reverted\n - Something was tested/experimented with but not kept\n - The attempt provides useful \"why we didn't do X\" context\n\n Exploration memories are valuable. They prevent repeating failed approaches.\n Include what was tried AND why it didn't work out.\n -->\n <title>[Short outcome-focused title - what was achieved or learned]</title>\n <!-- GOOD: \"OAuth2 PKCE flow added to authentication\" -->\n <!-- GOOD: \"Discovered flush strategy fails in multi-session environments\" -->\n <!-- GOOD (exploration): \"Tried emoji theme toggle - poor contrast on light backgrounds\" -->\n <!-- BAD: \"Analyzed authentication code\" (too vague, no outcome) -->\n <subtitle>[One sentence explanation of the outcome (max 24 words)]</subtitle>\n <facts>\n <fact>[Specific, self-contained statement with concrete details]</fact>\n <fact>[Include: file paths, function names, config values, error messages]</fact>\n <fact>[Each fact must stand alone - no pronouns like \"it\" or \"this\"]</fact>\n </facts>\n <narrative>[\n Full context: What was done, how it works, why it matters.\n For discoveries/debugging: what was investigated, what was found, what it means.\n For explorations: what was tried, why it didn't work, what was done instead.\n Include specific details: file paths, function names, configuration values.\n Aim for 100-500 words - enough to be useful, not overwhelming.\n ]</narrative>\n <concepts>\n <concept>[how-it-works, why-it-exists, what-changed, problem-solution, gotcha, pattern, trade-off]</concept>\n </concepts>\n <!-- concepts: 2-5 knowledge categories from the list above -->\n <files_read>\n <file>[full path from project root]</file>\n </files_read>\n <files_modified>\n <file>[full path from project root]</file>\n </files_modified>\n</observation>`;\n\nconst SUMMARY_SCHEMA = `<summary>\n <request>[What did the user request? What was the goal of this work session?]</request>\n\n <investigated>[What was explored or examined? What files, systems, logs were reviewed?\n What questions were asked? What did you try to understand?]</investigated>\n\n <learned>[What was learned about how things work? Any discoveries about the codebase,\n architecture, or domain? Gotchas or surprises? Understanding gained?]</learned>\n\n <completed>[What work was done? What shipped? What does the system do now that it\n didn't before? Be specific: files changed, features added, bugs fixed.]</completed>\n\n <next_steps>[What are the logical next steps? What remains to be done? What should\n the next session pick up? Any blockers or dependencies?]</next_steps>\n\n <notes>[Additional context, insights, or warnings. Anything future sessions should\n know that doesn't fit above. Design decisions, trade-offs, alternatives considered.]</notes>\n\n <files_read>\n <file>[path]</file>\n </files_read>\n <files_modified>\n <file>[path]</file>\n </files_modified>\n</summary>\n\nIf nothing meaningful happened, emit <skip_summary reason=\"low-signal\"/> and do not emit <summary>.\nOtherwise, write a summary that explains the current state of the PRIMARY work (not your observation process).\n\nIf the user prompt is a short approval or acknowledgement (\"yes\", \"ok\", \"approved\"),\ninfer the request from the observed work and the completed/learned sections instead.\n\nOnly summarize what is evidenced in the session context. Do not infer or fabricate\nfile edits, behaviors, or outcomes that are not explicitly observed.\n\nKeep summaries concise (aim for ~150-450 words total across all fields).\n\nThis summary helps future sessions understand where this work left off.`;\n\n// ---------------------------------------------------------------------------\n// XML escaping\n// ---------------------------------------------------------------------------\n\nfunction escapeXml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\nfunction formatJson(value: unknown): string {\n\tif (value == null) return \"\";\n\tif (typeof value === \"string\") return value;\n\ttry {\n\t\treturn JSON.stringify(value, null, 2);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction formatToolEvent(event: ToolEvent): string {\n\tconst parts = [\"<observed_from_primary_session>\"];\n\tparts.push(` <what_happened>${escapeXml(event.toolName)}</what_happened>`);\n\tif (event.timestamp) {\n\t\tparts.push(` <occurred_at>${escapeXml(event.timestamp)}</occurred_at>`);\n\t}\n\tif (event.cwd) {\n\t\tparts.push(` <working_directory>${escapeXml(event.cwd)}</working_directory>`);\n\t}\n\tconst params = escapeXml(formatJson(event.toolInput));\n\tconst outcome = escapeXml(formatJson(event.toolOutput));\n\tconst error = escapeXml(formatJson(event.toolError));\n\tif (params) parts.push(` <parameters>${params}</parameters>`);\n\tif (outcome) parts.push(` <outcome>${outcome}</outcome>`);\n\tif (error) parts.push(` <error>${error}</error>`);\n\tparts.push(\"</observed_from_primary_session>\");\n\treturn parts.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build the observer prompt from session context.\n *\n * Returns `{ system, user }` — the system prompt contains identity + schema,\n * the user prompt contains the observed session context.\n *\n * The Python version concatenates everything into one prompt string passed as\n * the user message. We split system/user for cleaner API mapping, but the\n * content is equivalent.\n */\nexport function buildObserverPrompt(context: ObserverContext): {\n\tsystem: string;\n\tuser: string;\n} {\n\t// System prompt: identity + guidance + schemas\n\tconst systemBlocks: string[] = [\n\t\tSYSTEM_IDENTITY,\n\t\t\"\",\n\t\tRECORDING_FOCUS,\n\t\t\"\",\n\t\tSKIP_GUIDANCE,\n\t\t\"\",\n\t\tNARRATIVE_GUIDANCE,\n\t\t\"\",\n\t\tOUTPUT_GUIDANCE,\n\t\t\"\",\n\t\t\"Observation XML schema:\",\n\t\tOBSERVATION_SCHEMA,\n\t];\n\tif (context.includeSummary) {\n\t\tsystemBlocks.push(\"\", \"Summary XML schema:\", SUMMARY_SCHEMA);\n\t}\n\tconst system = systemBlocks.join(\"\\n\\n\").trim();\n\n\t// User prompt: observed session context\n\tconst userBlocks: string[] = [\"Observed session context:\"];\n\n\tif (context.userPrompt) {\n\t\tconst promptBlock = [\"<observed_from_primary_session>\"];\n\t\tpromptBlock.push(` <user_request>${escapeXml(context.userPrompt)}</user_request>`);\n\t\tif (context.promptNumber != null) {\n\t\t\tpromptBlock.push(` <prompt_number>${context.promptNumber}</prompt_number>`);\n\t\t}\n\t\tif (context.project) {\n\t\t\tpromptBlock.push(` <project>${escapeXml(context.project)}</project>`);\n\t\t}\n\t\tpromptBlock.push(\"</observed_from_primary_session>\");\n\t\tuserBlocks.push(promptBlock.join(\"\\n\"));\n\t}\n\n\tif (context.diffSummary) {\n\t\tuserBlocks.push(\n\t\t\t`<observed_from_primary_session>\\n <diff_summary>${escapeXml(context.diffSummary)}</diff_summary>\\n</observed_from_primary_session>`,\n\t\t);\n\t}\n\n\tif (context.recentFiles) {\n\t\tuserBlocks.push(\n\t\t\t`<observed_from_primary_session>\\n <recent_files>${escapeXml(context.recentFiles)}</recent_files>\\n</observed_from_primary_session>`,\n\t\t);\n\t}\n\n\tfor (const event of context.toolEvents) {\n\t\tuserBlocks.push(formatToolEvent(event));\n\t}\n\n\tif (context.includeSummary && context.lastAssistantMessage) {\n\t\tuserBlocks.push(\"Summary context:\");\n\t\tuserBlocks.push(\n\t\t\t`<summary_context>\\n <assistant_response>${escapeXml(context.lastAssistantMessage)}</assistant_response>\\n</summary_context>`,\n\t\t);\n\t}\n\n\tconst user = userBlocks.join(\"\\n\\n\").trim();\n\n\treturn { system, user };\n}\n","/**\n * Transcript building and request analysis for the ingest pipeline.\n *\n * Ports codemem/ingest/transcript.py + relevant extraction functions\n * from codemem/plugin_ingest.py.\n */\n\nimport { stripPrivate } from \"./ingest-sanitize.js\";\nimport type { ParsedSummary } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Trivial request detection\n// ---------------------------------------------------------------------------\n\nexport const TRIVIAL_REQUESTS = new Set([\n\t\"yes\",\n\t\"y\",\n\t\"ok\",\n\t\"okay\",\n\t\"approved\",\n\t\"approve\",\n\t\"looks good\",\n\t\"lgtm\",\n\t\"ship it\",\n\t\"sounds good\",\n\t\"sure\",\n\t\"go ahead\",\n\t\"proceed\",\n]);\n\n/** Normalize request text for comparison: trim, strip quotes, collapse whitespace, lowercase. */\nexport function normalizeRequestText(text: string | null): string {\n\tif (!text) return \"\";\n\tlet cleaned = text\n\t\t.trim()\n\t\t.replace(/^[\"']+|[\"']+$/g, \"\")\n\t\t.trim();\n\tcleaned = cleaned.replace(/\\s+/g, \" \");\n\treturn cleaned.toLowerCase();\n}\n\n/** Check whether the text is a trivial approval/acknowledgement. */\nexport function isTrivialRequest(text: string | null): boolean {\n\tconst normalized = normalizeRequestText(text);\n\tif (!normalized) return true;\n\treturn TRIVIAL_REQUESTS.has(normalized);\n}\n\n// ---------------------------------------------------------------------------\n// Sentence extraction\n// ---------------------------------------------------------------------------\n\n/** Extract the first sentence from text (strip markdown prefixes). */\nexport function firstSentence(text: string): string {\n\tlet cleaned = text\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tcleaned = cleaned.replace(/^[#*\\-\\d.\\s]+/, \"\");\n\tconst parts = cleaned.split(/(?<=[.!?])\\s+/);\n\treturn (parts[0] ?? cleaned).trim();\n}\n\n/** Derive a meaningful request from summary fields when the original is trivial. */\nexport function deriveRequest(summary: ParsedSummary): string {\n\tconst candidates = [\n\t\tsummary.completed,\n\t\tsummary.learned,\n\t\tsummary.investigated,\n\t\tsummary.nextSteps,\n\t\tsummary.notes,\n\t];\n\tfor (const candidate of candidates) {\n\t\tif (candidate) return firstSentence(candidate);\n\t}\n\treturn \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Transcript building\n// ---------------------------------------------------------------------------\n\n/**\n * Build a chronological transcript from user prompts and assistant messages.\n * Strips private content before including text.\n */\nexport function buildTranscript(events: Record<string, unknown>[]): string {\n\tconst parts: string[] = [];\n\tfor (const event of events) {\n\t\tconst eventType = event.type;\n\t\tif (eventType === \"user_prompt\") {\n\t\t\tconst promptText = stripPrivate(String(event.prompt_text ?? \"\")).trim();\n\t\t\tif (promptText) parts.push(`User: ${promptText}`);\n\t\t} else if (eventType === \"assistant_message\") {\n\t\t\tconst assistantText = stripPrivate(String(event.assistant_text ?? \"\")).trim();\n\t\t\tif (assistantText) parts.push(`Assistant: ${assistantText}`);\n\t\t}\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Extraction helpers\n// ---------------------------------------------------------------------------\n\n/** Extract assistant text messages from raw events. */\nexport function extractAssistantMessages(events: Record<string, unknown>[]): string[] {\n\tconst messages: string[] = [];\n\tfor (const event of events) {\n\t\tif (event.type !== \"assistant_message\") continue;\n\t\tconst text = String(event.assistant_text ?? \"\").trim();\n\t\tif (text) messages.push(text);\n\t}\n\treturn messages;\n}\n\n/** Extract token usage events from assistant_usage events. */\nexport function extractAssistantUsage(events: Record<string, unknown>[]): Record<string, number>[] {\n\tconst usageEvents: Record<string, number>[] = [];\n\tfor (const event of events) {\n\t\tif (event.type !== \"assistant_usage\") continue;\n\t\tconst usage = event.usage;\n\t\tif (usage == null || typeof usage !== \"object\") continue;\n\t\tconst u = usage as Record<string, unknown>;\n\t\tconst inputTokens = Number(u.input_tokens ?? 0) || 0;\n\t\tconst outputTokens = Number(u.output_tokens ?? 0) || 0;\n\t\tconst cacheCreation = Number(u.cache_creation_input_tokens ?? 0) || 0;\n\t\tconst cacheRead = Number(u.cache_read_input_tokens ?? 0) || 0;\n\t\tconst total = inputTokens + outputTokens + cacheCreation;\n\t\tif (total <= 0) continue;\n\t\tusageEvents.push({\n\t\t\tinput_tokens: inputTokens,\n\t\t\toutput_tokens: outputTokens,\n\t\t\tcache_creation_input_tokens: cacheCreation,\n\t\t\tcache_read_input_tokens: cacheRead,\n\t\t\ttotal_tokens: total,\n\t\t});\n\t}\n\treturn usageEvents;\n}\n\n/** Extract user prompts with prompt numbers from raw events. */\nexport function extractPrompts(\n\tevents: Record<string, unknown>[],\n): { promptText: string; promptNumber: number | null; timestamp: string | null }[] {\n\tconst prompts: { promptText: string; promptNumber: number | null; timestamp: string | null }[] =\n\t\t[];\n\tfor (const event of events) {\n\t\tif (event.type !== \"user_prompt\") continue;\n\t\tconst promptText = String(event.prompt_text ?? \"\").trim();\n\t\tif (!promptText) continue;\n\t\tprompts.push({\n\t\t\tpromptText,\n\t\t\tpromptNumber: typeof event.prompt_number === \"number\" ? event.prompt_number : null,\n\t\t\ttimestamp: typeof event.timestamp === \"string\" ? event.timestamp : null,\n\t\t});\n\t}\n\treturn prompts;\n}\n\n/**\n * Project adapter events into the flat event format used by transcript building.\n *\n * Adapter events use the `_adapter` envelope (schema v1.0). This function\n * converts prompt and assistant adapter events into the standard\n * `user_prompt` / `assistant_message` shapes so buildTranscript can\n * process them uniformly.\n */\nexport function normalizeAdapterEvents(\n\tevents: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn events.map((event) => {\n\t\tconst adapter = event._adapter;\n\t\tif (adapter == null || typeof adapter !== \"object\" || Array.isArray(adapter)) return event;\n\t\tconst a = adapter as Record<string, unknown>;\n\t\tif (a.schema_version !== \"1.0\") return event;\n\n\t\tconst payload = a.payload;\n\t\tif (payload == null || typeof payload !== \"object\") return event;\n\t\tconst p = payload as Record<string, unknown>;\n\n\t\tconst eventType = String(a.event_type ?? \"\");\n\t\tif (eventType === \"prompt\") {\n\t\t\tconst text = String(p.text ?? \"\").trim();\n\t\t\tif (!text) return event;\n\t\t\treturn {\n\t\t\t\ttype: \"user_prompt\",\n\t\t\t\tprompt_text: text,\n\t\t\t\tprompt_number: p.prompt_number ?? null,\n\t\t\t\ttimestamp: a.ts ?? null,\n\t\t\t};\n\t\t}\n\t\tif (eventType === \"assistant\") {\n\t\t\tconst text = String(p.text ?? \"\").trim();\n\t\t\tif (!text) return event;\n\t\t\treturn {\n\t\t\t\ttype: \"assistant_message\",\n\t\t\t\tassistant_text: text,\n\t\t\t\ttimestamp: a.ts ?? null,\n\t\t\t};\n\t\t}\n\t\treturn event;\n\t});\n}\n","/**\n * XML response parser for the observer LLM output.\n *\n * Ports codemem/xml_parser.py — uses regex-based parsing to extract\n * observations and session summaries from the observer's XML response.\n *\n * The observer output is structured XML, not arbitrary HTML, so regex\n * is sufficient (no DOM parser needed).\n */\n\nimport type { ParsedObservation, ParsedOutput, ParsedSummary } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Regex patterns\n// ---------------------------------------------------------------------------\n\n// Match <observation> with optional attributes (LLMs sometimes add kind=\"...\")\nconst OBSERVATION_BLOCK_RE = /<observation[^>]*>.*?<\\/observation>/gs;\nconst SUMMARY_BLOCK_RE = /<summary[^>]*>.*?<\\/summary>/gs;\nconst SKIP_SUMMARY_RE = /<skip_summary(?:\\s+reason=\"(?<reason>[^\"]+)\")?\\s*\\/>/i;\nconst CODE_FENCE_RE = /```(?:xml)?/gi;\n\n// ---------------------------------------------------------------------------\n// Text extraction helpers\n// ---------------------------------------------------------------------------\n\n/** Remove code fences and trim whitespace. */\nfunction cleanXmlText(text: string): string {\n\treturn text.replace(CODE_FENCE_RE, \"\").trim();\n}\n\n/** Extract text content from within a single XML tag. Returns empty string if not found. */\nfunction extractTagText(xml: string, tag: string): string {\n\tconst re = new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\");\n\tconst match = re.exec(xml);\n\tif (!match?.[1]) return \"\";\n\treturn match[1].trim();\n}\n\n/** Extract text content of repeated child elements within a parent tag. */\nfunction extractChildTexts(xml: string, parentTag: string, childTag: string): string[] {\n\tconst parentRe = new RegExp(`<${parentTag}[^>]*>([\\\\s\\\\S]*?)</${parentTag}>`, \"i\");\n\tconst parentMatch = parentRe.exec(xml);\n\tif (!parentMatch?.[1]) return [];\n\n\tconst childRe = new RegExp(`<${childTag}[^>]*>([\\\\s\\\\S]*?)</${childTag}>`, \"gi\");\n\tconst items: string[] = [];\n\tfor (\n\t\tlet match = childRe.exec(parentMatch[1]);\n\t\tmatch !== null;\n\t\tmatch = childRe.exec(parentMatch[1])\n\t) {\n\t\tconst text = match[1]?.trim();\n\t\tif (text) items.push(text);\n\t}\n\treturn items;\n}\n\n// ---------------------------------------------------------------------------\n// Block parsers\n// ---------------------------------------------------------------------------\n\nfunction parseObservationBlock(block: string): ParsedObservation | null {\n\t// Minimal validation — must have at least a type or title\n\tconst kind = extractTagText(block, \"type\");\n\tconst title = extractTagText(block, \"title\");\n\tif (!kind && !title) return null;\n\n\treturn {\n\t\tkind,\n\t\ttitle,\n\t\tnarrative: extractTagText(block, \"narrative\"),\n\t\tsubtitle: extractTagText(block, \"subtitle\") || null,\n\t\tfacts: extractChildTexts(block, \"facts\", \"fact\"),\n\t\tconcepts: extractChildTexts(block, \"concepts\", \"concept\"),\n\t\tfilesRead: extractChildTexts(block, \"files_read\", \"file\"),\n\t\tfilesModified: extractChildTexts(block, \"files_modified\", \"file\"),\n\t};\n}\n\nfunction parseSummaryBlock(block: string): ParsedSummary | null {\n\tconst request = extractTagText(block, \"request\");\n\tconst investigated = extractTagText(block, \"investigated\");\n\tconst learned = extractTagText(block, \"learned\");\n\tconst completed = extractTagText(block, \"completed\");\n\tconst nextSteps = extractTagText(block, \"next_steps\");\n\tconst notes = extractTagText(block, \"notes\");\n\n\t// At least one field must be populated\n\tif (!request && !investigated && !learned && !completed && !nextSteps && !notes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trequest,\n\t\tinvestigated,\n\t\tlearned,\n\t\tcompleted,\n\t\tnextSteps,\n\t\tnotes,\n\t\tfilesRead: extractChildTexts(block, \"files_read\", \"file\"),\n\t\tfilesModified: extractChildTexts(block, \"files_modified\", \"file\"),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the observer LLM's XML response into structured data.\n *\n * Extracts all `<observation>` blocks and the last `<summary>` block.\n * Handles missing/empty tags gracefully.\n */\nexport function parseObserverResponse(raw: string): ParsedOutput {\n\tconst cleaned = cleanXmlText(raw);\n\n\t// Extract observations\n\tconst observations: ParsedObservation[] = [];\n\tconst obsBlocks = cleaned.match(OBSERVATION_BLOCK_RE) ?? [];\n\tfor (const block of obsBlocks) {\n\t\tconst parsed = parseObservationBlock(block);\n\t\tif (parsed) observations.push(parsed);\n\t}\n\n\t// Extract summary (use last block if multiple)\n\tlet summary: ParsedSummary | null = null;\n\tconst summaryBlocks = cleaned.match(SUMMARY_BLOCK_RE) ?? [];\n\tconst lastSummaryBlock = summaryBlocks.at(-1);\n\tif (lastSummaryBlock) {\n\t\tsummary = parseSummaryBlock(lastSummaryBlock);\n\t}\n\n\t// Check for skip_summary\n\tconst skipMatch = SKIP_SUMMARY_RE.exec(cleaned);\n\tconst skipReason = skipMatch?.groups?.reason ?? null;\n\n\treturn { observations, summary, skipSummaryReason: skipReason };\n}\n\n/** Return true if at least one observation has a title or narrative. */\nexport function hasMeaningfulObservation(observations: ParsedObservation[]): boolean {\n\treturn observations.some((obs) => obs.title || obs.narrative);\n}\n","/**\n * Tag derivation — port of Python's codemem/store/tags.py.\n *\n * Derives searchable tags from memory kind, title, concepts, and file paths.\n * Used at ingest time to populate memory_items.tags_text inline (matching\n * Python's store_observation → derive_tags flow).\n */\n\nconst STOPWORDS = new Set([\n\t\"the\",\n\t\"and\",\n\t\"for\",\n\t\"with\",\n\t\"this\",\n\t\"that\",\n\t\"from\",\n\t\"are\",\n\t\"was\",\n\t\"were\",\n\t\"has\",\n\t\"have\",\n\t\"had\",\n\t\"not\",\n\t\"but\",\n\t\"can\",\n\t\"will\",\n\t\"all\",\n\t\"been\",\n\t\"each\",\n\t\"which\",\n\t\"their\",\n\t\"said\",\n\t\"its\",\n\t\"into\",\n\t\"than\",\n\t\"other\",\n\t\"some\",\n\t\"could\",\n\t\"them\",\n\t\"about\",\n\t\"then\",\n\t\"made\",\n\t\"after\",\n\t\"many\",\n\t\"also\",\n\t\"did\",\n\t\"just\",\n\t\"should\",\n\t\"over\",\n\t\"such\",\n\t\"there\",\n\t\"would\",\n\t\"more\",\n\t\"now\",\n\t\"very\",\n\t\"when\",\n\t\"what\",\n\t\"your\",\n\t\"how\",\n\t\"out\",\n\t\"our\",\n\t\"his\",\n\t\"her\",\n\t\"she\",\n\t\"him\",\n\t\"most\",\n]);\n\n/**\n * Normalize a raw value into a tag-safe lowercase slug.\n * Returns empty string if the value is empty, a stopword, or too short.\n */\nexport function normalizeTag(value: string, stopwords?: Set<string>): string {\n\tlet lowered = (value || \"\").trim().toLowerCase();\n\tif (!lowered) return \"\";\n\tlowered = lowered\n\t\t.replace(/[^a-z0-9_]+/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\");\n\tif (!lowered) return \"\";\n\tconst words = stopwords ?? STOPWORDS;\n\tif (words.has(lowered)) return \"\";\n\tif (lowered.length > 40) lowered = lowered.slice(0, 40).replace(/-$/, \"\");\n\treturn lowered;\n}\n\n/**\n * Extract tags from a file path (basename, parent dir, top-level dir).\n */\nexport function fileTags(pathValue: string, stopwords?: Set<string>): string[] {\n\tconst raw = (pathValue || \"\").trim();\n\tif (!raw) return [];\n\tconst parts = raw.split(/[\\\\/]+/).filter((p): p is string => !!p && p !== \".\" && p !== \"..\");\n\tif (!parts.length) return [];\n\n\tconst tags: string[] = [];\n\tconst last = parts.at(-1);\n\tif (last) {\n\t\tconst basename = normalizeTag(last, stopwords);\n\t\tif (basename) tags.push(basename);\n\t}\n\tif (parts.length >= 2) {\n\t\tconst secondLast = parts.at(-2);\n\t\tif (secondLast) {\n\t\t\tconst parent = normalizeTag(secondLast, stopwords);\n\t\t\tif (parent) tags.push(parent);\n\t\t}\n\t}\n\tif (parts.length >= 3) {\n\t\tconst first = parts.at(0);\n\t\tif (first) {\n\t\t\tconst top = normalizeTag(first, stopwords);\n\t\t\tif (top) tags.push(top);\n\t\t}\n\t}\n\treturn tags;\n}\n\n/**\n * Derive tags from memory metadata, matching Python's derive_tags().\n *\n * Sources (in priority order):\n * 1. Memory kind\n * 2. Concepts (from observer XML output)\n * 3. File paths (read + modified)\n * 4. Title tokens (fallback when nothing else produced tags)\n */\nexport function deriveTags(opts: {\n\tkind: string;\n\ttitle?: string;\n\tconcepts?: string[];\n\tfilesRead?: string[];\n\tfilesModified?: string[];\n\tstopwords?: Set<string>;\n}): string[] {\n\tconst words = opts.stopwords ?? STOPWORDS;\n\tconst tags: string[] = [];\n\n\tconst kindTag = normalizeTag(opts.kind, words);\n\tif (kindTag) tags.push(kindTag);\n\n\tfor (const concept of opts.concepts ?? []) {\n\t\tconst normalized = normalizeTag(concept, words);\n\t\tif (normalized) tags.push(normalized);\n\t}\n\n\tfor (const pathValue of [...(opts.filesRead ?? []), ...(opts.filesModified ?? [])]) {\n\t\ttags.push(...fileTags(pathValue, words));\n\t}\n\n\t// Fallback: extract tokens from title if no other tags were found\n\tif (!tags.length && opts.title) {\n\t\tfor (const match of opts.title.toLowerCase().matchAll(/[a-z0-9_]+/g)) {\n\t\t\tconst normalized = normalizeTag(match[0], words);\n\t\t\tif (normalized) tags.push(normalized);\n\t\t}\n\t}\n\n\t// Dedupe while preserving order, cap at 20\n\tconst deduped: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const tag of tags) {\n\t\tif (seen.has(tag)) continue;\n\t\tseen.add(tag);\n\t\tdeduped.push(tag);\n\t\tif (deduped.length >= 20) break;\n\t}\n\treturn deduped;\n}\n","/**\n * Vector store operations for semantic search.\n *\n * Ports codemem/store/vectors.py — backfill and on-insert vector writes\n * against the sqlite-vec `memory_vectors` virtual table.\n *\n * All functions accept a raw better-sqlite3 Database so they work outside\n * the MemoryStore class. Embedding is async; callers await then write\n * synchronously (matches the runtime-topology decision: main thread owns DB).\n */\n\nimport type { Database } from \"./db.js\";\nimport {\n\tchunkText,\n\tembedTexts,\n\tgetEmbeddingClient,\n\thashText,\n\tserializeFloat32,\n} from \"./embeddings.js\";\nimport { projectClause } from \"./project.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface BackfillVectorsResult {\n\tchecked: number;\n\tembedded: number;\n\tinserted: number;\n\tskipped: number;\n}\n\nexport interface BackfillVectorsOptions {\n\tlimit?: number | null;\n\tsince?: string | null;\n\tproject?: string | null;\n\tactiveOnly?: boolean;\n\tdryRun?: boolean;\n\tmemoryIds?: number[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// storeVectors — called inline when a memory is created/remembered\n// ---------------------------------------------------------------------------\n\n/**\n * Embed and store vectors for a single memory item.\n * No-op when embeddings are disabled or the client is unavailable.\n */\nexport async function storeVectors(\n\tdb: Database,\n\tmemoryId: number,\n\ttitle: string,\n\tbodyText: string,\n): Promise<void> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return;\n\n\tconst text = `${title}\\n${bodyText}`.trim();\n\tconst chunks = chunkText(text);\n\tif (chunks.length === 0) return;\n\n\tconst embeddings = await embedTexts(chunks);\n\tif (embeddings.length === 0) return;\n\n\tconst model = client.model;\n\tconst stmt = db.prepare(\n\t\t\"INSERT INTO memory_vectors(embedding, memory_id, chunk_index, content_hash, model) VALUES (?, ?, ?, ?, ?)\",\n\t);\n\n\tfor (let i = 0; i < chunks.length && i < embeddings.length; i++) {\n\t\tconst vector = embeddings[i];\n\t\tif (!vector || vector.length === 0) continue;\n\t\tstmt.run(serializeFloat32(vector), memoryId, i, hashText(chunks[i]!), model);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// backfillVectors — CLI batch backfill\n// ---------------------------------------------------------------------------\n\n/**\n * Backfill vectors for memories that don't have them yet.\n * Matches Python's `backfill_vectors()` in store/vectors.py.\n */\nexport async function backfillVectors(\n\tdb: Database,\n\topts: BackfillVectorsOptions = {},\n): Promise<BackfillVectorsResult> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return { checked: 0, embedded: 0, inserted: 0, skipped: 0 };\n\n\tconst { limit, since, project, activeOnly = true, dryRun = false, memoryIds } = opts;\n\n\tconst params: unknown[] = [];\n\tconst whereClauses: string[] = [];\n\n\tif (activeOnly) whereClauses.push(\"memory_items.active = 1\");\n\tif (since) {\n\t\twhereClauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(since);\n\t}\n\tif (project) {\n\t\tconst pc = projectClause(project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t}\n\t}\n\tif (memoryIds && memoryIds.length > 0) {\n\t\tconst placeholders = memoryIds.map(() => \"?\").join(\",\");\n\t\twhereClauses.push(`memory_items.id IN (${placeholders})`);\n\t\tparams.push(...memoryIds);\n\t}\n\n\tconst where = whereClauses.length > 0 ? whereClauses.join(\" AND \") : \"1=1\";\n\tconst joinSessions = project != null;\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\tconst limitClause = limit != null && limit > 0 ? \"LIMIT ?\" : \"\";\n\tif (limit != null && limit > 0) params.push(limit);\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.id, memory_items.title, memory_items.body_text\n\t\t\t FROM memory_items ${joinClause}\n\t\t\t WHERE ${where}\n\t\t\t ORDER BY memory_items.created_at ASC ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{ id: number; title: string | null; body_text: string | null }>;\n\n\tconst model = client.model;\n\tlet checked = 0;\n\tlet embedded = 0;\n\tlet inserted = 0;\n\tlet skipped = 0;\n\n\tfor (const row of rows) {\n\t\tchecked++;\n\t\tconst text = `${row.title ?? \"\"}\\n${row.body_text ?? \"\"}`.trim();\n\t\tconst chunks = chunkText(text);\n\t\tif (chunks.length === 0) continue;\n\n\t\t// Check existing hashes\n\t\tconst existingRows = db\n\t\t\t.prepare(\"SELECT content_hash FROM memory_vectors WHERE memory_id = ? AND model = ?\")\n\t\t\t.all(row.id, model) as Array<{ content_hash: string | null }>;\n\t\tconst existingHashes = new Set(\n\t\t\texistingRows.map((r) => r.content_hash).filter((h): h is string => h != null),\n\t\t);\n\n\t\tconst pendingChunks: string[] = [];\n\t\tconst pendingHashes: string[] = [];\n\t\tfor (const chunk of chunks) {\n\t\t\tconst h = hashText(chunk);\n\t\t\tif (existingHashes.has(h)) {\n\t\t\t\tskipped++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpendingChunks.push(chunk);\n\t\t\tpendingHashes.push(h);\n\t\t}\n\n\t\tif (pendingChunks.length === 0) continue;\n\n\t\tconst embeddings = await embedTexts(pendingChunks);\n\t\tif (embeddings.length === 0) continue;\n\t\tembedded += embeddings.length;\n\n\t\tif (dryRun) {\n\t\t\tinserted += embeddings.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst stmt = db.prepare(\n\t\t\t\"INSERT INTO memory_vectors(embedding, memory_id, chunk_index, content_hash, model) VALUES (?, ?, ?, ?, ?)\",\n\t\t);\n\t\tfor (let i = 0; i < embeddings.length; i++) {\n\t\t\tconst vector = embeddings[i];\n\t\t\tif (!vector || vector.length === 0) continue;\n\t\t\tstmt.run(serializeFloat32(vector), row.id, i, pendingHashes[i], model);\n\t\t\tinserted++;\n\t\t}\n\t}\n\n\treturn { checked, embedded, inserted, skipped };\n}\n\n// ---------------------------------------------------------------------------\n// semanticSearch — vector KNN query\n// ---------------------------------------------------------------------------\n\nexport interface SemanticSearchResult {\n\tid: number;\n\tkind: string;\n\ttitle: string;\n\tbody_text: string;\n\tconfidence: number;\n\ttags_text: string;\n\tmetadata_json: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tsession_id: number;\n\tscore: number;\n\tdistance: number;\n}\n\n/**\n * Search for memories by vector similarity (KNN via sqlite-vec MATCH).\n * Returns an empty array when embeddings are disabled or unavailable.\n *\n * Matches Python's `_semantic_search()` in store/search.py.\n */\nexport async function semanticSearch(\n\tdb: Database,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: { project?: string | null } | null,\n): Promise<SemanticSearchResult[]> {\n\tif (query.trim().length < 3) return [];\n\n\tconst embeddings = await embedTexts([query]);\n\tif (embeddings.length === 0) return [];\n\n\tconst firstEmbedding = embeddings[0];\n\tif (!firstEmbedding) return [];\n\tconst queryEmbedding = serializeFloat32(firstEmbedding);\n\tconst params: unknown[] = [queryEmbedding, limit];\n\tconst whereClauses: string[] = [\"memory_items.active = 1\"];\n\tlet joinSessions = false;\n\n\tif (filters?.project) {\n\t\tconst pc = projectClause(filters.project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t\tjoinSessions = true;\n\t\t}\n\t}\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\n\tconst sql = `\n\t\tSELECT memory_items.*, memory_vectors.distance\n\t\tFROM memory_vectors\n\t\tJOIN memory_items ON memory_items.id = memory_vectors.memory_id\n\t\t${joinClause}\n\t\tWHERE memory_vectors.embedding MATCH ?\n\t\t AND k = ?\n\t\t AND ${where}\n\t\tORDER BY memory_vectors.distance ASC\n\t`;\n\n\tconst rows = db.prepare(sql).all(...params) as Array<Record<string, unknown>>;\n\n\treturn rows.map((row) => ({\n\t\tid: Number(row.id),\n\t\tkind: String(row.kind ?? \"observation\"),\n\t\ttitle: String(row.title ?? \"\"),\n\t\tbody_text: String(row.body_text ?? \"\"),\n\t\tconfidence: Number(row.confidence ?? 0),\n\t\ttags_text: String(row.tags_text ?? \"\"),\n\t\tmetadata_json: row.metadata_json == null ? null : String(row.metadata_json),\n\t\tcreated_at: String(row.created_at ?? \"\"),\n\t\tupdated_at: String(row.updated_at ?? \"\"),\n\t\tsession_id: Number(row.session_id),\n\t\tscore: 1.0 / (1.0 + Number(row.distance ?? 0)),\n\t\tdistance: Number(row.distance ?? 0),\n\t}));\n}\n","/**\n * Main ingest pipeline — processes raw coding session events, calls the\n * observer LLM, and stores extracted memories.\n *\n * Ports the `ingest()` function from codemem/plugin_ingest.py.\n *\n * Pipeline stages:\n * 1. Extract session context, events, cwd\n * 2. Create/find session in store\n * 3. Extract prompts, tool events, assistant messages\n * 4. Build transcript\n * 5. Budget tool events\n * 6. Build observer context + prompt\n * 7. Call observer LLM via ObserverClient.observe()\n * 8. Parse XML response\n * 9. Filter low-signal observations\n * 10. Persist observations as memories\n * 11. Persist session summary\n * 12. End session\n */\n\nimport { and, isNull, lt } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { normalizeProjectLabel } from \"./claude-hooks.js\";\nimport { toJson } from \"./db.js\";\nimport {\n\tbudgetToolEvents,\n\teventToToolEvent,\n\textractAdapterEvent,\n\textractToolEvents,\n\tprojectAdapterToolEvent,\n} from \"./ingest-events.js\";\nimport { isLowSignalObservation } from \"./ingest-filters.js\";\nimport { buildObserverPrompt } from \"./ingest-prompts.js\";\nimport {\n\tbuildTranscript,\n\tderiveRequest,\n\textractAssistantMessages,\n\textractAssistantUsage,\n\textractPrompts,\n\tfirstSentence,\n\tisTrivialRequest,\n\tnormalizeAdapterEvents,\n} from \"./ingest-transcript.js\";\nimport type {\n\tIngestPayload,\n\tObserverContext,\n\tParsedSummary,\n\tSessionContext,\n\tToolEvent,\n} from \"./ingest-types.js\";\nimport { hasMeaningfulObservation, parseObserverResponse } from \"./ingest-xml-parser.js\";\nimport type { ObserverClient } from \"./observer-client.js\";\nimport { resolveProject } from \"./project.js\";\nimport * as schema from \"./schema.js\";\nimport type { MemoryStore } from \"./store.js\";\nimport { deriveTags } from \"./tags.js\";\nimport { storeVectors } from \"./vectors.js\";\n\n// ---------------------------------------------------------------------------\n// Allowed memory kinds (matches Python)\n// ---------------------------------------------------------------------------\n\nconst ALLOWED_KINDS = new Set([\n\t\"bugfix\",\n\t\"feature\",\n\t\"refactor\",\n\t\"change\",\n\t\"discovery\",\n\t\"decision\",\n\t\"exploration\",\n]);\n\n// ---------------------------------------------------------------------------\n// Path normalization\n// ---------------------------------------------------------------------------\n\nfunction normalizePath(path: string, repoRoot: string | null): string {\n\tif (!path) return \"\";\n\tconst cleaned = path.trim();\n\tif (!repoRoot) return cleaned;\n\tconst root = repoRoot.replace(/\\/+$/, \"\");\n\tif (cleaned === root) return \".\";\n\tif (cleaned.startsWith(`${root}/`)) return cleaned.slice(root.length + 1);\n\treturn cleaned;\n}\n\nfunction normalizePaths(paths: string[], repoRoot: string | null): string[] {\n\treturn paths.map((p) => normalizePath(p, repoRoot)).filter(Boolean);\n}\n\n// ---------------------------------------------------------------------------\n// Summary body formatting\n// ---------------------------------------------------------------------------\n\nfunction summaryBody(summary: ParsedSummary): string {\n\tconst sections: [string, string][] = [\n\t\t[\"Request\", summary.request],\n\t\t[\"Completed\", summary.completed],\n\t\t[\"Learned\", summary.learned],\n\t\t[\"Investigated\", summary.investigated],\n\t\t[\"Next steps\", summary.nextSteps],\n\t\t[\"Notes\", summary.notes],\n\t];\n\treturn sections\n\t\t.filter(([, value]) => value)\n\t\t.map(([label, value]) => `## ${label}\\n${value}`)\n\t\t.join(\"\\n\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Event normalization (adapter projection)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert raw events with adapter envelopes into normalized flat events.\n * Handles both tool events (via adapter projection) and transcript events\n * (via normalizeAdapterEvents).\n */\nfunction normalizeEventsForToolExtraction(\n\tevents: Record<string, unknown>[],\n\tmaxChars: number,\n): ToolEvent[] {\n\tconst toolEvents: ToolEvent[] = [];\n\tfor (const event of events) {\n\t\tconst adapter = extractAdapterEvent(event);\n\t\tif (adapter) {\n\t\t\t// Skip tool_call events (only tool_result matters)\n\t\t\tif (adapter.event_type === \"tool_call\") continue;\n\t\t\tconst projected = projectAdapterToolEvent(adapter, event);\n\t\t\tif (projected) {\n\t\t\t\tconst te = eventToToolEvent(projected, maxChars);\n\t\t\t\tif (te) {\n\t\t\t\t\ttoolEvents.push(te);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Direct (non-adapter) events\n\t\tconst directEvents = extractToolEvents([event], maxChars);\n\t\ttoolEvents.push(...directEvents);\n\t}\n\treturn toolEvents;\n}\n\n// ---------------------------------------------------------------------------\n// Main pipeline\n// ---------------------------------------------------------------------------\n\nexport interface IngestOptions {\n\t/** Observer LLM client. */\n\tobserver: ObserverClient;\n\t/** Maximum chars per tool event payload (from config). Default 12000. */\n\tmaxChars?: number;\n\t/** Maximum chars for observer total budget. Default 12000. */\n\tobserverMaxChars?: number;\n\t/** Whether to store summaries. Default true. */\n\tstoreSummary?: boolean;\n\t/** Whether to store typed observations. Default true. */\n\tstoreTyped?: boolean;\n}\n\n/**\n * Process a batch of raw coding session events through the full ingest pipeline.\n *\n * Extracts prompts, tool events, and assistant messages from the payload,\n * builds a transcript, calls the observer LLM, parses the response,\n * filters low-signal content, and persists observations + summary.\n */\nexport async function ingest(\n\tpayload: IngestPayload,\n\tstore: MemoryStore,\n\toptions: IngestOptions,\n): Promise<void> {\n\tconst cwd = payload.cwd ?? process.cwd();\n\tconst events = payload.events ?? [];\n\tif (!Array.isArray(events) || events.length === 0) return;\n\n\tconst sessionContext = payload.sessionContext ?? {};\n\tconst storeSummary = options.storeSummary ?? true;\n\tconst storeTyped = options.storeTyped ?? true;\n\tconst maxChars = options.maxChars ?? 12_000;\n\tconst observerMaxChars = options.observerMaxChars ?? 12_000;\n\n\tconst d = drizzle(store.db, { schema });\n\tconst now = new Date().toISOString();\n\tconst project = normalizeProjectLabel(payload.project) ?? resolveProject(cwd) ?? null;\n\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: now,\n\t\t\tcwd,\n\t\t\tproject,\n\t\t\tuser: process.env.USER ?? \"unknown\",\n\t\t\ttool_version: \"plugin-ts\",\n\t\t\tmetadata_json: toJson({\n\t\t\t\tsource: \"plugin\",\n\t\t\t\tevent_count: events.length,\n\t\t\t\tstarted_at: payload.startedAt,\n\t\t\t\tsession_context: sessionContext,\n\t\t\t}),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst sessionId = rows[0]?.id;\n\tif (sessionId == null) throw new Error(\"session insert returned no id\");\n\n\ttry {\n\t\t// ------------------------------------------------------------------\n\t\t// Extract data from events\n\t\t// ------------------------------------------------------------------\n\t\tconst normalizedEvents = normalizeAdapterEvents(events);\n\t\tconst prompts = extractPrompts(normalizedEvents);\n\t\tconst promptNumber =\n\t\t\tprompts.length > 0 ? (prompts[prompts.length - 1]?.promptNumber ?? prompts.length) : null;\n\n\t\t// Tool events — handle adapter projection\n\t\tlet toolEvents = normalizeEventsForToolExtraction(events, maxChars);\n\n\t\t// Budget tool events\n\t\tconst toolBudget = Math.max(2000, Math.min(8000, observerMaxChars - 5000));\n\t\ttoolEvents = budgetToolEvents(toolEvents, toolBudget, 30);\n\n\t\t// Assistant messages\n\t\tconst assistantMessages = extractAssistantMessages(normalizedEvents);\n\t\tconst assistantUsageEvents = extractAssistantUsage(normalizedEvents);\n\t\tconst lastAssistantMessage = assistantMessages.at(-1) ?? null;\n\n\t\t// Latest prompt\n\t\tconst latestPrompt =\n\t\t\tsessionContext.firstPrompt ??\n\t\t\t(prompts.length > 0 ? prompts[prompts.length - 1]?.promptText : null) ??\n\t\t\tnull;\n\n\t\t// ------------------------------------------------------------------\n\t\t// Should we process?\n\t\t// ------------------------------------------------------------------\n\t\tlet shouldProcess =\n\t\t\ttoolEvents.length > 0 ||\n\t\t\tBoolean(latestPrompt) ||\n\t\t\t(storeSummary && Boolean(lastAssistantMessage));\n\n\t\tif (\n\t\t\tlatestPrompt &&\n\t\t\tisTrivialRequest(latestPrompt) &&\n\t\t\ttoolEvents.length === 0 &&\n\t\t\t!lastAssistantMessage\n\t\t) {\n\t\t\tshouldProcess = false;\n\t\t}\n\n\t\tif (!shouldProcess) {\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t\treturn;\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// Build transcript\n\t\t// ------------------------------------------------------------------\n\t\tconst transcript = buildTranscript(normalizedEvents);\n\n\t\t// ------------------------------------------------------------------\n\t\t// Build observer prompt\n\t\t// ------------------------------------------------------------------\n\t\tconst sessionSummaryParts: string[] = [];\n\t\tif ((sessionContext.promptCount ?? 0) > 1) {\n\t\t\tsessionSummaryParts.push(`Session had ${sessionContext.promptCount} prompts`);\n\t\t}\n\t\tif ((sessionContext.toolCount ?? 0) > 0) {\n\t\t\tsessionSummaryParts.push(`${sessionContext.toolCount} tool executions`);\n\t\t}\n\t\tif ((sessionContext.durationMs ?? 0) > 0) {\n\t\t\tconst durationMin = (sessionContext.durationMs ?? 0) / 60000;\n\t\t\tsessionSummaryParts.push(`~${durationMin.toFixed(1)} minutes of work`);\n\t\t}\n\t\tif (sessionContext.filesModified?.length) {\n\t\t\tsessionSummaryParts.push(`Modified: ${sessionContext.filesModified.slice(0, 5).join(\", \")}`);\n\t\t}\n\t\tif (sessionContext.filesRead?.length) {\n\t\t\tsessionSummaryParts.push(`Read: ${sessionContext.filesRead.slice(0, 5).join(\", \")}`);\n\t\t}\n\t\tconst sessionInfoText = sessionSummaryParts.join(\"; \");\n\n\t\tlet observerPrompt = latestPrompt ?? \"\";\n\t\tif (sessionInfoText) {\n\t\t\tobserverPrompt = observerPrompt\n\t\t\t\t? `${observerPrompt}\\n\\n[Session context: ${sessionInfoText}]`\n\t\t\t\t: `[Session context: ${sessionInfoText}]`;\n\t\t}\n\n\t\tconst observerContext: ObserverContext = {\n\t\t\tproject,\n\t\t\tuserPrompt: observerPrompt,\n\t\t\tpromptNumber,\n\t\t\ttoolEvents,\n\t\t\tlastAssistantMessage: storeSummary ? lastAssistantMessage : null,\n\t\t\tincludeSummary: storeSummary,\n\t\t\tdiffSummary: \"\",\n\t\t\trecentFiles: \"\",\n\t\t};\n\n\t\tconst { system, user } = buildObserverPrompt(observerContext);\n\n\t\t// ------------------------------------------------------------------\n\t\t// Call observer LLM\n\t\t// ------------------------------------------------------------------\n\t\tconst response = await options.observer.observe(system, user);\n\n\t\tif (!response.raw) {\n\t\t\t// Raw-event flushes must be lossless: if the observer returns no output,\n\t\t\t// fail the flush so we do NOT advance last_flushed_event_seq.\n\t\t\tif (sessionContext?.flusher === \"raw_events\") {\n\t\t\t\tthrow new Error(\"observer failed during raw-event flush\");\n\t\t\t}\n\n\t\t\t// Surface the failure for normal ingest paths.\n\t\t\tconst status = options.observer.getStatus();\n\t\t\tconsole.warn(\n\t\t\t\t`[codemem] Observer returned no output (provider=${response.provider}, model=${response.model}` +\n\t\t\t\t\t`${status.lastError ? `, error=${status.lastError}` : \"\"}). No memories will be created for this session.`,\n\t\t\t);\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t\treturn;\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// Parse response\n\t\t// ------------------------------------------------------------------\n\t\tconst rawText = response.raw;\n\t\tconst parsed = parseObserverResponse(rawText);\n\n\t\tconst observationsToStore: typeof parsed.observations = [];\n\t\tif (storeTyped && hasMeaningfulObservation(parsed.observations)) {\n\t\t\tfor (const obs of parsed.observations) {\n\t\t\t\tconst kind = obs.kind.trim().toLowerCase();\n\t\t\t\tif (!ALLOWED_KINDS.has(kind)) continue;\n\t\t\t\tif (!obs.title && !obs.narrative) continue;\n\t\t\t\tif (isLowSignalObservation(obs.title) || isLowSignalObservation(obs.narrative)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tobs.filesRead = normalizePaths(obs.filesRead, cwd);\n\t\t\t\tobs.filesModified = normalizePaths(obs.filesModified, cwd);\n\t\t\t\tobservationsToStore.push(obs);\n\t\t\t}\n\t\t}\n\n\t\tlet summaryToStore: { summary: ParsedSummary; request: string; body: string } | null = null;\n\t\tif (storeSummary && parsed.summary && !parsed.skipSummaryReason) {\n\t\t\tconst summary = parsed.summary;\n\t\t\tif (\n\t\t\t\tsummary.request ||\n\t\t\t\tsummary.investigated ||\n\t\t\t\tsummary.learned ||\n\t\t\t\tsummary.completed ||\n\t\t\t\tsummary.nextSteps ||\n\t\t\t\tsummary.notes\n\t\t\t) {\n\t\t\t\tsummary.filesRead = normalizePaths(summary.filesRead, cwd);\n\t\t\t\tsummary.filesModified = normalizePaths(summary.filesModified, cwd);\n\n\t\t\t\tlet request = summary.request;\n\t\t\t\tif (isTrivialRequest(request)) {\n\t\t\t\t\tconst derived = deriveRequest(summary);\n\t\t\t\t\tif (derived) request = derived;\n\t\t\t\t}\n\n\t\t\t\tconst body = summaryBody(summary);\n\t\t\t\tif (body && !isLowSignalObservation(firstSentence(body))) {\n\t\t\t\t\tsummaryToStore = { summary, request, body };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (sessionContext?.flusher === \"raw_events\") {\n\t\t\tconst storableCount = observationsToStore.length + (summaryToStore ? 1 : 0);\n\t\t\tif (storableCount === 0) {\n\t\t\t\tthrow new Error(\"observer produced no storable output for raw-event flush\");\n\t\t\t}\n\t\t}\n\n\t\tconst vectorWriteInputs: Array<{ memoryId: number; title: string; bodyText: string }> = [];\n\n\t\t// Persist all observations, summary, and usage atomically\n\t\tstore.db.transaction(() => {\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Filter and persist observations\n\t\t\t// ------------------------------------------------------------------\n\t\t\tfor (const obs of observationsToStore) {\n\t\t\t\tconst kind = obs.kind.trim().toLowerCase();\n\n\t\t\t\tconst bodyParts: string[] = [];\n\t\t\t\tif (obs.narrative) bodyParts.push(obs.narrative);\n\t\t\t\tif (obs.facts.length > 0) {\n\t\t\t\t\tbodyParts.push(obs.facts.map((f) => `- ${f}`).join(\"\\n\"));\n\t\t\t\t}\n\t\t\t\tconst bodyText = bodyParts.join(\"\\n\\n\");\n\n\t\t\t\tconst memoryTitle = obs.title || obs.narrative;\n\t\t\t\tconst tags = deriveTags({\n\t\t\t\t\tkind,\n\t\t\t\t\ttitle: memoryTitle,\n\t\t\t\t\tconcepts: obs.concepts,\n\t\t\t\t\tfilesRead: obs.filesRead,\n\t\t\t\t\tfilesModified: obs.filesModified,\n\t\t\t\t});\n\t\t\t\tconst memoryId = store.remember(sessionId, kind, memoryTitle, bodyText, 0.5, tags, {\n\t\t\t\t\tsubtitle: obs.subtitle,\n\t\t\t\t\tfacts: obs.facts,\n\t\t\t\t\tconcepts: obs.concepts,\n\t\t\t\t\tfiles_read: obs.filesRead,\n\t\t\t\t\tfiles_modified: obs.filesModified,\n\t\t\t\t\tprompt_number: promptNumber,\n\t\t\t\t\tsource: \"observer\",\n\t\t\t\t});\n\t\t\t\tvectorWriteInputs.push({ memoryId, title: memoryTitle, bodyText });\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Persist session summary\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (summaryToStore) {\n\t\t\t\tconst { summary, request, body } = summaryToStore;\n\t\t\t\tconst summaryTitle = request || \"Session summary\";\n\t\t\t\tconst summaryTags = deriveTags({\n\t\t\t\t\tkind: \"change\",\n\t\t\t\t\ttitle: summaryTitle,\n\t\t\t\t\tfilesRead: summary.filesRead,\n\t\t\t\t\tfilesModified: summary.filesModified,\n\t\t\t\t});\n\t\t\t\tconst memoryId = store.remember(sessionId, \"change\", summaryTitle, body, 0.3, summaryTags, {\n\t\t\t\t\tis_summary: true,\n\t\t\t\t\trequest,\n\t\t\t\t\tinvestigated: summary.investigated,\n\t\t\t\t\tlearned: summary.learned,\n\t\t\t\t\tcompleted: summary.completed,\n\t\t\t\t\tnext_steps: summary.nextSteps,\n\t\t\t\t\tnotes: summary.notes,\n\t\t\t\t\tprompt_number: promptNumber,\n\t\t\t\t\tfiles_read: summary.filesRead,\n\t\t\t\t\tfiles_modified: summary.filesModified,\n\t\t\t\t\tsource: \"observer_summary\",\n\t\t\t\t});\n\t\t\t\tvectorWriteInputs.push({ memoryId, title: summaryTitle, bodyText: body });\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Record observer usage\n\t\t\t// ------------------------------------------------------------------\n\t\t\tconst usageTokenTotal = assistantUsageEvents.reduce(\n\t\t\t\t(sum, e) => sum + (e.total_tokens ?? 0),\n\t\t\t\t0,\n\t\t\t);\n\t\t\td.insert(schema.usageEvents)\n\t\t\t\t.values({\n\t\t\t\t\tsession_id: sessionId,\n\t\t\t\t\tevent: \"observer_call\",\n\t\t\t\t\ttokens_read: rawText.length,\n\t\t\t\t\ttokens_written: transcript.length,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t\tmetadata_json: toJson({\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tobservation_count: observationsToStore.length,\n\t\t\t\t\t\thas_summary: summaryToStore != null,\n\t\t\t\t\t\tprovider: response.provider,\n\t\t\t\t\t\tmodel: response.model,\n\t\t\t\t\t\tsession_usage_tokens: usageTokenTotal,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t})();\n\n\t\tfor (const input of vectorWriteInputs) {\n\t\t\ttry {\n\t\t\t\tawait storeVectors(store.db, input.memoryId, input.title, input.bodyText);\n\t\t\t} catch {\n\t\t\t\t// Non-fatal — ingestion should not fail when embeddings are unavailable\n\t\t\t}\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// End session\n\t\t// ------------------------------------------------------------------\n\t\tendSession(store, sessionId, events.length, sessionContext);\n\t} catch (err) {\n\t\t// End session even on error\n\t\ttry {\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t} catch {\n\t\t\t// ignore cleanup errors\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Session lifecycle helpers\n// ---------------------------------------------------------------------------\n\nfunction endSession(\n\tstore: MemoryStore,\n\tsessionId: number,\n\teventCount: number,\n\tsessionContext: SessionContext,\n): void {\n\t// Use store.endSession() which merges metadata instead of replacing it,\n\t// preserving fields set during session creation (startedAt, session_context, etc.)\n\tstore.endSession(sessionId, {\n\t\tpost: {},\n\t\tsource: \"plugin\",\n\t\tevent_count: eventCount,\n\t\tsession_context: sessionContext,\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Orphan session cleanup\n// ---------------------------------------------------------------------------\n\n/**\n * Close orphan sessions (started but never ended) older than maxAgeHours.\n * Returns the number of sessions closed.\n */\nexport function cleanOrphanSessions(store: MemoryStore, maxAgeHours = 24): number {\n\tconst d = drizzle(store.db, { schema });\n\tconst cutoff = new Date(Date.now() - maxAgeHours * 3600_000).toISOString();\n\tconst result = d\n\t\t.update(schema.sessions)\n\t\t.set({ ended_at: cutoff })\n\t\t.where(and(isNull(schema.sessions.ended_at), lt(schema.sessions.started_at, cutoff)))\n\t\t.run();\n\treturn result.changes;\n}\n\n// ---------------------------------------------------------------------------\n// CLI entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Read a JSON payload from stdin and run the ingest pipeline.\n *\n * This is the TypeScript equivalent of `codemem ingest` — receives events\n * from the plugin and processes them through the observer LLM.\n */\nexport async function main(store: MemoryStore, observer: ObserverClient): Promise<void> {\n\tconst chunks: string[] = [];\n\tfor await (const chunk of process.stdin) {\n\t\tchunks.push(String(chunk));\n\t}\n\tconst raw = chunks.join(\"\");\n\tif (!raw.trim()) return;\n\n\tlet payload: IngestPayload;\n\ttry {\n\t\tpayload = JSON.parse(raw) as IngestPayload;\n\t} catch (err) {\n\t\tthrow new Error(`codemem: invalid payload: ${err}`);\n\t}\n\n\tawait ingest(payload, store, { observer });\n}\n","const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);\nconst MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);\n\n/**\n * Parse a strict integer string like Python's int(), but reject partial strings\n * like \"10abc\" that parseInt() would silently truncate.\n */\nexport function parseStrictInteger(value: string | undefined): number | null {\n\tif (value == null) return null;\n\tconst trimmed = value.trim();\n\tif (!/^[+-]?\\d+$/.test(trimmed)) return null;\n\ttry {\n\t\tconst parsed = BigInt(trimmed);\n\t\tif (parsed < MIN_SAFE_BIGINT || parsed > MAX_SAFE_BIGINT) return null;\n\t\treturn Number(parsed);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a positive memory ID matching Python's stricter semantics:\n * - allow integer numbers\n * - allow digit-only strings\n * - reject bools, floats, scientific notation, whitespacey strings, unsafe ints\n */\nexport function parsePositiveMemoryId(value: unknown): number | null {\n\tif (typeof value === \"boolean\") return null;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isInteger(value) || !Number.isSafeInteger(value) || value <= 0) return null;\n\t\treturn value;\n\t}\n\tif (typeof value === \"string\") {\n\t\tif (!/^\\d+$/.test(value)) return null;\n\t\ttry {\n\t\t\tconst parsed = BigInt(value);\n\t\t\tif (parsed <= 0n || parsed > MAX_SAFE_BIGINT) return null;\n\t\t\treturn Number(parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n","/**\n * Observer authentication: credential resolution, caching, and header rendering.\n *\n * Mirrors codemem/observer_auth.py — resolves auth tokens from explicit values,\n * environment variables, OAuth caches, files, or external commands. Supports\n * template-based header rendering with `${auth.token}` / `${auth.type}` / `${auth.source}`.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { arch, homedir, platform, release } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Inline version to avoid circular import (index.ts re-exports from this module).\n// Keep in sync with the VERSION constant in index.ts.\nconst PACKAGE_VERSION = \"0.0.1\";\n\n// ---------------------------------------------------------------------------\n// Redaction\n// ---------------------------------------------------------------------------\n\nconst REDACT_PATTERNS = [/sk-[A-Za-z0-9]{10,}/g, /Bearer\\s+[A-Za-z0-9._-]{10,}/g];\n\n/** Redact API keys and bearer tokens in text. Truncates at `limit` chars. */\nexport function redactText(text: string, limit = 400): string {\n\tlet redacted = text;\n\tfor (const pattern of REDACT_PATTERNS) {\n\t\tredacted = redacted.replace(new RegExp(pattern.source, pattern.flags), \"[redacted]\");\n\t}\n\treturn redacted.length > limit ? `${redacted.slice(0, limit)}…` : redacted;\n}\n\n// ---------------------------------------------------------------------------\n// Auth material\n// ---------------------------------------------------------------------------\n\nexport interface ObserverAuthMaterial {\n\ttoken: string | null;\n\tauthType: string;\n\tsource: string;\n}\n\nfunction noAuth(): ObserverAuthMaterial {\n\treturn { token: null, authType: \"none\", source: \"none\" };\n}\n\n// ---------------------------------------------------------------------------\n// OAuth cache (OpenCode's auth.json)\n// ---------------------------------------------------------------------------\n\nfunction getOpenCodeAuthPath(): string {\n\treturn join(homedir(), \".local\", \"share\", \"opencode\", \"auth.json\");\n}\n\n/** Load the OpenCode OAuth token cache from `~/.local/share/opencode/auth.json`. */\nexport function loadOpenCodeOAuthCache(): Record<string, unknown> {\n\tconst authPath = getOpenCodeAuthPath();\n\tif (!existsSync(authPath)) return {};\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(authPath, \"utf-8\"));\n\t\treturn data != null && typeof data === \"object\" && !Array.isArray(data) ? data : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Determine OAuth provider from configured provider name or model prefix. */\nexport function resolveOAuthProvider(configured: string | null | undefined, model: string): string {\n\tif (configured && [\"openai\", \"anthropic\"].includes(configured.toLowerCase())) {\n\t\treturn configured.toLowerCase();\n\t}\n\treturn model.toLowerCase().startsWith(\"claude\") ? \"anthropic\" : \"openai\";\n}\n\ntype OAuthEntry = Record<string, unknown>;\n\nfunction getOAuthEntry(cache: Record<string, unknown>, provider: string): OAuthEntry | null {\n\tconst entry = cache[provider];\n\treturn entry != null && typeof entry === \"object\" && !Array.isArray(entry)\n\t\t? (entry as OAuthEntry)\n\t\t: null;\n}\n\n/** Extract access token from OAuth cache for a given provider. */\nexport function extractOAuthAccess(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst access = entry.access;\n\treturn typeof access === \"string\" && access ? access : null;\n}\n\n/** Extract API key from auth cache for a given provider. */\nexport function extractProviderApiKey(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst key = entry.key;\n\treturn typeof key === \"string\" && key ? key : null;\n}\n\n/** Probe which credential sources are currently available per built-in provider. */\nexport function probeAvailableCredentials(): Record<\n\tstring,\n\t{ oauth: boolean; api_key: boolean; env_var: boolean }\n> {\n\tconst cache = loadOpenCodeOAuthCache();\n\tconst now = Date.now();\n\tconst explicitApiKey = Boolean(process.env.CODEMEM_OBSERVER_API_KEY);\n\tconst providers = [\n\t\t[\"openai\", \"OPENAI_API_KEY\"],\n\t\t[\"anthropic\", \"ANTHROPIC_API_KEY\"],\n\t\t[\"opencode\", null],\n\t] as const;\n\tconst result: Record<string, { oauth: boolean; api_key: boolean; env_var: boolean }> = {};\n\tfor (const [provider, envVar] of providers) {\n\t\tconst oauthAccess = extractOAuthAccess(cache, provider);\n\t\tconst oauthExpires = extractOAuthExpires(cache, provider);\n\t\tconst oauthValid =\n\t\t\tprovider === \"opencode\"\n\t\t\t\t? Boolean(extractProviderApiKey(cache, provider))\n\t\t\t\t: Boolean(oauthAccess) && (oauthExpires == null || oauthExpires > now);\n\t\tresult[provider] = {\n\t\t\toauth: oauthValid,\n\t\t\tapi_key: explicitApiKey,\n\t\t\tenv_var: envVar ? Boolean(process.env[envVar]) : false,\n\t\t};\n\t}\n\treturn result;\n}\n\n/** Extract account ID from OAuth cache for a given provider. */\nexport function extractOAuthAccountId(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst accountId = entry.accountId;\n\treturn typeof accountId === \"string\" && accountId ? accountId : null;\n}\n\n/** Extract expiry timestamp (ms) from OAuth cache for a given provider. */\nexport function extractOAuthExpires(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): number | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst expires = entry.expires;\n\treturn typeof expires === \"number\" ? expires : null;\n}\n\n// ---------------------------------------------------------------------------\n// Codex headers\n// ---------------------------------------------------------------------------\n\n/** Build OpenAI Codex transport headers from an OAuth access token. */\nexport function buildCodexHeaders(\n\taccessToken: string,\n\taccountId: string | null,\n): Record<string, string> {\n\tconst originator = process.env.CODEMEM_CODEX_ORIGINATOR ?? \"opencode\";\n\tconst userAgent =\n\t\tprocess.env.CODEMEM_CODEX_USER_AGENT ??\n\t\t`codemem/${PACKAGE_VERSION} (${platform()} ${release()}; ${arch()})`;\n\n\tconst headers: Record<string, string> = {\n\t\tauthorization: `Bearer ${accessToken}`,\n\t\toriginator,\n\t\t\"User-Agent\": userAgent,\n\t\taccept: \"text/event-stream\",\n\t};\n\tif (accountId) {\n\t\theaders[\"ChatGPT-Account-Id\"] = accountId;\n\t}\n\treturn headers;\n}\n\n// ---------------------------------------------------------------------------\n// External auth: command execution & file reading\n// ---------------------------------------------------------------------------\n\n/** Execute an external auth command and return the token (stdout, trimmed). */\nexport function runAuthCommand(command: string[], timeoutMs: number): string | null {\n\tconst cmd = command[0];\n\tif (!cmd) return null;\n\t// execFileSync timeout is in milliseconds\n\tconst effectiveTimeoutMs = Math.max(100, timeoutMs);\n\ttry {\n\t\tconst stdout = execFileSync(cmd, command.slice(1), {\n\t\t\ttimeout: effectiveTimeoutMs,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tconst token = (stdout ?? \"\").trim();\n\t\treturn token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Read a token from a file path (supports `~` and `$ENV_VAR` expansion). */\nexport function readAuthFile(filePath: string | null): string | null {\n\tif (!filePath) return null;\n\t// Expand ~ and $ENV_VAR\n\tlet resolved = filePath.replace(/^~/, homedir());\n\tresolved = resolved.replace(\n\t\t/\\$\\{([^}]+)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n\t\t(match, braced, bare) => {\n\t\t\tconst name = braced ?? bare;\n\t\t\treturn process.env[name] ?? match;\n\t\t},\n\t);\n\ttry {\n\t\tif (!existsSync(resolved) || !statSync(resolved).isFile()) return null;\n\t\tconst token = readFileSync(resolved, \"utf-8\").trim();\n\t\treturn token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Source normalization\n// ---------------------------------------------------------------------------\n\nconst VALID_SOURCES = new Set([\"\", \"auto\", \"env\", \"file\", \"command\", \"none\"]);\n\nfunction normalizeAuthSource(value: string | null | undefined): string {\n\tconst normalized = (value ?? \"\").trim().toLowerCase();\n\treturn VALID_SOURCES.has(normalized) ? normalized || \"auto\" : \"auto\";\n}\n\n// ---------------------------------------------------------------------------\n// Auth adapter (credential cascade with caching)\n// ---------------------------------------------------------------------------\n\nexport interface ObserverAuthResolveOptions {\n\texplicitToken?: string | null;\n\tenvTokens?: string[];\n\toauthToken?: string | null;\n\tforceRefresh?: boolean;\n}\n\n/**\n * Resolves auth credentials through a configurable cascade:\n * explicit → env → oauth → file → command.\n *\n * Results from file/command sources are cached for `cacheTtlS` seconds.\n */\nexport class ObserverAuthAdapter {\n\treadonly source: string;\n\treadonly filePath: string | null;\n\treadonly command: string[];\n\treadonly timeoutMs: number;\n\treadonly cacheTtlS: number;\n\n\tprivate cached: ObserverAuthMaterial = noAuth();\n\tprivate cachedAtMs = 0;\n\n\tconstructor(opts?: {\n\t\tsource?: string;\n\t\tfilePath?: string | null;\n\t\tcommand?: string[];\n\t\ttimeoutMs?: number;\n\t\tcacheTtlS?: number;\n\t}) {\n\t\tthis.source = opts?.source ?? \"auto\";\n\t\tthis.filePath = opts?.filePath ?? null;\n\t\tthis.command = opts?.command ?? [];\n\t\tthis.timeoutMs = opts?.timeoutMs ?? 1500;\n\t\tthis.cacheTtlS = opts?.cacheTtlS ?? 300;\n\t}\n\n\t/** Resolve auth material through the credential cascade. */\n\tresolve(opts?: ObserverAuthResolveOptions): ObserverAuthMaterial {\n\t\tconst source = normalizeAuthSource(this.source);\n\t\tconst explicitToken = opts?.explicitToken ?? null;\n\t\tconst envTokens = opts?.envTokens ?? [];\n\t\tconst oauthToken = opts?.oauthToken ?? null;\n\t\tconst forceRefresh = opts?.forceRefresh ?? false;\n\n\t\tif (source === \"none\") return noAuth();\n\n\t\t// Check cache for file/command sources\n\t\tif (!forceRefresh && (source === \"command\" || source === \"file\") && this.cacheTtlS > 0) {\n\t\t\tconst ageMs = performance.now() - this.cachedAtMs;\n\t\t\tif (this.cachedAtMs > 0 && ageMs <= this.cacheTtlS * 1000) {\n\t\t\t\treturn this.cached;\n\t\t\t}\n\t\t}\n\n\t\tlet token: string | null = null;\n\t\tlet tokenSource = \"none\";\n\n\t\tif (source === \"auto\") {\n\t\t\tif (explicitToken) {\n\t\t\t\ttoken = explicitToken;\n\t\t\t\ttokenSource = \"explicit\";\n\t\t\t}\n\t\t\tif (!token) {\n\t\t\t\ttoken = envTokens.find((t) => !!t) ?? null;\n\t\t\t\tif (token) tokenSource = \"env\";\n\t\t\t}\n\t\t\tif (!token && oauthToken) {\n\t\t\t\ttoken = oauthToken;\n\t\t\t\ttokenSource = \"oauth\";\n\t\t\t}\n\t\t} else if (source === \"env\") {\n\t\t\ttoken = envTokens.find((t) => !!t) ?? null;\n\t\t\tif (token) tokenSource = \"env\";\n\t\t}\n\n\t\tif ((source === \"auto\" || source === \"file\") && !token) {\n\t\t\ttoken = readAuthFile(this.filePath);\n\t\t\tif (token) tokenSource = \"file\";\n\t\t}\n\n\t\tif ((source === \"auto\" || source === \"command\") && !token) {\n\t\t\ttoken = runAuthCommand(this.command, this.timeoutMs);\n\t\t\tif (token) tokenSource = \"command\";\n\t\t}\n\n\t\tconst resolved: ObserverAuthMaterial = token\n\t\t\t? { token, authType: \"bearer\", source: tokenSource }\n\t\t\t: noAuth();\n\n\t\tconst shouldCache = source === \"command\" || source === \"file\";\n\t\tif (shouldCache && resolved.token) {\n\t\t\tthis.cached = resolved;\n\t\t\tthis.cachedAtMs = performance.now();\n\t\t} else if (shouldCache) {\n\t\t\tthis.invalidateCache();\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/** Clear the cached auth material. */\n\tinvalidateCache(): void {\n\t\tthis.cached = noAuth();\n\t\tthis.cachedAtMs = 0;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Header rendering\n// ---------------------------------------------------------------------------\n\nconst AUTH_TOKEN_RE = /\\$\\{auth\\.token\\}/g;\nconst AUTH_TYPE_RE = /\\$\\{auth\\.type\\}/g;\nconst AUTH_SOURCE_RE = /\\$\\{auth\\.source\\}/g;\n\n/** Render observer headers with `${auth.token}`, `${auth.type}`, `${auth.source}` substitution. */\nexport function renderObserverHeaders(\n\theaders: Record<string, string>,\n\tauth: ObserverAuthMaterial,\n): Record<string, string> {\n\tconst token = auth.token ?? \"\";\n\tconst rendered: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (typeof key !== \"string\" || typeof value !== \"string\") continue;\n\n\t\tlet candidate = value.replace(AUTH_TOKEN_RE, token);\n\t\tcandidate = candidate.replace(AUTH_TYPE_RE, auth.authType);\n\t\tcandidate = candidate.replace(AUTH_SOURCE_RE, auth.source);\n\n\t\t// Skip headers that reference auth.token when no token is available\n\t\t// biome-ignore lint/suspicious/noTemplateCurlyInString: intentional template pattern, not JS template literal\n\t\tif (value.includes(\"${auth.token}\") && !token) continue;\n\n\t\tconst cleaned = candidate.trim();\n\t\tif (!cleaned) continue;\n\t\trendered[key] = cleaned;\n\t}\n\treturn rendered;\n}\n","/**\n * Observer client: LLM caller for analyzing coding session transcripts.\n *\n * Mirrors codemem/observer.py — resolves provider config + auth, then calls\n * an LLM (Anthropic Messages or OpenAI Chat Completions) via fetch to extract\n * memories from session transcripts.\n *\n * Phase 1 scope: api_http runtime only (no claude_sidecar, no opencode_run).\n * Non-streaming responses via fetch (no SDK deps).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n\tbuildCodexHeaders,\n\textractOAuthAccess,\n\textractOAuthAccountId,\n\textractOAuthExpires,\n\textractProviderApiKey,\n\tloadOpenCodeOAuthCache,\n\tObserverAuthAdapter,\n\ttype ObserverAuthMaterial,\n\tredactText,\n\trenderObserverHeaders,\n\tresolveOAuthProvider,\n} from \"./observer-auth.js\";\nimport {\n\tgetOpenCodeProviderConfig,\n\tgetProviderApiKey,\n\tlistConfiguredOpenCodeProviders,\n\tresolveBuiltInProviderDefaultModel,\n\tresolveBuiltInProviderFromModel,\n\tresolveBuiltInProviderModel,\n\tresolveCustomProviderDefaultModel,\n\tresolveCustomProviderFromModel,\n\tresolveCustomProviderModel,\n\tstripJsonComments,\n\tstripTrailingCommas,\n} from \"./observer-config.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_ANTHROPIC_MODEL = \"claude-sonnet-4-20250514\";\nconst DEFAULT_OPENAI_MODEL = \"gpt-5.1-codex-mini\";\n\nconst ANTHROPIC_MESSAGES_ENDPOINT = \"https://api.anthropic.com/v1/messages\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\n\nconst CODEX_API_ENDPOINT = \"https://chatgpt.com/backend-api/codex/responses\";\n\nconst FETCH_TIMEOUT_MS = 60_000;\n\n// Anthropic model name aliases (friendly → API id)\nconst ANTHROPIC_MODEL_ALIASES: Record<string, string> = {\n\t\"claude-4.5-haiku\": \"claude-haiku-4-5\",\n\t\"claude-4.5-sonnet\": \"claude-sonnet-4-5\",\n\t\"claude-4.5-opus\": \"claude-opus-4-5\",\n\t\"claude-4.6-sonnet\": \"claude-sonnet-4-6\",\n\t\"claude-4.6-opus\": \"claude-opus-4-6\",\n\t\"claude-4.1-opus\": \"claude-opus-4-1\",\n\t\"claude-4.0-sonnet\": \"claude-sonnet-4-0\",\n\t\"claude-4.0-opus\": \"claude-opus-4-0\",\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ObserverConfig {\n\tobserverProvider: string | null;\n\tobserverModel: string | null;\n\tobserverRuntime: string | null;\n\tobserverApiKey: string | null;\n\tobserverBaseUrl: string | null;\n\tobserverMaxChars: number;\n\tobserverMaxTokens: number;\n\tobserverHeaders: Record<string, string>;\n\tobserverAuthSource: string;\n\tobserverAuthFile: string | null;\n\tobserverAuthCommand: string[];\n\tobserverAuthTimeoutMs: number;\n\tobserverAuthCacheTtlS: number;\n}\n\nexport interface ObserverResponse {\n\traw: string | null;\n\tparsed: Record<string, unknown> | null;\n\tprovider: string;\n\tmodel: string;\n}\n\nexport interface ObserverStatus {\n\tprovider: string;\n\tmodel: string;\n\truntime: string;\n\tauth: { source: string; type: string; hasToken: boolean };\n\tlastError?: { code: string; message: string } | null;\n}\n\n// ---------------------------------------------------------------------------\n// Config loading\n// ---------------------------------------------------------------------------\n\nfunction parseIntSafe(value: unknown, fallback: number): number {\n\tif (value == null) return fallback;\n\tconst n = typeof value === \"string\" ? Number.parseInt(value, 10) : Number(value);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction coerceStringMap(value: unknown): Record<string, string> | null {\n\tif (value == null) return null;\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed) return {};\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\tif (typeof parsed !== \"object\" || parsed == null || Array.isArray(parsed)) return null;\n\t\t\treturn parsed as Record<string, string>;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\treturn value as Record<string, string>;\n\t}\n\treturn null;\n}\n\nfunction coerceCommand(value: unknown): string[] | null {\n\tif (value == null) return null;\n\tif (Array.isArray(value)) {\n\t\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : null;\n\t}\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed) return [];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\tif (Array.isArray(parsed) && parsed.every((v: unknown) => typeof v === \"string\")) {\n\t\t\t\treturn parsed as string[];\n\t\t\t}\n\t\t} catch {\n\t\t\t/* not JSON — ignore */\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\n/**\n * Load observer config from `~/.config/codemem/config.json{c}`.\n *\n * Reads the codemem config file (not OpenCode's) and extracts observer-related\n * fields with environment variable overrides.\n */\nexport function loadObserverConfig(): ObserverConfig {\n\tconst defaults: ObserverConfig = {\n\t\tobserverProvider: null,\n\t\tobserverModel: null,\n\t\tobserverRuntime: null,\n\t\tobserverApiKey: null,\n\t\tobserverBaseUrl: null,\n\t\tobserverMaxChars: 12_000,\n\t\tobserverMaxTokens: 4_000,\n\t\tobserverHeaders: {},\n\t\tobserverAuthSource: \"auto\",\n\t\tobserverAuthFile: null,\n\t\tobserverAuthCommand: [],\n\t\tobserverAuthTimeoutMs: 1_500,\n\t\tobserverAuthCacheTtlS: 300,\n\t};\n\n\t// Read config file\n\tconst configDir = join(homedir(), \".config\", \"codemem\");\n\tconst envPath = process.env.CODEMEM_CONFIG;\n\tlet configPath: string | null = null;\n\tif (envPath) {\n\t\tconfigPath = envPath.replace(/^~/, homedir());\n\t} else {\n\t\tconst candidates = [join(configDir, \"config.json\"), join(configDir, \"config.jsonc\")];\n\t\tconfigPath = candidates.find((p) => existsSync(p)) ?? null;\n\t}\n\n\tlet data: Record<string, unknown> = {};\n\tif (configPath && existsSync(configPath)) {\n\t\ttry {\n\t\t\tlet text = readFileSync(configPath, \"utf-8\");\n\t\t\tif (text.trim()) {\n\t\t\t\ttry {\n\t\t\t\t\tdata = JSON.parse(text) as Record<string, unknown>;\n\t\t\t\t} catch {\n\t\t\t\t\ttext = stripTrailingCommas(stripJsonComments(text));\n\t\t\t\t\tdata = JSON.parse(text) as Record<string, unknown>;\n\t\t\t\t}\n\t\t\t\tif (typeof data !== \"object\" || data == null || Array.isArray(data)) {\n\t\t\t\t\tdata = {};\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\tdata = {};\n\t\t}\n\t}\n\n\t// Apply config file values\n\tconst cfg = { ...defaults };\n\n\tif (typeof data.observer_provider === \"string\") cfg.observerProvider = data.observer_provider;\n\tif (typeof data.observer_model === \"string\") cfg.observerModel = data.observer_model;\n\tif (typeof data.observer_runtime === \"string\") cfg.observerRuntime = data.observer_runtime;\n\tif (typeof data.observer_api_key === \"string\") cfg.observerApiKey = data.observer_api_key;\n\tif (typeof data.observer_base_url === \"string\") cfg.observerBaseUrl = data.observer_base_url;\n\tcfg.observerMaxChars = parseIntSafe(data.observer_max_chars, cfg.observerMaxChars);\n\tcfg.observerMaxTokens = parseIntSafe(data.observer_max_tokens, cfg.observerMaxTokens);\n\tif (typeof data.observer_auth_source === \"string\")\n\t\tcfg.observerAuthSource = data.observer_auth_source;\n\tif (typeof data.observer_auth_file === \"string\") cfg.observerAuthFile = data.observer_auth_file;\n\tcfg.observerAuthTimeoutMs = parseIntSafe(\n\t\tdata.observer_auth_timeout_ms,\n\t\tcfg.observerAuthTimeoutMs,\n\t);\n\tcfg.observerAuthCacheTtlS = parseIntSafe(\n\t\tdata.observer_auth_cache_ttl_s,\n\t\tcfg.observerAuthCacheTtlS,\n\t);\n\n\tconst headers = coerceStringMap(data.observer_headers);\n\tif (headers) cfg.observerHeaders = headers;\n\n\tconst authCmd = coerceCommand(data.observer_auth_command);\n\tif (authCmd) cfg.observerAuthCommand = authCmd;\n\n\t// Apply env var overrides (take precedence over file)\n\tcfg.observerProvider = process.env.CODEMEM_OBSERVER_PROVIDER ?? cfg.observerProvider;\n\tcfg.observerModel = process.env.CODEMEM_OBSERVER_MODEL ?? cfg.observerModel;\n\tcfg.observerRuntime = process.env.CODEMEM_OBSERVER_RUNTIME ?? cfg.observerRuntime;\n\tcfg.observerApiKey = process.env.CODEMEM_OBSERVER_API_KEY ?? cfg.observerApiKey;\n\tcfg.observerBaseUrl = process.env.CODEMEM_OBSERVER_BASE_URL ?? cfg.observerBaseUrl;\n\tcfg.observerAuthSource = process.env.CODEMEM_OBSERVER_AUTH_SOURCE ?? cfg.observerAuthSource;\n\tcfg.observerAuthFile = process.env.CODEMEM_OBSERVER_AUTH_FILE ?? cfg.observerAuthFile;\n\tcfg.observerMaxChars = parseIntSafe(process.env.CODEMEM_OBSERVER_MAX_CHARS, cfg.observerMaxChars);\n\tcfg.observerMaxTokens = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_MAX_TOKENS,\n\t\tcfg.observerMaxTokens,\n\t);\n\tcfg.observerAuthTimeoutMs = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_AUTH_TIMEOUT_MS,\n\t\tcfg.observerAuthTimeoutMs,\n\t);\n\tcfg.observerAuthCacheTtlS = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_AUTH_CACHE_TTL_S,\n\t\tcfg.observerAuthCacheTtlS,\n\t);\n\n\tconst envHeaders = coerceStringMap(process.env.CODEMEM_OBSERVER_HEADERS);\n\tif (envHeaders) cfg.observerHeaders = envHeaders;\n\n\tconst envAuthCmd = coerceCommand(process.env.CODEMEM_OBSERVER_AUTH_COMMAND);\n\tif (envAuthCmd) cfg.observerAuthCommand = envAuthCmd;\n\n\treturn cfg;\n}\n\n// ---------------------------------------------------------------------------\n// Auth error\n// ---------------------------------------------------------------------------\n\nexport class ObserverAuthError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ObserverAuthError\";\n\t}\n}\n\nfunction isAuthStatus(status: number): boolean {\n\treturn status === 401 || status === 403;\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic helpers\n// ---------------------------------------------------------------------------\n\nfunction normalizeAnthropicModel(model: string): string {\n\tconst normalized = model.trim();\n\tif (!normalized) return normalized;\n\treturn ANTHROPIC_MODEL_ALIASES[normalized.toLowerCase()] ?? normalized;\n}\n\nfunction resolveAnthropicEndpoint(): string {\n\treturn process.env.CODEMEM_ANTHROPIC_ENDPOINT ?? ANTHROPIC_MESSAGES_ENDPOINT;\n}\n\nfunction buildAnthropicHeaders(token: string, isOAuth: boolean): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"anthropic-version\": ANTHROPIC_VERSION,\n\t\t\"content-type\": \"application/json\",\n\t};\n\tif (isOAuth) {\n\t\theaders.authorization = `Bearer ${token}`;\n\t\theaders[\"anthropic-beta\"] = \"oauth-2025-04-20\";\n\t} else {\n\t\theaders[\"x-api-key\"] = token;\n\t}\n\treturn headers;\n}\n\nfunction buildAnthropicPayload(\n\tmodel: string,\n\tsystemPrompt: string,\n\tuserPrompt: string,\n\tmaxTokens: number,\n): Record<string, unknown> {\n\treturn {\n\t\tmodel: normalizeAnthropicModel(model),\n\t\tmax_tokens: maxTokens,\n\t\tsystem: systemPrompt,\n\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t};\n}\n\nfunction parseAnthropicResponse(body: Record<string, unknown>): string | null {\n\tconst content = body.content;\n\tif (!Array.isArray(content)) {\n\t\tconsole.warn(\n\t\t\t`[codemem] Anthropic response has no content array (stop_reason=${body.stop_reason ?? \"unknown\"}, ` +\n\t\t\t\t`keys=${Object.keys(body).join(\",\")})`,\n\t\t);\n\t\treturn null;\n\t}\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (\n\t\t\ttypeof block === \"object\" &&\n\t\t\tblock != null &&\n\t\t\t(block as Record<string, unknown>).type === \"text\"\n\t\t) {\n\t\t\tconst text = (block as Record<string, unknown>).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\tif (parts.length === 0 && content.length > 0) {\n\t\tconst blockTypes = content\n\t\t\t.map((b) =>\n\t\t\t\ttypeof b === \"object\" && b != null ? (b as Record<string, unknown>).type : typeof b,\n\t\t\t)\n\t\t\t.join(\",\");\n\t\tconsole.warn(\n\t\t\t`[codemem] Anthropic response has ${content.length} content block(s) but no text blocks (types=${blockTypes}, ` +\n\t\t\t\t`stop_reason=${body.stop_reason ?? \"unknown\"})`,\n\t\t);\n\t}\n\treturn parts.length > 0 ? parts.join(\"\") : null;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI helpers\n// ---------------------------------------------------------------------------\n\nfunction buildOpenAIHeaders(token: string): Record<string, string> {\n\treturn {\n\t\tauthorization: `Bearer ${token}`,\n\t\t\"content-type\": \"application/json\",\n\t};\n}\n\nfunction buildOpenAIPayload(\n\tmodel: string,\n\tsystemPrompt: string,\n\tuserPrompt: string,\n\tmaxTokens: number,\n): Record<string, unknown> {\n\treturn {\n\t\tmodel,\n\t\tmax_tokens: maxTokens,\n\t\ttemperature: 0,\n\t\tmessages: [\n\t\t\t{ role: \"system\", content: systemPrompt },\n\t\t\t{ role: \"user\", content: userPrompt },\n\t\t],\n\t};\n}\n\nfunction parseOpenAIResponse(body: Record<string, unknown>): string | null {\n\tconst choices = body.choices;\n\tif (!Array.isArray(choices) || choices.length === 0) return null;\n\tconst first = choices[0] as Record<string, unknown> | undefined;\n\tif (!first) return null;\n\tconst message = first.message as Record<string, unknown> | undefined;\n\tif (!message) return null;\n\tconst content = message.content;\n\treturn typeof content === \"string\" ? content : null;\n}\n\n// ---------------------------------------------------------------------------\n// Codex consumer helpers\n// ---------------------------------------------------------------------------\n\nfunction resolveCodexEndpoint(): string {\n\treturn process.env.CODEMEM_CODEX_ENDPOINT ?? CODEX_API_ENDPOINT;\n}\n\nfunction buildCodexPayload(model: string, userPrompt: string): Record<string, unknown> {\n\treturn {\n\t\tmodel,\n\t\tinstructions: \"You are a memory observer.\",\n\t\tinput: [\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: userPrompt }],\n\t\t\t},\n\t\t],\n\t\tstore: false,\n\t\tstream: true,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// SSE stream text extraction (shared for Codex and Anthropic OAuth)\n// ---------------------------------------------------------------------------\n\nfunction extractTextFromSSE(\n\trawText: string,\n\textractDelta: (event: Record<string, unknown>) => string | null,\n): string | null {\n\tconst parts: string[] = [];\n\tfor (const line of rawText.split(\"\\n\")) {\n\t\tif (!line.startsWith(\"data:\")) continue;\n\t\tconst payload = line.slice(5).trim();\n\t\tif (!payload || payload === \"[DONE]\") continue;\n\t\ttry {\n\t\t\tconst event = JSON.parse(payload) as Record<string, unknown>;\n\t\t\tconst delta = extractDelta(event);\n\t\t\tif (delta) parts.push(delta);\n\t\t} catch {\n\t\t\t// skip malformed events\n\t\t}\n\t}\n\treturn parts.length > 0 ? parts.join(\"\").trim() : null;\n}\n\nfunction extractCodexDelta(event: Record<string, unknown>): string | null {\n\tif (event.type === \"response.output_text.delta\") {\n\t\tconst delta = event.delta;\n\t\treturn typeof delta === \"string\" && delta ? delta : null;\n\t}\n\treturn null;\n}\n\nfunction extractAnthropicStreamDelta(event: Record<string, unknown>): string | null {\n\tif (event.type === \"content_block_delta\") {\n\t\tconst delta = event.delta as Record<string, unknown> | undefined;\n\t\tif (delta && delta.type === \"text_delta\") {\n\t\t\tconst text = delta.text;\n\t\t\treturn typeof text === \"string\" && text ? text : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// nowMs helper\n// ---------------------------------------------------------------------------\n\nfunction nowMs(): number {\n\treturn Date.now();\n}\n\n// ---------------------------------------------------------------------------\n// ObserverClient\n// ---------------------------------------------------------------------------\n\n/**\n * LLM client for analyzing coding session transcripts and extracting memories.\n *\n * Resolves provider + auth from codemem config, then calls the LLM via fetch.\n * Supports Anthropic Messages API, OpenAI Chat Completions, Codex consumer\n * (OpenAI OAuth + SSE), and Anthropic OAuth consumer (SSE).\n */\nexport class ObserverClient {\n\treadonly provider: string;\n\tmodel: string;\n\treadonly runtime: string;\n\treadonly maxChars: number;\n\treadonly maxTokens: number;\n\n\t/** Resolved auth material — updated on refresh. */\n\tauth: ObserverAuthMaterial;\n\treadonly authAdapter: ObserverAuthAdapter;\n\n\tprivate _observerHeaders: Record<string, string>;\n\tprivate _customBaseUrl: string | null;\n\tprivate readonly _apiKey: string | null;\n\n\t// OAuth consumer state\n\tprivate _codexAccess: string | null = null;\n\tprivate _codexAccountId: string | null = null;\n\tprivate _anthropicOAuthAccess: string | null = null;\n\n\t// Error tracking\n\tprivate _lastErrorCode: string | null = null;\n\tprivate _lastErrorMessage: string | null = null;\n\n\tconstructor(config?: ObserverConfig) {\n\t\tconst cfg = config ?? loadObserverConfig();\n\n\t\tconst provider = (cfg.observerProvider ?? \"\").toLowerCase();\n\t\tconst model = (cfg.observerModel ?? \"\").trim();\n\n\t\t// Collect known custom providers\n\t\tconst customProviders = listConfiguredOpenCodeProviders();\n\t\tif (provider && provider !== \"openai\" && provider !== \"anthropic\") {\n\t\t\tcustomProviders.add(provider);\n\t\t}\n\n\t\t// Resolve provider\n\t\tlet resolved = provider;\n\t\tif (!resolved) {\n\t\t\tconst inferred = resolveCustomProviderFromModel(model, customProviders);\n\t\t\tif (inferred) resolved = inferred;\n\t\t}\n\t\tif (!resolved) {\n\t\t\tconst builtIn = resolveBuiltInProviderFromModel(model);\n\t\t\tif (builtIn) resolved = builtIn;\n\t\t}\n\t\tif (!resolved) {\n\t\t\tresolved = resolveOAuthProvider(null, model || DEFAULT_OPENAI_MODEL);\n\t\t}\n\t\tif (\n\t\t\tresolved !== \"openai\" &&\n\t\t\tresolved !== \"anthropic\" &&\n\t\t\tresolved !== \"opencode\" &&\n\t\t\t!customProviders.has(resolved)\n\t\t) {\n\t\t\tresolved = \"openai\";\n\t\t}\n\t\tthis.provider = resolved;\n\n\t\t// Resolve runtime (Phase 1: api_http only)\n\t\tconst runtimeRaw = cfg.observerRuntime;\n\t\tconst runtime = typeof runtimeRaw === \"string\" ? runtimeRaw.trim().toLowerCase() : \"api_http\";\n\t\tthis.runtime = runtime === \"api_http\" ? \"api_http\" : \"api_http\"; // only api_http supported\n\n\t\t// Resolve model\n\t\tif (model) {\n\t\t\tthis.model = model;\n\t\t} else if (resolved === \"anthropic\") {\n\t\t\tthis.model = DEFAULT_ANTHROPIC_MODEL;\n\t\t} else if (resolved === \"openai\") {\n\t\t\tthis.model = DEFAULT_OPENAI_MODEL;\n\t\t} else {\n\t\t\tthis.model =\n\t\t\t\tresolveBuiltInProviderDefaultModel(resolved) ??\n\t\t\t\tresolveCustomProviderDefaultModel(resolved) ??\n\t\t\t\t\"\";\n\t\t}\n\n\t\tthis.maxChars = cfg.observerMaxChars;\n\t\tthis.maxTokens = cfg.observerMaxTokens;\n\t\tthis._observerHeaders = { ...cfg.observerHeaders };\n\t\tthis._apiKey = cfg.observerApiKey ?? null;\n\n\t\tconst baseUrl = cfg.observerBaseUrl;\n\t\tthis._customBaseUrl = typeof baseUrl === \"string\" && baseUrl.trim() ? baseUrl.trim() : null;\n\n\t\t// Set up auth adapter\n\t\tthis.authAdapter = new ObserverAuthAdapter({\n\t\t\tsource: cfg.observerAuthSource,\n\t\t\tfilePath: cfg.observerAuthFile,\n\t\t\tcommand: cfg.observerAuthCommand,\n\t\t\ttimeoutMs: Math.max(100, cfg.observerAuthTimeoutMs),\n\t\t\tcacheTtlS: Math.max(0, cfg.observerAuthCacheTtlS),\n\t\t});\n\t\tthis.auth = { token: null, authType: \"none\", source: \"none\" };\n\n\t\t// Initialize provider client state\n\t\tthis._initProvider(false);\n\t}\n\n\t/** Return the resolved runtime state of this observer client. */\n\tgetStatus(): ObserverStatus {\n\t\tlet method = \"none\";\n\t\tif (this._anthropicOAuthAccess) {\n\t\t\tmethod = \"anthropic_consumer\";\n\t\t} else if (this._codexAccess) {\n\t\t\tmethod = \"codex_consumer\";\n\t\t} else if (this.provider === \"opencode\" && this.auth.token) {\n\t\t\tmethod = \"sdk_client\";\n\t\t} else if (this.auth.token) {\n\t\t\tmethod = \"api_direct\";\n\t\t}\n\n\t\tconst status: ObserverStatus = {\n\t\t\tprovider: this.provider,\n\t\t\tmodel: this.model,\n\t\t\truntime: this.runtime,\n\t\t\tauth: {\n\t\t\t\tsource: this.auth.source,\n\t\t\t\ttype: method,\n\t\t\t\thasToken: !!this.auth.token,\n\t\t\t},\n\t\t};\n\t\tif (this._lastErrorMessage) {\n\t\t\tstatus.lastError = {\n\t\t\t\tcode: this._lastErrorCode ?? \"observer_error\",\n\t\t\t\tmessage: this._lastErrorMessage,\n\t\t\t};\n\t\t}\n\t\treturn status;\n\t}\n\n\t/** Force-refresh auth credentials. */\n\trefreshAuth(force = true): void {\n\t\tthis.authAdapter.invalidateCache();\n\t\tthis._initProvider(force);\n\t}\n\n\t/**\n\t * Call the LLM with a system prompt and user prompt, return the response.\n\t *\n\t * This is the main entry point. On auth errors, attempts one refresh + retry.\n\t */\n\tasync observe(systemPrompt: string, userPrompt: string): Promise<ObserverResponse> {\n\t\t// Enforce configured prompt-length cap (matches Python behavior)\n\t\tconst maxChars = this.maxChars;\n\t\tconst minUserBudget = Math.floor(maxChars * 0.25);\n\t\tconst systemBudget = Math.max(0, maxChars - minUserBudget);\n\t\tconst clippedSystem =\n\t\t\tsystemPrompt.length > systemBudget ? systemPrompt.slice(0, systemBudget) : systemPrompt;\n\t\tconst userBudget = Math.max(minUserBudget, maxChars - clippedSystem.length);\n\t\tconst clippedUser =\n\t\t\tuserPrompt.length > userBudget ? userPrompt.slice(0, userBudget) : userPrompt;\n\n\t\ttry {\n\t\t\tconst raw = await this._callOnce(clippedSystem, clippedUser);\n\t\t\tif (raw) this._clearLastError();\n\t\t\treturn {\n\t\t\t\traw,\n\t\t\t\tparsed: raw ? tryParseJSON(raw) : null,\n\t\t\t\tprovider: this.provider,\n\t\t\t\tmodel: this.model,\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) {\n\t\t\t\t// Attempt one auth refresh + retry\n\t\t\t\tthis.refreshAuth();\n\t\t\t\tif (!this.auth.token) throw err;\n\t\t\t\ttry {\n\t\t\t\t\tconst raw = await this._callOnce(clippedSystem, clippedUser);\n\t\t\t\t\tif (raw) this._clearLastError();\n\t\t\t\t\treturn {\n\t\t\t\t\t\traw,\n\t\t\t\t\t\tparsed: raw ? tryParseJSON(raw) : null,\n\t\t\t\t\t\tprovider: this.provider,\n\t\t\t\t\t\tmodel: this.model,\n\t\t\t\t\t};\n\t\t\t\t} catch {\n\t\t\t\t\tthrow err; // re-throw original\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Provider initialization\n\t// -----------------------------------------------------------------------\n\n\tprivate _initProvider(forceRefresh: boolean): void {\n\t\tthis._codexAccess = null;\n\t\tthis._codexAccountId = null;\n\t\tthis._anthropicOAuthAccess = null;\n\n\t\tconst oauthCache = loadOpenCodeOAuthCache();\n\t\tlet oauthAccess: string | null = null;\n\t\tlet oauthProvider: string | null = null;\n\n\t\tif (this.provider === \"openai\" || this.provider === \"anthropic\") {\n\t\t\toauthProvider = resolveOAuthProvider(this.provider, this.model);\n\t\t\toauthAccess = extractOAuthAccess(oauthCache, oauthProvider);\n\t\t\tconst oauthExpires = extractOAuthExpires(oauthCache, oauthProvider);\n\t\t\tif (oauthAccess && oauthExpires != null && oauthExpires <= nowMs()) {\n\t\t\t\toauthAccess = null;\n\t\t\t}\n\t\t}\n\n\t\tif (this.provider !== \"openai\" && this.provider !== \"anthropic\") {\n\t\t\t// Custom provider — resolve base URL, model ID, and headers from OpenCode config\n\t\t\tconst providerConfig = getOpenCodeProviderConfig(this.provider);\n\t\t\tconst hasExplicitProviderConfig = Object.keys(providerConfig).length > 0;\n\t\t\tconst [baseUrl, modelId, providerHeaders] = hasExplicitProviderConfig\n\t\t\t\t? resolveCustomProviderModel(this.provider, this.model)\n\t\t\t\t: resolveBuiltInProviderModel(this.provider, this.model);\n\n\t\t\t// Persist resolved values for use in _callOpenAIDirect\n\t\t\tif (baseUrl && !this._customBaseUrl) this._customBaseUrl = baseUrl;\n\t\t\tif (modelId) this.model = modelId;\n\t\t\tif (providerHeaders && Object.keys(providerHeaders).length > 0) {\n\t\t\t\tthis._observerHeaders = { ...this._observerHeaders, ...providerHeaders };\n\t\t\t}\n\n\t\t\tconst effectiveBaseUrl = this._customBaseUrl;\n\t\t\tif (!effectiveBaseUrl) return;\n\n\t\t\tconst cachedApiKey =\n\t\t\t\tthis.provider === \"opencode\" ? extractProviderApiKey(oauthCache, this.provider) : null;\n\t\t\tconst apiKey = getProviderApiKey(providerConfig) || this._apiKey || cachedApiKey;\n\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: apiKey,\n\t\t\t\tenvTokens: [process.env.CODEMEM_OBSERVER_API_KEY ?? \"\"],\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t} else if (this.provider === \"anthropic\") {\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: this._apiKey,\n\t\t\t\tenvTokens: [process.env.ANTHROPIC_API_KEY ?? \"\"],\n\t\t\t\toauthToken: oauthAccess,\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t\tif (this.auth.source === \"oauth\" && oauthAccess) {\n\t\t\t\tthis._anthropicOAuthAccess = oauthAccess;\n\t\t\t}\n\t\t} else {\n\t\t\t// OpenAI\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: this._apiKey,\n\t\t\t\tenvTokens: [\n\t\t\t\t\tprocess.env.OPENCODE_API_KEY ?? \"\",\n\t\t\t\t\tprocess.env.OPENAI_API_KEY ?? \"\",\n\t\t\t\t\tprocess.env.CODEX_API_KEY ?? \"\",\n\t\t\t\t],\n\t\t\t\toauthToken: oauthAccess,\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t\tif (this.auth.source === \"oauth\" && oauthAccess) {\n\t\t\t\tthis._codexAccess = oauthAccess;\n\t\t\t\tthis._codexAccountId = extractOAuthAccountId(oauthCache, oauthProvider ?? \"openai\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// LLM call dispatch\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callOnce(systemPrompt: string, userPrompt: string): Promise<string | null> {\n\t\t// Codex consumer path (OpenAI OAuth)\n\t\tif (this._codexAccess) {\n\t\t\treturn this._callCodexConsumer(userPrompt);\n\t\t}\n\n\t\t// Anthropic OAuth consumer path\n\t\tif (this._anthropicOAuthAccess) {\n\t\t\treturn this._callAnthropicConsumer(systemPrompt, userPrompt);\n\t\t}\n\n\t\t// Refresh if we have no token\n\t\tif (!this.auth.token) {\n\t\t\tthis._initProvider(true);\n\t\t\tif (this._codexAccess) return this._callCodexConsumer(userPrompt);\n\t\t\tif (this._anthropicOAuthAccess) return this._callAnthropicConsumer(systemPrompt, userPrompt);\n\t\t\tif (!this.auth.token) {\n\t\t\t\tthis._setLastError(`${capitalize(this.provider)} credentials are missing.`, \"auth_missing\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Direct API call via fetch\n\t\tif (this.provider === \"anthropic\") {\n\t\t\treturn this._callAnthropicDirect(systemPrompt, userPrompt);\n\t\t}\n\t\treturn this._callOpenAIDirect(systemPrompt, userPrompt);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Anthropic direct (API key)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callAnthropicDirect(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tconst url = resolveAnthropicEndpoint();\n\t\tconst token = this.auth.token ?? \"\";\n\t\tconst headers = buildAnthropicHeaders(token, false);\n\t\tconst mergedHeaders = {\n\t\t\t...headers,\n\t\t\t...renderObserverHeaders(this._observerHeaders, this.auth),\n\t\t};\n\t\tconst payload = buildAnthropicPayload(this.model, systemPrompt, userPrompt, this.maxTokens);\n\n\t\treturn this._fetchJSON(url, mergedHeaders, payload, {\n\t\t\tparseResponse: parseAnthropicResponse,\n\t\t\tproviderLabel: \"Anthropic\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// OpenAI direct (API key)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callOpenAIDirect(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tlet url: string;\n\t\tif (this._customBaseUrl) {\n\t\t\turl = `${this._customBaseUrl.replace(/\\/+$/, \"\")}/chat/completions`;\n\t\t} else {\n\t\t\turl = \"https://api.openai.com/v1/chat/completions\";\n\t\t}\n\n\t\tconst headers = buildOpenAIHeaders(this.auth.token ?? \"\");\n\t\tconst mergedHeaders = {\n\t\t\t...headers,\n\t\t\t...renderObserverHeaders(this._observerHeaders, this.auth),\n\t\t};\n\t\tconst payload = buildOpenAIPayload(this.model, systemPrompt, userPrompt, this.maxTokens);\n\n\t\treturn this._fetchJSON(url, mergedHeaders, payload, {\n\t\t\tparseResponse: parseOpenAIResponse,\n\t\t\tproviderLabel: capitalize(this.provider),\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Codex consumer (OpenAI OAuth + SSE streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callCodexConsumer(userPrompt: string): Promise<string | null> {\n\t\tif (!this._codexAccess) return null;\n\n\t\tconst headers = buildCodexHeaders(this._codexAccess, this._codexAccountId);\n\t\tif (Object.keys(this._observerHeaders).length > 0) {\n\t\t\tconst codexAuth: ObserverAuthMaterial = {\n\t\t\t\ttoken: this._codexAccess,\n\t\t\t\tauthType: \"bearer\",\n\t\t\t\tsource: this.auth.source,\n\t\t\t};\n\t\t\tObject.assign(headers, renderObserverHeaders(this._observerHeaders, codexAuth));\n\t\t}\n\t\theaders[\"content-type\"] = \"application/json\";\n\n\t\tconst payload = buildCodexPayload(this.model, userPrompt);\n\t\tconst url = resolveCodexEndpoint();\n\n\t\treturn this._fetchSSE(url, headers, payload, extractCodexDelta, {\n\t\t\tproviderLabel: \"OpenAI\",\n\t\t\tauthErrorMessage: \"OpenAI authentication failed. Refresh credentials and retry.\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Anthropic OAuth consumer (SSE streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callAnthropicConsumer(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tif (!this._anthropicOAuthAccess) return null;\n\n\t\tconst headers = buildAnthropicHeaders(this._anthropicOAuthAccess, true);\n\t\tif (Object.keys(this._observerHeaders).length > 0) {\n\t\t\tconst anthropicAuth: ObserverAuthMaterial = {\n\t\t\t\ttoken: this._anthropicOAuthAccess,\n\t\t\t\tauthType: \"bearer\",\n\t\t\t\tsource: this.auth.source,\n\t\t\t};\n\t\t\tObject.assign(headers, renderObserverHeaders(this._observerHeaders, anthropicAuth));\n\t\t}\n\n\t\t// Append ?beta=true to the endpoint\n\t\tconst baseEndpoint = resolveAnthropicEndpoint();\n\t\tconst endpointUrl = new URL(baseEndpoint);\n\t\tendpointUrl.searchParams.set(\"beta\", \"true\");\n\t\tconst url = endpointUrl.toString();\n\n\t\tconst payload: Record<string, unknown> = {\n\t\t\tmodel: normalizeAnthropicModel(this.model),\n\t\t\tmax_tokens: this.maxTokens,\n\t\t\tstream: true,\n\t\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t\t\tsystem: systemPrompt,\n\t\t};\n\n\t\treturn this._fetchSSE(url, headers, payload, extractAnthropicStreamDelta, {\n\t\t\tproviderLabel: \"Anthropic\",\n\t\t\tauthErrorMessage: \"Anthropic authentication failed. Refresh credentials and retry.\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Shared fetch: JSON response (non-streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _fetchJSON(\n\t\turl: string,\n\t\theaders: Record<string, string>,\n\t\tpayload: Record<string, unknown>,\n\t\topts: {\n\t\t\tparseResponse: (body: Record<string, unknown>) => string | null;\n\t\t\tproviderLabel: string;\n\t\t},\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\tsignal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorText = await response.text().catch(() => \"\");\n\t\t\t\tthis._handleHttpError(response.status, errorText, opts.providerLabel);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as Record<string, unknown>;\n\t\t\tconst result = opts.parseResponse(body);\n\t\t\tif (result === null) {\n\t\t\t\tthis._setLastError(\n\t\t\t\t\t`${opts.providerLabel} returned 200 but response contained no extractable text.`,\n\t\t\t\t\t\"empty_response\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) throw err;\n\t\t\tthis._setLastError(\n\t\t\t\t`${opts.providerLabel} processing failed during observer inference.`,\n\t\t\t\t\"observer_call_failed\",\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Shared fetch: SSE streaming response\n\t// -----------------------------------------------------------------------\n\n\tprivate async _fetchSSE(\n\t\turl: string,\n\t\theaders: Record<string, string>,\n\t\tpayload: Record<string, unknown>,\n\t\textractDelta: (event: Record<string, unknown>) => string | null,\n\t\topts: { providerLabel: string; authErrorMessage: string },\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\tsignal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\t// Consume body to avoid dangling connection\n\t\t\t\tawait response.text().catch(() => \"\");\n\t\t\t\tif (isAuthStatus(response.status)) {\n\t\t\t\t\tthis._setLastError(opts.authErrorMessage, \"auth_failed\");\n\t\t\t\t\tthrow new ObserverAuthError(`${opts.providerLabel} auth error: ${response.status}`);\n\t\t\t\t}\n\t\t\t\tthis._setLastError(\n\t\t\t\t\t`${opts.providerLabel} request failed during observer processing.`,\n\t\t\t\t\t\"provider_request_failed\",\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Read full response body as text and parse SSE events\n\t\t\tconst rawText = await response.text();\n\t\t\treturn extractTextFromSSE(rawText, extractDelta);\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) throw err;\n\t\t\tthis._setLastError(\n\t\t\t\t`${opts.providerLabel} processing failed during observer inference.`,\n\t\t\t\t\"observer_call_failed\",\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Error handling\n\t// -----------------------------------------------------------------------\n\n\tprivate _handleHttpError(status: number, errorText: string, providerLabel: string): void {\n\t\tconst summary = redactText(errorText);\n\n\t\tif (isAuthStatus(status)) {\n\t\t\tthis._setLastError(\n\t\t\t\t`${providerLabel} authentication failed. Refresh credentials and retry.`,\n\t\t\t\t\"auth_failed\",\n\t\t\t);\n\t\t\tthrow new ObserverAuthError(`${providerLabel} auth error: ${status}: ${summary}`);\n\t\t}\n\n\t\tif (status === 429) {\n\t\t\tthis._setLastError(`${providerLabel} rate limited. Retry later.`, \"rate_limited\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for model-not-found in Anthropic error responses\n\t\tif (errorText) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(errorText) as Record<string, unknown>;\n\t\t\t\tconst error = parsed.error as Record<string, unknown> | undefined;\n\t\t\t\tif (error && typeof error === \"object\") {\n\t\t\t\t\tconst errorType = String(error.type ?? \"\").toLowerCase();\n\t\t\t\t\tconst message = String(error.message ?? \"\");\n\t\t\t\t\tif (errorType === \"not_found_error\" && message.toLowerCase().startsWith(\"model:\")) {\n\t\t\t\t\t\tthis._setLastError(\n\t\t\t\t\t\t\t`${providerLabel} model ID not found: ${message.split(\":\")[1]?.trim() ?? this.model}.`,\n\t\t\t\t\t\t\t\"invalid_model_id\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// not JSON — ignore\n\t\t\t}\n\t\t}\n\n\t\tthis._setLastError(`${providerLabel} request failed (${status}).`, \"provider_request_failed\");\n\t}\n\n\tprivate _setLastError(message: string, code?: string): void {\n\t\tconst text = message.trim();\n\t\tif (!text) return;\n\t\tthis._lastErrorMessage = text;\n\t\tthis._lastErrorCode = (code ?? \"observer_error\").trim() || \"observer_error\";\n\t}\n\n\tprivate _clearLastError(): void {\n\t\tthis._lastErrorCode = null;\n\t\tthis._lastErrorMessage = null;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction capitalize(s: string): string {\n\treturn s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction tryParseJSON(text: string): Record<string, unknown> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(text);\n\t\treturn typeof parsed === \"object\" && parsed != null && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","/**\n * FTS5 full-text search, timeline, and explain for memory items.\n *\n * FTS5 MATCH queries and BM25 scoring use raw SQL (Drizzle has no FTS5 support).\n * Simple queries (anchor lookup, batch ID fetch, session projects) use Drizzle typed queries.\n * Dynamic filter queries use raw SQL since the filter builder returns SQL strings.\n */\n\nimport { and, eq, inArray } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { fromJson } from \"./db.js\";\nimport {\n\tbuildFilterClausesWithContext,\n\tnormalizeFilterStrings,\n\tnormalizeVisibilityValues,\n\tnormalizeWorkspaceKinds,\n} from \"./filters.js\";\nimport { parsePositiveMemoryId } from \"./integers.js\";\nimport { projectMatchesFilter } from \"./project.js\";\nimport * as schema from \"./schema.js\";\nimport type {\n\tExplainError,\n\tExplainItem,\n\tExplainResponse,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tTimelineItemResponse,\n} from \"./types.js\";\n\nexport interface ExplainOptions {\n\tincludePackContext?: boolean;\n}\n\n/** Lazily wrap a better-sqlite3 Database in a Drizzle ORM instance. */\nfunction getDrizzle(db: Database) {\n\treturn drizzle(db, { schema });\n}\n\n/** Structural type for the store parameter (avoids circular import). */\nexport interface StoreHandle {\n\treadonly db: import(\"better-sqlite3\").Database;\n\treadonly actorId: string;\n\treadonly deviceId: string;\n\tget(memoryId: number): MemoryItemResponse | null;\n\tmemoryOwnedBySelf(item: MemoryItem | MemoryResult | Record<string, unknown>): boolean;\n\trecent(limit?: number, filters?: MemoryFilters | null, offset?: number): MemoryItemResponse[];\n\trecentByKinds(\n\t\tkinds: string[],\n\t\tlimit?: number,\n\t\tfilters?: MemoryFilters | null,\n\t\toffset?: number,\n\t): MemoryItemResponse[];\n}\n\nconst MEMORY_KIND_BONUS: Record<string, number> = {\n\tsession_summary: 0.25,\n\tdecision: 0.2,\n\tfeature: 0.18,\n\tbugfix: 0.18,\n\trefactor: 0.17,\n\tnote: 0.15,\n\tchange: 0.12,\n\tdiscovery: 0.12,\n\tobservation: 0.1,\n\texploration: 0.1,\n\tentities: 0.05,\n};\n\nconst PERSONAL_FIRST_BONUS = 0.45;\nconst TRUST_BIAS_LEGACY_UNKNOWN_PENALTY = 0.18;\nconst TRUST_BIAS_UNREVIEWED_PENALTY = 0.12;\nconst WIDEN_SHARED_DEFAULT_MIN_PERSONAL_RESULTS = 3;\nconst WIDEN_SHARED_DEFAULT_MIN_PERSONAL_SCORE = 0.0;\nconst WIDEN_SHARED_MAX_SHARED_RESULTS = 2;\nconst PERSONAL_QUERY_PATTERNS = [\n\t/\\bwhat did i\\b/i,\n\t/\\bmy notes\\b/i,\n\t/\\bmy last session\\b/i,\n\t/\\bmy machine\\b/i,\n];\n\n/** FTS5 operators that must be stripped from user queries. */\nconst FTS5_OPERATORS = new Set([\"or\", \"and\", \"not\", \"near\", \"phrase\"]);\n\n/**\n * Expand a user query string into an FTS5 MATCH expression.\n *\n * Extracts alphanumeric tokens, filters out FTS5 operators,\n * and joins multiple tokens with OR for broader matching.\n */\nexport function expandQuery(query: string): string {\n\tconst rawTokens = query.match(/[A-Za-z0-9_]+/g);\n\tif (!rawTokens) return \"\";\n\tconst tokens = rawTokens.filter((t) => !FTS5_OPERATORS.has(t.toLowerCase()));\n\tif (tokens.length === 0) return \"\";\n\tif (tokens.length === 1) return tokens[0] as string;\n\treturn tokens.join(\" OR \");\n}\n\n/**\n * Compute a recency score for a memory based on its creation timestamp.\n *\n * Returns a value in (0, 1] where 1.0 means \"just created\" and the\n * score decays with a 7-day half-life: score = 1 / (1 + days_ago / 7).\n */\nexport function recencyScore(createdAt: string, now?: Date): number {\n\tconst parsed = Date.parse(createdAt);\n\tif (Number.isNaN(parsed)) return 0.0;\n\n\tconst referenceNow = now ?? new Date();\n\tconst ageDays = Math.max(0, Math.floor((referenceNow.getTime() - parsed) / 86_400_000));\n\treturn 1.0 / (1.0 + ageDays / 7.0);\n}\n\n/**\n * Return a scoring bonus for a memory kind.\n *\n * Higher-signal kinds (decisions, features) get a larger bonus.\n * Unknown or null kinds return 0.\n */\nexport function kindBonus(kind: string | null): number {\n\tif (!kind) return 0.0;\n\treturn MEMORY_KIND_BONUS[kind.trim().toLowerCase()] ?? 0.0;\n}\n\nfunction personalFirstEnabled(filters: MemoryFilters | undefined): boolean {\n\tif (!filters || filters.personal_first === undefined) return true;\n\tconst value = filters.personal_first;\n\tif (typeof value === \"string\") {\n\t\tconst lowered = value.trim().toLowerCase();\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(lowered)) return false;\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(lowered)) return true;\n\t}\n\treturn Boolean(value);\n}\n\nfunction trustBiasMode(filters: MemoryFilters | undefined): \"off\" | \"soft\" {\n\tconst value = String(filters?.trust_bias ?? \"off\")\n\t\t.trim()\n\t\t.toLowerCase();\n\treturn value === \"soft\" ? \"soft\" : \"off\";\n}\n\nfunction widenSharedWhenWeakEnabled(filters: MemoryFilters | undefined): boolean {\n\tif (!filters || filters.widen_shared_when_weak === undefined) return false;\n\tconst value = filters.widen_shared_when_weak;\n\tif (typeof value === \"string\") {\n\t\tconst lowered = value.trim().toLowerCase();\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(lowered)) return false;\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(lowered)) return true;\n\t}\n\treturn Boolean(value);\n}\n\nfunction widenSharedMinPersonalResults(filters: MemoryFilters | undefined): number {\n\tconst value = filters?.widen_shared_min_personal_results;\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn WIDEN_SHARED_DEFAULT_MIN_PERSONAL_RESULTS;\n\t}\n\treturn Math.max(1, Math.trunc(value));\n}\n\nfunction widenSharedMinPersonalScore(filters: MemoryFilters | undefined): number {\n\tconst value = filters?.widen_shared_min_personal_score;\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn WIDEN_SHARED_DEFAULT_MIN_PERSONAL_SCORE;\n\t}\n\treturn Math.max(0, value);\n}\n\nfunction queryBlocksSharedWidening(query: string): boolean {\n\treturn PERSONAL_QUERY_PATTERNS.some((pattern) => pattern.test(query));\n}\n\nfunction filtersBlockSharedWidening(filters: MemoryFilters | undefined): boolean {\n\tif (!filters) return false;\n\tconst ownershipScope = String(filters.ownership_scope ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (ownershipScope === \"mine\" || ownershipScope === \"theirs\") return true;\n\tconst includeVisibility = normalizeVisibilityValues(\n\t\tfilters.include_visibility ?? filters.visibility,\n\t);\n\tconst excludeVisibility = normalizeVisibilityValues(filters.exclude_visibility);\n\tconst includeWorkspaceIds = normalizeFilterStrings(filters.include_workspace_ids);\n\tconst excludeWorkspaceIds = normalizeFilterStrings(filters.exclude_workspace_ids);\n\tconst includeWorkspaceKinds = normalizeWorkspaceKinds(filters.include_workspace_kinds);\n\tconst excludeWorkspaceKinds = normalizeWorkspaceKinds(filters.exclude_workspace_kinds);\n\tif (includeVisibility.length || includeWorkspaceIds.length || includeWorkspaceKinds.length)\n\t\treturn true;\n\tif (excludeVisibility.includes(\"private\") || excludeVisibility.includes(\"shared\")) return true;\n\tif (excludeWorkspaceKinds.includes(\"shared\") || excludeWorkspaceKinds.includes(\"personal\")) {\n\t\treturn true;\n\t}\n\treturn excludeWorkspaceIds.some(\n\t\t(value) => value.startsWith(\"personal:\") || value.startsWith(\"shared:\"),\n\t);\n}\n\nfunction sharedWideningFilters(filters: MemoryFilters | undefined): MemoryFilters {\n\treturn {\n\t\t...(filters ?? {}),\n\t\tvisibility: undefined,\n\t\townership_scope: undefined,\n\t\tinclude_visibility: [\"shared\"],\n\t\tinclude_workspace_kinds: [\"shared\"],\n\t\tpersonal_first: false,\n\t\twiden_shared_when_weak: false,\n\t};\n}\n\nfunction markWideningMetadata(items: MemoryResult[]): MemoryResult[] {\n\treturn items.map((item) => ({\n\t\t...item,\n\t\tmetadata: { ...(item.metadata ?? {}), widened_from_shared: true },\n\t}));\n}\n\nfunction personalBias(\n\tstore: StoreHandle,\n\titem: MemoryResult,\n\tfilters: MemoryFilters | undefined,\n): number {\n\tif (!personalFirstEnabled(filters)) return 0.0;\n\treturn store.memoryOwnedBySelf(item) ? PERSONAL_FIRST_BONUS : 0.0;\n}\n\nfunction sharedTrustPenalty(\n\tstore: StoreHandle,\n\titem: MemoryResult,\n\tfilters: MemoryFilters | undefined,\n): number {\n\tif (trustBiasMode(filters) !== \"soft\") return 0.0;\n\tif (store.memoryOwnedBySelf(item)) return 0.0;\n\tconst metadata = item.metadata ?? {};\n\tconst visibility = String(metadata.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tconst workspaceKind = String(metadata.workspace_kind ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (visibility !== \"shared\" && workspaceKind !== \"shared\") return 0.0;\n\tconst trustState = String(metadata.trust_state ?? \"trusted\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (trustState === \"legacy_unknown\") return TRUST_BIAS_LEGACY_UNKNOWN_PENALTY;\n\tif (trustState === \"unreviewed\") return TRUST_BIAS_UNREVIEWED_PENALTY;\n\treturn 0.0;\n}\n\nfunction canonicalPath(rawPath: string): string {\n\tlet path = rawPath.trim().replaceAll(\"\\\\\", \"/\");\n\tif (path.startsWith(\"./\")) {\n\t\tpath = path.slice(2);\n\t}\n\tconst parts = path.split(\"/\").filter((part) => part && part !== \".\");\n\tif (parts.length === 0) return \"\";\n\treturn parts.join(\"/\").toLowerCase();\n}\n\nfunction pathSegments(path: string): string[] {\n\tconst canonical = canonicalPath(path);\n\tif (!canonical) return [];\n\treturn canonical.split(\"/\");\n}\n\nfunction pathBasename(path: string): string {\n\tconst segments = pathSegments(path);\n\tif (segments.length === 0) return \"\";\n\treturn segments[segments.length - 1] ?? \"\";\n}\n\nfunction pathSegmentsOverlap(a: string[], b: string[]): boolean {\n\tif (a.length === 0 || b.length === 0) return false;\n\tif (a.length <= b.length) {\n\t\treturn b.slice(b.length - a.length).join(\"/\") === a.join(\"/\");\n\t}\n\treturn a.slice(a.length - b.length).join(\"/\") === b.join(\"/\");\n}\n\nfunction normalizeWorkingSetPaths(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const raw of value) {\n\t\tif (typeof raw !== \"string\") continue;\n\t\tconst path = canonicalPath(raw);\n\t\tif (!path || seen.has(path)) continue;\n\t\tseen.add(path);\n\t\tnormalized.push(path);\n\t}\n\treturn normalized;\n}\n\nfunction memoryFilesModified(item: MemoryResult): string[] {\n\tconst metadata = item.metadata ?? {};\n\tconst rawPaths = metadata.files_modified;\n\tif (!Array.isArray(rawPaths)) return [];\n\tconst normalized: string[] = [];\n\tfor (const raw of rawPaths) {\n\t\tif (typeof raw !== \"string\") continue;\n\t\tconst path = canonicalPath(raw);\n\t\tif (path) normalized.push(path);\n\t}\n\treturn normalized;\n}\n\nfunction workingSetOverlapBoost(item: MemoryResult, workingSetPaths: string[]): number {\n\tif (workingSetPaths.length === 0) return 0.0;\n\tconst itemPaths = memoryFilesModified(item);\n\tif (itemPaths.length === 0) return 0.0;\n\n\tconst itemPathSegments = [...new Set(itemPaths)].map((path) => pathSegments(path));\n\tconst workingSetSegments = [...new Set(workingSetPaths)].map((path) => pathSegments(path));\n\tconst itemBasenames = new Set(itemPaths.map((path) => pathBasename(path)).filter(Boolean));\n\tconst workingSetBasenames = new Set(\n\t\tworkingSetPaths.map((path) => pathBasename(path)).filter(Boolean),\n\t);\n\n\tlet directHits = 0;\n\tfor (const itemSegment of itemPathSegments) {\n\t\tif (workingSetSegments.some((wsSegment) => pathSegmentsOverlap(itemSegment, wsSegment))) {\n\t\t\tdirectHits += 1;\n\t\t}\n\t}\n\n\tlet basenameHits = 0;\n\tfor (const basename of itemBasenames) {\n\t\tif (workingSetBasenames.has(basename)) basenameHits += 1;\n\t}\n\n\tconst boost = directHits * 0.16 + basenameHits * 0.06;\n\treturn Math.min(0.32, boost);\n}\n\n/**\n * Re-rank search results by combining BM25 score, recency, kind bonus,\n * personal bias, and shared trust penalty.\n */\nexport function rerankResults(\n\tstore: StoreHandle,\n\tresults: MemoryResult[],\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst referenceNow = new Date();\n\tconst workingSetPaths = normalizeWorkingSetPaths(filters?.working_set_paths);\n\n\tconst scored = results.map((item) => ({\n\t\titem,\n\t\tcombinedScore:\n\t\t\titem.score * 1.5 +\n\t\t\trecencyScore(item.created_at, referenceNow) +\n\t\t\tkindBonus(item.kind) +\n\t\t\tworkingSetOverlapBoost(item, workingSetPaths) +\n\t\t\tpersonalBias(store, item, filters) -\n\t\t\tsharedTrustPenalty(store, item, filters),\n\t}));\n\n\tscored.sort((a, b) => b.combinedScore - a.combinedScore);\n\n\treturn scored.slice(0, limit).map((s) => s.item);\n}\n\n/**\n * Execute an FTS5 full-text search against memory_items.\n *\n * Uses expandQuery to prepare the MATCH expression, applies filters\n * via buildFilterClauses, and re-ranks results. Optionally widens to\n * shared workspaces when personal results are weak.\n */\nexport function search(\n\tstore: StoreHandle,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst primary = searchOnce(store, query, limit, filters);\n\tif (\n\t\t!widenSharedWhenWeakEnabled(filters) ||\n\t\t!query ||\n\t\tqueryBlocksSharedWidening(query) ||\n\t\tfiltersBlockSharedWidening(filters)\n\t) {\n\t\treturn primary;\n\t}\n\n\tconst personalResults = primary.filter((item) => store.memoryOwnedBySelf(item));\n\tconst strongestPersonalScore = personalResults[0]?.score ?? -Infinity;\n\tconst personalStrongEnough =\n\t\tpersonalResults.length >= widenSharedMinPersonalResults(filters) &&\n\t\tstrongestPersonalScore >= widenSharedMinPersonalScore(filters);\n\tif (personalStrongEnough) return primary;\n\n\tconst shared = markWideningMetadata(\n\t\tsearchOnce(\n\t\t\tstore,\n\t\t\tquery,\n\t\t\tWIDEN_SHARED_MAX_SHARED_RESULTS,\n\t\t\tsharedWideningFilters(filters),\n\t\t).filter((item) => !store.memoryOwnedBySelf(item)),\n\t);\n\tconst seen = new Set(primary.map((item) => item.id));\n\tconst combined = [...primary];\n\tlet addedShared = 0;\n\tfor (const item of shared) {\n\t\tif (seen.has(item.id)) continue;\n\t\tseen.add(item.id);\n\t\tcombined.push(item);\n\t\taddedShared += 1;\n\t\tif (addedShared >= WIDEN_SHARED_MAX_SHARED_RESULTS) break;\n\t}\n\treturn combined;\n}\n\nfunction searchOnce(\n\tstore: StoreHandle,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst effectiveLimit = Math.max(1, Math.trunc(limit));\n\tconst expanded = expandQuery(query);\n\tif (!expanded) return [];\n\n\t// Widen the SQL candidate set before reranking — kindBonus can promote items\n\t// that SQL ordering missed, so we fetch more than the final limit.\n\tconst queryLimit = Math.min(Math.max(effectiveLimit * 4, effectiveLimit + 8), 200);\n\n\tconst params: unknown[] = [expanded];\n\tconst whereClauses = [\"memory_items.active = 1\", \"memory_fts MATCH ?\"];\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\twhereClauses.push(...filterResult.clauses);\n\tparams.push(...filterResult.params);\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\n\tconst sql = `\n\t\tSELECT memory_items.*,\n\t\t\t-bm25(memory_fts, 1.0, 1.0, 0.25) AS score,\n\t\t\t(1.0 / (1.0 + ((julianday('now') - julianday(memory_items.created_at)) / 7.0))) AS recency\n\t\tFROM memory_fts\n\t\tJOIN memory_items ON memory_items.id = memory_fts.rowid\n\t\t${joinClause}\n\t\tWHERE ${where}\n\t\tORDER BY (score * 1.5 + recency) DESC, memory_items.created_at DESC, memory_items.id DESC\n\t\tLIMIT ?\n\t`;\n\tparams.push(queryLimit);\n\n\tconst rows = store.db.prepare(sql).all(...params) as Record<string, unknown>[];\n\n\tconst results: MemoryResult[] = rows.map((row) => {\n\t\tconst metadata: Record<string, unknown> = {\n\t\t\t...fromJson(row.metadata_json as string | null),\n\t\t};\n\t\tfor (const key of [\n\t\t\t\"actor_id\",\n\t\t\t\"actor_display_name\",\n\t\t\t\"visibility\",\n\t\t\t\"workspace_id\",\n\t\t\t\"workspace_kind\",\n\t\t\t\"origin_device_id\",\n\t\t\t\"origin_source\",\n\t\t\t\"trust_state\",\n\t\t] as const) {\n\t\t\tconst value = row[key];\n\t\t\tif (value != null) metadata[key] = value;\n\t\t}\n\n\t\t// Propagate files_modified into metadata when stored as a JSON array\n\t\tif (row.files_modified && typeof row.files_modified === \"string\") {\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(row.files_modified as string);\n\t\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\t\tmetadata.files_modified ??= parsed;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// not valid JSON — ignore\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid: row.id as number,\n\t\t\tkind: row.kind as string,\n\t\t\ttitle: row.title as string,\n\t\t\tbody_text: row.body_text as string,\n\t\t\tconfidence: row.confidence as number,\n\t\t\tcreated_at: row.created_at as string,\n\t\t\tupdated_at: row.updated_at as string,\n\t\t\ttags_text: (row.tags_text as string) ?? \"\",\n\t\t\tscore: Number(row.score),\n\t\t\tsession_id: row.session_id as number,\n\t\t\tmetadata,\n\t\t};\n\t});\n\n\treturn rerankResults(store, results, effectiveLimit, filters);\n}\n\n/**\n * Return a chronological window of memories around an anchor.\n *\n * Finds an anchor by memoryId or query, then fetches neighbors in the\n * same session ordered by created_at.\n */\nexport function timeline(\n\tstore: StoreHandle,\n\tquery?: string | null,\n\tmemoryId?: number | null,\n\tdepthBefore = 3,\n\tdepthAfter = 3,\n\tfilters?: MemoryFilters | null,\n): TimelineItemResponse[] {\n\t// Find anchor: prefer explicit memoryId, fall back to search\n\tlet anchorRef: { id: number; session_id: number; created_at: string } | null = null;\n\tif (memoryId != null) {\n\t\tconst row = store.get(memoryId);\n\t\tif (row) {\n\t\t\tanchorRef = { id: row.id, session_id: row.session_id, created_at: row.created_at };\n\t\t}\n\t}\n\tif (anchorRef == null && query) {\n\t\tconst matches = search(store, query, 1, filters ?? undefined);\n\t\tif (matches.length > 0) {\n\t\t\tconst m = matches[0] as MemoryResult;\n\t\t\tanchorRef = {\n\t\t\t\tid: m.id,\n\t\t\t\tsession_id: m.session_id,\n\t\t\t\tcreated_at: m.created_at,\n\t\t\t};\n\t\t}\n\t}\n\tif (anchorRef == null) {\n\t\treturn [];\n\t}\n\n\treturn timelineAround(store, anchorRef, depthBefore, depthAfter, filters);\n}\n\n/** Fetch memories before/after an anchor within the same session. */\nfunction timelineAround(\n\tstore: StoreHandle,\n\tanchor: { id: number; session_id: number; created_at: string },\n\tdepthBefore: number,\n\tdepthAfter: number,\n\tfilters?: MemoryFilters | null,\n): TimelineItemResponse[] {\n\tconst anchorId = anchor.id;\n\tconst anchorCreatedAt = anchor.created_at;\n\tconst anchorSessionId = anchor.session_id;\n\n\tif (!anchorId || !anchorCreatedAt) {\n\t\treturn [];\n\t}\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\tconst whereParts = [\"memory_items.active = 1\", ...filterResult.clauses];\n\tconst baseParams = [...filterResult.params];\n\n\tif (anchorSessionId) {\n\t\twhereParts.push(\"memory_items.session_id = ?\");\n\t\tbaseParams.push(anchorSessionId);\n\t}\n\n\tconst whereClause = whereParts.join(\" AND \");\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\n\t// Before: older memories, descending (we'll reverse later)\n\tconst beforeRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${whereClause} AND memory_items.created_at < ?\n\t\t\t ORDER BY memory_items.created_at DESC\n\t\t\t LIMIT ?`,\n\t\t)\n\t\t.all(...baseParams, anchorCreatedAt, depthBefore) as MemoryItem[];\n\n\t// After: newer memories, ascending\n\tconst afterRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${whereClause} AND memory_items.created_at > ?\n\t\t\t ORDER BY memory_items.created_at ASC\n\t\t\t LIMIT ?`,\n\t\t)\n\t\t.all(...baseParams, anchorCreatedAt, depthAfter) as MemoryItem[];\n\n\t// Re-fetch anchor row to get full columns (anchor from search may be partial)\n\tconst d = getDrizzle(store.db);\n\tconst anchorRow = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(and(eq(schema.memoryItems.id, anchorId), eq(schema.memoryItems.active, 1)))\n\t\t.get() as MemoryItem | undefined;\n\n\t// Combine: reversed(before) + anchor + after\n\tconst rows: MemoryItem[] = [...beforeRows.reverse()];\n\tif (anchorRow) {\n\t\trows.push(anchorRow);\n\t}\n\trows.push(...afterRows);\n\n\treturn rows.map((row) => {\n\t\tconst { metadata_json, ...rest } = row;\n\t\treturn { ...rest, metadata_json: fromJson(metadata_json), linked_prompt: null };\n\t});\n}\n\n/**\n * Deduplicate an array of IDs, preserving order. Returns valid int IDs\n * and a list of values that could not be parsed as integers.\n */\nexport function dedupeOrderedIds(ids: unknown[]): { ordered: number[]; invalid: string[] } {\n\tconst seen = new Set<number>();\n\tconst ordered: number[] = [];\n\tconst invalid: string[] = [];\n\n\tfor (const rawId of ids) {\n\t\tconst parsed = parsePositiveMemoryId(rawId);\n\t\tif (parsed == null) {\n\t\t\tinvalid.push(String(rawId));\n\t\t\tcontinue;\n\t\t}\n\t\tif (seen.has(parsed)) continue;\n\t\tseen.add(parsed);\n\t\tordered.push(parsed);\n\t}\n\n\treturn { ordered, invalid };\n}\n\n/** Build an explain payload for a single memory item. */\nfunction explainItem(\n\titem: MemoryResult,\n\tsource: string,\n\trank: number | null,\n\tqueryTokens: string[],\n\tprojectFilter: string | null | undefined,\n\tprojectValue: string | null | undefined,\n\tincludePackContext: boolean,\n\treferenceNow: Date,\n): ExplainItem {\n\tconst text = `${item.title} ${item.body_text} ${item.tags_text}`.toLowerCase();\n\tconst matchedTerms = queryTokens.filter((token) => text.includes(token));\n\n\tconst baseScore: number | null =\n\t\tsource === \"query\" || source === \"query+id_lookup\" ? item.score : null;\n\tconst recencyComponent = recencyScore(item.created_at, referenceNow);\n\tconst kindComponent = kindBonus(item.kind);\n\n\tlet totalScore: number | null = null;\n\tif (baseScore != null) {\n\t\ttotalScore = baseScore * 1.5 + recencyComponent + kindComponent;\n\t}\n\n\treturn {\n\t\tid: item.id,\n\t\tkind: item.kind,\n\t\ttitle: item.title,\n\t\tcreated_at: item.created_at,\n\t\tproject: projectValue ?? null,\n\t\tretrieval: {\n\t\t\tsource,\n\t\t\trank,\n\t\t},\n\t\tscore: {\n\t\t\ttotal: totalScore,\n\t\t\tcomponents: {\n\t\t\t\tbase: baseScore,\n\t\t\t\trecency: recencyComponent,\n\t\t\t\tkind_bonus: kindComponent,\n\t\t\t\tpersonal_bias: 0.0,\n\t\t\t\tsemantic_boost: null,\n\t\t\t},\n\t\t},\n\t\tmatches: {\n\t\t\tquery_terms: matchedTerms,\n\t\t\tproject_match: projectMatchesFilter(projectFilter, projectValue),\n\t\t},\n\t\tpack_context: includePackContext ? { included: null, section: null } : null,\n\t};\n}\n\nfunction loadItemsByIdsForExplain(\n\tstore: StoreHandle,\n\tids: number[],\n\tfilters: MemoryFilters,\n): {\n\titems: MemoryResult[];\n\tmissingNotFound: number[];\n\tmissingProjectMismatch: number[];\n\tmissingFilterMismatch: number[];\n} {\n\tif (ids.length === 0) {\n\t\treturn {\n\t\t\titems: [],\n\t\t\tmissingNotFound: [],\n\t\t\tmissingProjectMismatch: [],\n\t\t\tmissingFilterMismatch: [],\n\t\t};\n\t}\n\n\tconst d = getDrizzle(store.db);\n\tconst allRows = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(and(eq(schema.memoryItems.active, 1), inArray(schema.memoryItems.id, ids)))\n\t\t.all() as MemoryItem[];\n\tconst allFoundIds = new Set(allRows.map((item) => item.id));\n\n\t// Placeholders for the dynamic-filter raw SQL queries below\n\tconst placeholders = ids.map(() => \"?\").join(\", \");\n\n\tlet projectScopedRows = allRows;\n\tlet projectScopedIds = new Set(allFoundIds);\n\tif (filters.project) {\n\t\tconst projectFiltersOnly: MemoryFilters = { project: filters.project };\n\t\tconst projectFilterResult = buildFilterClausesWithContext(projectFiltersOnly, {\n\t\t\tactorId: store.actorId,\n\t\t\tdeviceId: store.deviceId,\n\t\t});\n\t\tif (projectFilterResult.clauses.length > 0) {\n\t\t\tconst projectJoin = projectFilterResult.joinSessions\n\t\t\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t\t\t: \"\";\n\t\t\tprojectScopedRows = store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT memory_items.*\n\t\t\t\t FROM memory_items\n\t\t\t\t ${projectJoin}\n\t\t\t\t WHERE memory_items.active = 1\n\t\t\t\t AND memory_items.id IN (${placeholders})\n\t\t\t\t AND ${projectFilterResult.clauses.join(\" AND \")}`,\n\t\t\t\t)\n\t\t\t\t.all(...ids, ...projectFilterResult.params) as MemoryItem[];\n\t\t\tprojectScopedIds = new Set(projectScopedRows.map((item) => item.id));\n\t\t}\n\t}\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\tconst scopedRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t FROM memory_items\n\t\t ${joinClause}\n\t\t WHERE ${[\"memory_items.active = 1\", `memory_items.id IN (${placeholders})`, ...filterResult.clauses].join(\" AND \")}`,\n\t\t)\n\t\t.all(...ids, ...filterResult.params) as MemoryItem[];\n\tconst scopedIds = new Set(scopedRows.map((item) => item.id));\n\n\tconst missingNotFound = ids.filter((memoryId) => !allFoundIds.has(memoryId));\n\tconst missingProjectMismatch = ids.filter(\n\t\t(memoryId) => allFoundIds.has(memoryId) && !projectScopedIds.has(memoryId),\n\t);\n\tconst missingFilterMismatch = ids.filter(\n\t\t(memoryId) => projectScopedIds.has(memoryId) && !scopedIds.has(memoryId),\n\t);\n\n\tconst items = scopedRows.map((row) => ({\n\t\tid: row.id,\n\t\tkind: row.kind,\n\t\ttitle: row.title,\n\t\tbody_text: row.body_text,\n\t\tconfidence: row.confidence ?? 0,\n\t\tcreated_at: row.created_at,\n\t\tupdated_at: row.updated_at,\n\t\ttags_text: row.tags_text ?? \"\",\n\t\tscore: 0,\n\t\tsession_id: row.session_id,\n\t\tmetadata: fromJson(row.metadata_json),\n\t}));\n\n\treturn { items, missingNotFound, missingProjectMismatch, missingFilterMismatch };\n}\n\nfunction loadSessionProjects(\n\tstore: StoreHandle,\n\tsessionIds: Set<number>,\n): Map<number, string | null> {\n\tif (sessionIds.size === 0) return new Map();\n\tconst orderedIds = [...sessionIds].sort((a, b) => a - b);\n\tconst d = getDrizzle(store.db);\n\tconst rows = d\n\t\t.select({ id: schema.sessions.id, project: schema.sessions.project })\n\t\t.from(schema.sessions)\n\t\t.where(inArray(schema.sessions.id, orderedIds))\n\t\t.all();\n\treturn new Map(rows.map((row) => [row.id, row.project]));\n}\n\n/**\n * Explain search results with scoring breakdown.\n *\n * Accepts a query and/or explicit IDs, merges results, and returns a\n * detailed scoring payload for each item including retrieval source,\n * score components, and term matches.\n */\nexport function explain(\n\tstore: StoreHandle,\n\tquery?: string | null,\n\tids?: unknown[] | null,\n\tlimit = 10,\n\tfilters?: MemoryFilters | null,\n\toptions?: ExplainOptions,\n): ExplainResponse {\n\tconst includePackContext = options?.includePackContext ?? false;\n\tconst normalizedQuery = (query ?? \"\").trim();\n\tconst { ordered: orderedIds, invalid: invalidIds } = dedupeOrderedIds(ids ?? []);\n\n\tconst errors: ExplainError[] = [];\n\tif (invalidIds.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"INVALID_ARGUMENT\",\n\t\t\tfield: \"ids\",\n\t\t\tmessage: \"some ids are not valid integers\",\n\t\t\tids: invalidIds,\n\t\t});\n\t}\n\n\t// Require at least one of query or ids\n\tif (!normalizedQuery && orderedIds.length === 0) {\n\t\terrors.push({\n\t\t\tcode: \"INVALID_ARGUMENT\",\n\t\t\tfield: \"query\",\n\t\t\tmessage: \"at least one of query or ids is required\",\n\t\t});\n\t\treturn {\n\t\t\titems: [],\n\t\t\tmissing_ids: [],\n\t\t\terrors,\n\t\t\tmetadata: {\n\t\t\t\tquery: null,\n\t\t\t\tproject: null,\n\t\t\t\trequested_ids_count: orderedIds.length,\n\t\t\t\treturned_items_count: 0,\n\t\t\t\tinclude_pack_context: includePackContext,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Query-based results\n\tlet queryResults: MemoryResult[] = [];\n\tif (normalizedQuery) {\n\t\tqueryResults = search(\n\t\t\tstore,\n\t\t\tnormalizedQuery,\n\t\t\tMath.max(1, Math.trunc(limit)),\n\t\t\tfilters ?? undefined,\n\t\t);\n\t}\n\n\t// Build rank map for query results\n\tconst queryRank = new Map<number, number>();\n\tfor (let i = 0; i < queryResults.length; i++) {\n\t\tqueryRank.set((queryResults[i] as MemoryResult).id, i + 1);\n\t}\n\n\tconst {\n\t\titems: idRows,\n\t\tmissingNotFound,\n\t\tmissingProjectMismatch,\n\t\tmissingFilterMismatch,\n\t} = loadItemsByIdsForExplain(store, orderedIds, filters ?? {});\n\tconst idLookup = new Map(idRows.map((item) => [item.id, item]));\n\n\t// Merge: query results first, then id-lookup results not already seen\n\tconst explicitIdSet = new Set(orderedIds);\n\tconst selectedIds = new Set<number>();\n\tconst orderedItems: Array<{ item: MemoryResult; source: string; rank: number | null }> = [];\n\n\tfor (const item of queryResults) {\n\t\tselectedIds.add(item.id);\n\t\tconst source = explicitIdSet.has(item.id) ? \"query+id_lookup\" : \"query\";\n\t\torderedItems.push({ item, source, rank: queryRank.get(item.id) ?? null });\n\t}\n\n\tfor (const memId of orderedIds) {\n\t\tif (selectedIds.has(memId)) continue;\n\t\tconst item = idLookup.get(memId);\n\t\tif (!item) continue;\n\t\torderedItems.push({ item, source: \"id_lookup\", rank: null });\n\t\tselectedIds.add(memId);\n\t}\n\n\t// Tokenize query for term matching\n\tconst queryTokens = normalizedQuery\n\t\t? (normalizedQuery.match(/[A-Za-z0-9_]+/g) ?? []).map((t) => t.toLowerCase())\n\t\t: [];\n\n\tconst sessionProjects = loadSessionProjects(\n\t\tstore,\n\t\tnew Set(orderedItems.map(({ item }) => item.session_id).filter((sessionId) => sessionId > 0)),\n\t);\n\tconst referenceNow = new Date();\n\tconst itemsPayload = orderedItems.map(({ item, source, rank }) =>\n\t\texplainItem(\n\t\t\titem,\n\t\t\tsource,\n\t\t\trank,\n\t\t\tqueryTokens,\n\t\t\tfilters?.project ?? null,\n\t\t\tsessionProjects.get(item.session_id) ?? null,\n\t\t\tincludePackContext,\n\t\t\treferenceNow,\n\t\t),\n\t);\n\n\t// Collect all missing IDs (requested but not returned)\n\tconst missingIds = orderedIds.filter((id) => !selectedIds.has(id));\n\n\tif (missingNotFound.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tfield: \"ids\",\n\t\t\tmessage: \"some requested ids were not found\",\n\t\t\tids: missingNotFound,\n\t\t});\n\t}\n\tif (missingProjectMismatch.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"PROJECT_MISMATCH\",\n\t\t\tfield: \"project\",\n\t\t\tmessage: \"some requested ids are outside the requested project scope\",\n\t\t\tids: missingProjectMismatch,\n\t\t});\n\t}\n\tif (missingFilterMismatch.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"FILTER_MISMATCH\",\n\t\t\tfield: \"filters\",\n\t\t\tmessage: \"some requested ids do not match the provided filters\",\n\t\t\tids: missingFilterMismatch,\n\t\t});\n\t}\n\n\treturn {\n\t\titems: itemsPayload,\n\t\tmissing_ids: missingIds,\n\t\terrors,\n\t\tmetadata: {\n\t\t\tquery: normalizedQuery || null,\n\t\t\tproject: filters?.project ?? null,\n\t\t\trequested_ids_count: orderedIds.length,\n\t\t\treturned_items_count: itemsPayload.length,\n\t\t\tinclude_pack_context: includePackContext,\n\t\t},\n\t};\n}\n","/**\n * Memory pack builder — port of codemem/store/packs.py.\n *\n * Builds a formatted \"memory pack\" from search results, organized into\n * sections (summary, timeline, observations) with token budgeting.\n *\n * Ported: exact dedup, tag-overlap sorting, summary/observation fallback,\n * support_count, separate section dedup, semantic candidate merging.\n *\n * Semantic candidate merging is supported via `buildMemoryPackAsync` or\n * by passing pre-computed semantic results to `buildMemoryPack`.\n *\n * NOT ported: fuzzy search, pack delta\n * tracking, discovery-token work estimation.\n */\n\nimport type { Database } from \"./db.js\";\nimport { projectBasename } from \"./project.js\";\nimport type { StoreHandle } from \"./search.js\";\nimport { rerankResults, search, timeline } from \"./search.js\";\nimport type {\n\tMemoryFilters,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tPackItem,\n\tPackResponse,\n\tTimelineItemResponse,\n} from \"./types.js\";\nimport { semanticSearch } from \"./vectors.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Observation kind priority — higher-signal kinds sort first. */\nconst OBSERVATION_KIND_PRIORITY: Record<string, number> = {\n\tdecision: 0,\n\tfeature: 1,\n\tbugfix: 2,\n\trefactor: 3,\n\tchange: 4,\n\tdiscovery: 5,\n\texploration: 6,\n\tnote: 7,\n};\n\nconst TASK_RECENCY_DAYS = 365;\n\nconst TASK_HINT_QUERY =\n\t\"todo todos task tasks pending follow up follow-up next resume continue backlog pick up pick-up\";\n\nconst RECALL_HINT_QUERY = \"session summary recap remember last time previous work\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Rough token estimate: ~4 chars per token. */\nexport function estimateTokens(text: string): number {\n\treturn Math.ceil(text.length / 4);\n}\n\n/** Format a single memory item as a pack line. */\nfunction formatItem(item: MemoryResult): string {\n\tconst parts = [`[${item.id}]`, `(${item.kind})`, item.title];\n\tif (item.body_text) {\n\t\tparts.push(\"-\", item.body_text);\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Build a formatted section with header and items. */\nfunction formatSection(header: string, items: MemoryResult[]): string {\n\tconst heading = `## ${header}`;\n\tif (items.length === 0) return `${heading}\\n`;\n\treturn [heading, ...items.map(formatItem)].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Pack item shape (what goes into items array)\n// ---------------------------------------------------------------------------\n\nfunction toPackItem(result: MemoryResult, dedupeState?: DedupeState): PackItem {\n\tconst dupes = dedupeState?.duplicateIds.get(result.id);\n\tconst item: PackItem = {\n\t\tid: result.id,\n\t\tkind: result.kind,\n\t\ttitle: result.title,\n\t\tbody: result.body_text,\n\t\tconfidence: result.confidence,\n\t\ttags: result.tags_text,\n\t\tmetadata: result.metadata,\n\t};\n\tif (dupes && dupes.size > 0) {\n\t\titem.support_count = 1 + dupes.size;\n\t\titem.duplicate_ids = [...dupes].sort((a, b) => a - b);\n\t}\n\treturn item;\n}\n\n// ---------------------------------------------------------------------------\n// Exact dedup (ports Python's _collapse_exact_duplicates)\n// ---------------------------------------------------------------------------\n\n/** Normalize text for dedup comparison: lowercase, trim, collapse whitespace. */\nfunction normalizeDedupe(text: string): string {\n\treturn text.trim().toLowerCase().split(/\\s+/).join(\" \");\n}\n\n/**\n * Build a collision-free dedup key for non-summary items.\n * Uses length-prefixed fields so pipe characters in content can't\n * cause collisions between distinct (kind, title, body) tuples.\n */\nfunction exactDedupeKey(item: MemoryResult): string | null {\n\tif (item.kind === \"session_summary\") return null;\n\tconst title = normalizeDedupe(item.title);\n\tconst body = normalizeDedupe(item.body_text);\n\tif (!title && !body) return null;\n\treturn `${item.kind.length}:${item.kind}|${title.length}:${title}|${body.length}:${body}`;\n}\n\ninterface DedupeState {\n\tcanonicalByKey: Map<string, number>;\n\tduplicateIds: Map<number, Set<number>>;\n}\n\n/**\n * Collapse exact duplicates: same kind+title+body → keep first (canonical).\n * Tracks duplicate IDs so support_count can report how many were collapsed.\n */\nfunction collapseExactDuplicates(items: MemoryResult[], state: DedupeState): MemoryResult[] {\n\tconst collapsed: MemoryResult[] = [];\n\tfor (const item of items) {\n\t\tconst key = exactDedupeKey(item);\n\t\tif (key === null) {\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tconst canonicalId = state.canonicalByKey.get(key);\n\t\tif (canonicalId === undefined) {\n\t\t\tstate.canonicalByKey.set(key, item.id);\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tif (canonicalId === item.id) {\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\t// Track as duplicate of the canonical\n\t\tconst existing = state.duplicateIds.get(canonicalId);\n\t\tif (existing) existing.add(item.id);\n\t\telse state.duplicateIds.set(canonicalId, new Set([item.id]));\n\t}\n\treturn collapsed;\n}\n\n// ---------------------------------------------------------------------------\n// Tag-overlap sorting (ports Python's _sort_by_tag_overlap)\n// ---------------------------------------------------------------------------\n\n/** Sort items by tag overlap with the query, then by recency. */\nfunction sortByTagOverlap(items: MemoryResult[], query: string): MemoryResult[] {\n\tconst queryTokens = new Set((query.toLowerCase().match(/[a-z0-9_]+/g) ?? []).filter(Boolean));\n\tif (queryTokens.size === 0) return items;\n\n\treturn [...items].sort((a, b) => {\n\t\tconst aOverlap = countOverlap(a.tags_text, queryTokens);\n\t\tconst bOverlap = countOverlap(b.tags_text, queryTokens);\n\t\tif (bOverlap !== aOverlap) return bOverlap - aOverlap;\n\t\t// Tiebreak by recency (newest first)\n\t\treturn (b.created_at ?? \"\").localeCompare(a.created_at ?? \"\");\n\t});\n}\n\nfunction countOverlap(tags: string, tokens: Set<string>): number {\n\tconst tagSet = new Set(tags.split(/\\s+/).filter(Boolean));\n\tlet count = 0;\n\tfor (const t of tokens) {\n\t\tif (tagSet.has(t)) count++;\n\t}\n\treturn count;\n}\n\nfunction queryLooksLikeTasks(query: string): boolean {\n\tconst lowered = query.toLowerCase();\n\tfor (const token of [\n\t\t\"todo\",\n\t\t\"todos\",\n\t\t\"pending\",\n\t\t\"task\",\n\t\t\"tasks\",\n\t\t\"next\",\n\t\t\"resume\",\n\t\t\"continue\",\n\t\t\"backlog\",\n\t]) {\n\t\tif (lowered.includes(token)) return true;\n\t}\n\tfor (const phrase of [\n\t\t\"follow up\",\n\t\t\"follow-up\",\n\t\t\"followups\",\n\t\t\"pick up\",\n\t\t\"pick-up\",\n\t\t\"left off\",\n\t\t\"where we left off\",\n\t\t\"work on next\",\n\t\t\"what's next\",\n\t\t\"what was next\",\n\t]) {\n\t\tif (lowered.includes(phrase)) return true;\n\t}\n\treturn false;\n}\n\nfunction queryLooksLikeRecall(query: string): boolean {\n\tconst lowered = query.toLowerCase();\n\tfor (const token of [\"remember\", \"remind\", \"recall\", \"recap\", \"summary\", \"summarize\"]) {\n\t\tif (lowered.includes(token)) return true;\n\t}\n\tfor (const phrase of [\n\t\t\"what did we do\",\n\t\t\"what did we work on\",\n\t\t\"what did we decide\",\n\t\t\"what happened\",\n\t\t\"last time\",\n\t\t\"previous session\",\n\t\t\"previous work\",\n\t\t\"where were we\",\n\t\t\"catch me up\",\n\t\t\"catch up\",\n\t]) {\n\t\tif (lowered.includes(phrase)) return true;\n\t}\n\treturn false;\n}\n\nfunction toMemoryResult(row: MemoryItemResponse | TimelineItemResponse): MemoryResult {\n\treturn {\n\t\tid: row.id,\n\t\tkind: row.kind,\n\t\ttitle: row.title,\n\t\tbody_text: row.body_text,\n\t\tconfidence: row.confidence ?? 0,\n\t\tcreated_at: row.created_at,\n\t\tupdated_at: row.updated_at,\n\t\ttags_text: row.tags_text ?? \"\",\n\t\tscore: 0,\n\t\tsession_id: row.session_id,\n\t\tmetadata: row.metadata_json,\n\t};\n}\n\nfunction parseCreatedAt(value: string): number {\n\tconst parsed = Date.parse(value);\n\tif (Number.isNaN(parsed)) return Number.NEGATIVE_INFINITY;\n\treturn parsed;\n}\n\nfunction filterRecentResults(results: MemoryResult[], days: number): MemoryResult[] {\n\tconst cutoff = Date.now() - days * 86_400_000;\n\treturn results.filter((item) => parseCreatedAt(item.created_at) >= cutoff);\n}\n\nfunction prioritizeTaskResults(results: MemoryResult[], limit: number): MemoryResult[] {\n\tconst ordered = [...results].sort((a, b) =>\n\t\t(b.created_at ?? \"\").localeCompare(a.created_at ?? \"\"),\n\t);\n\tordered.sort((a, b) => {\n\t\tconst rank = (kind: string): number => {\n\t\t\tif (kind === \"note\") return 0;\n\t\t\tif (kind === \"decision\") return 1;\n\t\t\tif (kind === \"observation\") return 2;\n\t\t\treturn 3;\n\t\t};\n\t\treturn rank(a.kind) - rank(b.kind);\n\t});\n\treturn ordered.slice(0, limit);\n}\n\nfunction prioritizeRecallResults(results: MemoryResult[], limit: number): MemoryResult[] {\n\tconst ordered = [...results].sort((a, b) =>\n\t\t(b.created_at ?? \"\").localeCompare(a.created_at ?? \"\"),\n\t);\n\tordered.sort((a, b) => {\n\t\tconst rank = (kind: string): number => {\n\t\t\tif (kind === \"session_summary\") return 0;\n\t\t\tif (kind === \"decision\") return 1;\n\t\t\tif (kind === \"note\") return 2;\n\t\t\tif (kind === \"observation\") return 3;\n\t\t\tif (kind === \"entities\") return 4;\n\t\t\treturn 5;\n\t\t};\n\t\treturn rank(a.kind) - rank(b.kind);\n\t});\n\treturn ordered.slice(0, limit);\n}\n\nfunction taskFallbackRecent(\n\tstore: StoreHandle,\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst expandedLimit = Math.max(limit * 3, limit);\n\tconst recentRows = store.recent(expandedLimit, filters ?? null);\n\treturn prioritizeTaskResults(recentRows.map(toMemoryResult), limit);\n}\n\nfunction recallFallbackRecent(\n\tstore: StoreHandle,\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst summaryFilters = { ...(filters ?? {}), kind: \"session_summary\" };\n\tconst summaries = store.recent(limit, summaryFilters).map(toMemoryResult);\n\tif (summaries.length >= limit) return summaries.slice(0, limit);\n\n\tconst expandedLimit = Math.max(limit * 3, limit);\n\tconst recentAll = store.recent(expandedLimit, filters ?? null).map(toMemoryResult);\n\tconst summaryIds = new Set(summaries.map((item) => item.id));\n\tconst remainder = recentAll.filter((item) => !summaryIds.has(item.id));\n\tconst prioritized = prioritizeTaskResults(remainder, limit - summaries.length);\n\treturn [...summaries, ...prioritized];\n}\nfunction parseNonNegativeInt(value: unknown): number | null {\n\tif (value == null || typeof value === \"boolean\") return null;\n\tconst parsed = Number(value);\n\tif (!Number.isFinite(parsed)) return null;\n\tconst intValue = Math.trunc(parsed);\n\tif (intValue < 0) return null;\n\treturn intValue;\n}\n\nfunction dedupePositiveIds(values: unknown[]): number[] {\n\tconst deduped: number[] = [];\n\tconst seen = new Set<number>();\n\tfor (const raw of values) {\n\t\tconst parsed = parseNonNegativeInt(raw);\n\t\tif (parsed == null || parsed <= 0 || seen.has(parsed)) continue;\n\t\tseen.add(parsed);\n\t\tdeduped.push(parsed);\n\t}\n\treturn deduped;\n}\n\nfunction coercePackItemIds(value: unknown): { ids: number[]; valid: boolean } {\n\tif (!Array.isArray(value)) return { ids: [], valid: false };\n\tfor (const raw of value) {\n\t\tif (raw == null || typeof raw === \"boolean\") return { ids: [], valid: false };\n\t}\n\treturn { ids: dedupePositiveIds(value), valid: true };\n}\n\nfunction parseMetadataObject(value: unknown): Record<string, unknown> {\n\tif (!value) return {};\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(value) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t\treturn {};\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\treturn value as Record<string, unknown>;\n\t}\n\treturn {};\n}\n\nfunction getPackDeltaBaseline(\n\tstore: StoreHandle,\n\tproject: string | null,\n): { previousPackIds: number[] | null; previousPackTokens: number | null } {\n\tconst projectBase = project ? projectBasename(project) : null;\n\tconst metaProjectExpr =\n\t\t\"CASE WHEN json_valid(metadata_json) = 1 THEN json_extract(metadata_json, '$.project') ELSE NULL END\";\n\tconst rows = project\n\t\t? (store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT metadata_json, tokens_read\n\t\t\t\t\t FROM usage_events\n\t\t\t\t\t WHERE event = 'pack'\n\t\t\t\t\t AND (${metaProjectExpr} = ? OR ${metaProjectExpr} = ?)\n\t\t\t\t\t ORDER BY created_at DESC\n\t\t\t\t\t LIMIT 25`,\n\t\t\t\t)\n\t\t\t\t.all(project, projectBase ?? project) as Array<{\n\t\t\t\tmetadata_json: string | null;\n\t\t\t\ttokens_read: number | null;\n\t\t\t}>)\n\t\t: (store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT metadata_json, tokens_read\n\t\t\t\t\t FROM usage_events\n\t\t\t\t\t WHERE event = 'pack'\n\t\t\t\t\t ORDER BY created_at DESC\n\t\t\t\t\t LIMIT 25`,\n\t\t\t\t)\n\t\t\t\t.all() as Array<{ metadata_json: string | null; tokens_read: number | null }>);\n\n\tfor (const row of rows) {\n\t\tconst metadata = parseMetadataObject(row.metadata_json);\n\t\tif (project != null) {\n\t\t\tconst rowProject = typeof metadata.project === \"string\" ? metadata.project : null;\n\t\t\tif (rowProject !== project && rowProject !== projectBase) continue;\n\t\t}\n\t\tif (!(\"pack_item_ids\" in metadata)) continue;\n\n\t\tconst { ids, valid } = coercePackItemIds(metadata.pack_item_ids);\n\t\tif (!valid) continue;\n\n\t\tconst previousTokens =\n\t\t\tparseNonNegativeInt(metadata.pack_tokens) ?? parseNonNegativeInt(row.tokens_read);\n\t\tif (previousTokens == null) continue;\n\n\t\treturn { previousPackIds: ids, previousPackTokens: previousTokens };\n\t}\n\n\treturn { previousPackIds: null, previousPackTokens: null };\n}\n\nfunction resolveUsageSessionId(store: StoreHandle, project: string | null): number | null {\n\tif (!project) return null;\n\tconst projectBase = projectBasename(project);\n\tconst row = store.db\n\t\t.prepare(\n\t\t\t`SELECT id\n\t\t\t FROM sessions\n\t\t\t WHERE project = ? OR project = ?\n\t\t\t ORDER BY started_at DESC, id DESC\n\t\t\t LIMIT 1`,\n\t\t)\n\t\t.get(project, projectBase) as { id: number } | undefined;\n\treturn row?.id ?? null;\n}\n\nfunction estimateWorkTokens(item: MemoryResult): number {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst known = parseNonNegativeInt(metadata.discovery_tokens);\n\tif (known != null) return known;\n\treturn Math.max(2000, estimateTokens(`${item.title} ${item.body_text}`.trim()));\n}\n\nfunction discoveryGroup(item: MemoryResult): string {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst group = metadata.discovery_group;\n\tif (typeof group === \"string\" && group.trim().length > 0) return group.trim();\n\treturn `memory:${item.id}`;\n}\n\nfunction avoidedWorkTokens(item: MemoryResult): { tokens: number; source: string } {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst tokens = parseNonNegativeInt(metadata.discovery_tokens);\n\tif (tokens != null && tokens > 0) {\n\t\tconst source =\n\t\t\ttypeof metadata.discovery_source === \"string\" && metadata.discovery_source\n\t\t\t\t? metadata.discovery_source\n\t\t\t\t: \"known\";\n\t\treturn { tokens, source };\n\t}\n\treturn { tokens: 0, source: \"unknown\" };\n}\n\nfunction workSource(item: MemoryResult): \"usage\" | \"estimate\" {\n\tconst metadata = parseMetadataObject(item.metadata);\n\treturn metadata.discovery_source === \"usage\" ? \"usage\" : \"estimate\";\n}\n\nfunction recordPackUsage(store: StoreHandle, metrics: Record<string, unknown>): void {\n\tconst now = new Date().toISOString();\n\tconst tokensRead = parseNonNegativeInt(metrics.pack_tokens) ?? 0;\n\tconst tokensSaved = parseNonNegativeInt(metrics.tokens_saved) ?? 0;\n\tconst project = typeof metrics.project === \"string\" ? metrics.project : null;\n\tconst sessionId = resolveUsageSessionId(store, project);\n\ttry {\n\t\tstore.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO usage_events(session_id, event, tokens_read, tokens_written, tokens_saved, created_at, metadata_json)\n\t\t\t\t VALUES (?, 'pack', ?, 0, ?, ?, ?)`,\n\t\t\t)\n\t\t\t.run(sessionId, tokensRead, tokensSaved, now, JSON.stringify(metrics));\n\t} catch {\n\t\t// Non-fatal for pack building path\n\t}\n}\n// ---------------------------------------------------------------------------\n// buildMemoryPack\n// ---------------------------------------------------------------------------\n\n/**\n * Build a memory pack: a formatted, categorized summary of memories\n * matching a given context string.\n *\n * Flow:\n * 1. Search for memories matching `context`\n * 2. Separate into summary / timeline / observations sections\n * 3. Fall back to recent() if search returns nothing\n * 4. Apply token budget (truncate items if budget exceeded)\n * 5. Format sections into pack_text\n */\n/**\n * Merge FTS and semantic results by ID, keeping the higher score for dupes.\n * Matches Python's always-merge behavior when semantic results are available.\n */\nfunction mergeResults(\n\tstore: StoreHandle,\n\tftsResults: MemoryResult[],\n\tsemanticResults: MemoryResult[],\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): { merged: MemoryResult[]; ftsCount: number; semanticCount: number } {\n\tconst seen = new Map<number, MemoryResult>();\n\tfor (const r of ftsResults) {\n\t\tconst existing = seen.get(r.id);\n\t\tif (!existing || r.score > existing.score) seen.set(r.id, r);\n\t}\n\tlet semanticCount = 0;\n\tfor (const r of semanticResults) {\n\t\tif (!seen.has(r.id)) semanticCount++;\n\t\tconst existing = seen.get(r.id);\n\t\tif (!existing || r.score > existing.score) seen.set(r.id, r);\n\t}\n\tconst merged = rerankResults(store, [...seen.values()], limit, filters);\n\treturn { merged, ftsCount: ftsResults.length, semanticCount };\n}\n\nexport function buildMemoryPack(\n\tstore: StoreHandle,\n\tcontext: string,\n\tlimit = 10,\n\ttokenBudget: number | null = null,\n\tfilters?: MemoryFilters,\n\tsemanticResults?: MemoryResult[],\n): PackResponse {\n\tconst effectiveLimit = Math.max(1, Math.trunc(limit));\n\tlet fallbackUsed = false;\n\tlet ftsCount = 0;\n\tlet semanticCount = 0;\n\tlet results: MemoryResult[];\n\tconst taskMode = queryLooksLikeTasks(context);\n\tconst recallMode = !taskMode && queryLooksLikeRecall(context);\n\n\tif (taskMode) {\n\t\tconst taskQuery = `${context} ${TASK_HINT_QUERY}`.trim();\n\t\tlet taskResults = search(store, taskQuery, effectiveLimit, filters);\n\t\tftsCount = taskResults.length;\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, taskResults, semanticResults, effectiveLimit, filters);\n\t\t\ttaskResults = merge.merged;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t}\n\t\tif (taskResults.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = taskFallbackRecent(store, effectiveLimit, filters);\n\t\t} else {\n\t\t\tconst recentTaskResults = filterRecentResults(taskResults, TASK_RECENCY_DAYS);\n\t\t\tresults = prioritizeTaskResults(\n\t\t\t\trecentTaskResults.length > 0 ? recentTaskResults : taskResults,\n\t\t\t\teffectiveLimit,\n\t\t\t);\n\t\t}\n\t} else if (recallMode) {\n\t\tconst recallQuery = context.trim().length > 0 ? context : RECALL_HINT_QUERY;\n\t\tlet recallResults = search(store, recallQuery, effectiveLimit, filters);\n\t\tftsCount = recallResults.length;\n\t\tif (recallResults.length === 0) {\n\t\t\tconst recallFilters = { ...(filters ?? {}), kind: \"session_summary\" };\n\t\t\trecallResults = search(store, RECALL_HINT_QUERY, effectiveLimit, recallFilters);\n\t\t\tftsCount = recallResults.length;\n\t\t}\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, recallResults, semanticResults, effectiveLimit, filters);\n\t\t\trecallResults = merge.merged;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t}\n\t\tresults = prioritizeRecallResults(recallResults, effectiveLimit);\n\t\tif (results.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = recallFallbackRecent(store, effectiveLimit, filters);\n\t\t}\n\t\tconst anchorId = results[0]?.id;\n\t\tif (anchorId != null) {\n\t\t\tconst depthBefore = Math.max(0, Math.floor(effectiveLimit / 2));\n\t\t\tconst depthAfter = Math.max(0, effectiveLimit - depthBefore - 1);\n\t\t\tconst timelineRows = timeline(\n\t\t\t\tstore,\n\t\t\t\tundefined,\n\t\t\t\tanchorId,\n\t\t\t\tdepthBefore,\n\t\t\t\tdepthAfter,\n\t\t\t\tfilters ?? null,\n\t\t\t);\n\t\t\tif (timelineRows.length > 0) {\n\t\t\t\tresults = timelineRows.map(toMemoryResult);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tconst ftsResults = search(store, context, effectiveLimit, filters);\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, ftsResults, semanticResults, effectiveLimit, filters);\n\t\t\tresults = merge.merged;\n\t\t\tftsCount = merge.ftsCount;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t} else {\n\t\t\tresults = ftsResults;\n\t\t\tftsCount = results.length;\n\t\t}\n\t\tif (results.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = store.recent(effectiveLimit, filters ?? null).map(toMemoryResult);\n\t\t}\n\t}\n\n\t// Step 2: categorize results\n\n\t// Summary: prefer search match, fall back to most recent session_summary\n\tlet summaryItems = results.filter((r) => r.kind === \"session_summary\").slice(0, 1);\n\tif (summaryItems.length === 0) {\n\t\tconst recentSummary = store.recent(1, { ...(filters ?? {}), kind: \"session_summary\" });\n\t\tif (recentSummary.length > 0) {\n\t\t\tconst s = recentSummary[0]!;\n\t\t\tsummaryItems = [\n\t\t\t\t{\n\t\t\t\t\tid: s.id,\n\t\t\t\t\tkind: s.kind,\n\t\t\t\t\ttitle: s.title,\n\t\t\t\t\tbody_text: s.body_text,\n\t\t\t\t\tconfidence: s.confidence ?? 0,\n\t\t\t\t\tcreated_at: s.created_at,\n\t\t\t\t\tupdated_at: s.updated_at,\n\t\t\t\t\ttags_text: s.tags_text ?? \"\",\n\t\t\t\t\tscore: 0,\n\t\t\t\t\tsession_id: s.session_id,\n\t\t\t\t\tmetadata: s.metadata_json,\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\t}\n\n\tlet timelineItems = results.filter((r) => r.kind !== \"session_summary\").slice(0, 3);\n\tconst timelineIds = new Set(timelineItems.map((r) => r.id));\n\n\t// Observations: from search results, then fall back to recent by observation kinds\n\tconst OBSERVATION_KINDS = Object.keys(OBSERVATION_KIND_PRIORITY);\n\tlet observationItems = [...results]\n\t\t.filter((r) => r.kind !== \"session_summary\" && !timelineIds.has(r.id))\n\t\t.sort((a, b) => {\n\t\t\tconst pa = OBSERVATION_KIND_PRIORITY[a.kind] ?? 99;\n\t\t\tconst pb = OBSERVATION_KIND_PRIORITY[b.kind] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\tif (recallMode && observationItems.length === 0) {\n\t\tobservationItems = results.filter((r) => r.kind !== \"session_summary\");\n\t}\n\n\tif (observationItems.length === 0) {\n\t\tconst recentObs = store.recentByKinds(\n\t\t\tOBSERVATION_KINDS,\n\t\t\tMath.max(effectiveLimit * 3, 10),\n\t\t\tfilters ?? null,\n\t\t);\n\t\tobservationItems = recentObs.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tkind: row.kind,\n\t\t\ttitle: row.title,\n\t\t\tbody_text: row.body_text,\n\t\t\tconfidence: row.confidence ?? 0,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\ttags_text: row.tags_text ?? \"\",\n\t\t\tscore: 0,\n\t\t\tsession_id: row.session_id,\n\t\t\tmetadata: row.metadata_json,\n\t\t}));\n\t}\n\n\tif (observationItems.length === 0) {\n\t\tobservationItems = [...timelineItems];\n\t}\n\n\t// Sort observations by tag overlap with context, then by kind priority\n\tobservationItems = sortByTagOverlap(observationItems, context);\n\n\t// Exact dedup across all sections\n\tconst dedupeState: DedupeState = {\n\t\tcanonicalByKey: new Map(),\n\t\tduplicateIds: new Map(),\n\t};\n\tsummaryItems = collapseExactDuplicates(summaryItems, dedupeState);\n\ttimelineItems = collapseExactDuplicates(timelineItems, dedupeState);\n\tobservationItems = collapseExactDuplicates(observationItems, dedupeState);\n\n\t// Step 4: apply token budget\n\tlet budgetedSummary = summaryItems;\n\tlet budgetedTimeline = timelineItems;\n\tlet budgetedObservations = observationItems;\n\n\tif (tokenBudget != null && tokenBudget > 0) {\n\t\tlet tokensUsed = 0;\n\n\t\tbudgetedSummary = [];\n\t\tfor (const item of summaryItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedSummary.push(item);\n\t\t}\n\n\t\tbudgetedTimeline = [];\n\t\tfor (const item of timelineItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedTimeline.push(item);\n\t\t}\n\n\t\tbudgetedObservations = [];\n\t\tfor (const item of observationItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedObservations.push(item);\n\t\t}\n\t}\n\n\t// Step 5: format sections\n\tconst sections = [\n\t\tformatSection(\"Summary\", budgetedSummary),\n\t\tformatSection(\"Timeline\", budgetedTimeline),\n\t\tformatSection(\"Observations\", budgetedObservations),\n\t];\n\n\tconst packText = sections.join(\"\\n\\n\");\n\tconst packTokens = estimateTokens(packText);\n\n\t// Collect all unique items across sections\n\tconst seenIds = new Set<number>();\n\tconst allItems: PackItem[] = [];\n\tconst selectedItems: MemoryResult[] = [];\n\tconst allItemIds: number[] = [];\n\tfor (const item of [...budgetedSummary, ...budgetedTimeline, ...budgetedObservations]) {\n\t\tif (seenIds.has(item.id)) continue;\n\t\tseenIds.add(item.id);\n\t\tselectedItems.push(item);\n\t\tallItems.push(toPackItem(item, dedupeState));\n\t\tallItemIds.push(item.id);\n\t}\n\n\tconst { previousPackIds, previousPackTokens } = getPackDeltaBaseline(\n\t\tstore,\n\t\tfilters?.project ?? null,\n\t);\n\tconst packDeltaAvailable = previousPackIds != null && previousPackTokens != null;\n\tconst previousSet = new Set(previousPackIds ?? []);\n\tconst currentSet = new Set(allItemIds);\n\tconst addedIds = packDeltaAvailable ? allItemIds.filter((id) => !previousSet.has(id)) : [];\n\tconst removedIds = packDeltaAvailable\n\t\t? (previousPackIds ?? []).filter((id) => !currentSet.has(id))\n\t\t: [];\n\tconst retainedIds = packDeltaAvailable ? allItemIds.filter((id) => previousSet.has(id)) : [];\n\tconst packTokenDelta = packDeltaAvailable ? packTokens - (previousPackTokens ?? 0) : 0;\n\n\tconst workTokens = selectedItems.reduce((sum, item) => sum + estimateWorkTokens(item), 0);\n\tconst groupedWork = new Map<string, number>();\n\tfor (const item of selectedItems) {\n\t\tconst key = discoveryGroup(item);\n\t\tconst estimate = estimateWorkTokens(item);\n\t\tconst existing = groupedWork.get(key) ?? 0;\n\t\tif (estimate > existing) groupedWork.set(key, estimate);\n\t}\n\tconst workTokensUnique = [...groupedWork.values()].reduce((sum, value) => sum + value, 0);\n\tconst tokensSaved = Math.max(0, workTokensUnique - packTokens);\n\n\tlet avoidedWorkTokensTotal = 0;\n\tlet avoidedKnownItems = 0;\n\tlet avoidedUnknownItems = 0;\n\tconst avoidedWorkSources: Record<string, number> = {};\n\tfor (const item of selectedItems) {\n\t\tconst avoided = avoidedWorkTokens(item);\n\t\tif (avoided.tokens > 0) {\n\t\t\tavoidedWorkTokensTotal += avoided.tokens;\n\t\t\tavoidedKnownItems += 1;\n\t\t\tavoidedWorkSources[avoided.source] = (avoidedWorkSources[avoided.source] ?? 0) + 1;\n\t\t} else {\n\t\t\tavoidedUnknownItems += 1;\n\t\t}\n\t}\n\tconst avoidedWorkSaved = Math.max(0, avoidedWorkTokensTotal - packTokens);\n\tconst avoidedWorkRatio =\n\t\tavoidedWorkTokensTotal > 0 ? avoidedWorkTokensTotal / Math.max(packTokens, 1) : null;\n\n\tconst workSources = selectedItems.map(workSource);\n\tconst workUsageItems = workSources.filter((source) => source === \"usage\").length;\n\tconst workEstimateItems = workSources.length - workUsageItems;\n\tconst workSourceLabel: \"estimate\" | \"usage\" | \"mixed\" =\n\t\tworkUsageItems > 0 && workEstimateItems > 0\n\t\t\t? \"mixed\"\n\t\t\t: workUsageItems > 0\n\t\t\t\t? \"usage\"\n\t\t\t\t: \"estimate\";\n\n\tconst compressionRatio = workTokensUnique > 0 ? packTokens / workTokensUnique : null;\n\tconst overheadTokens = workTokensUnique > 0 ? packTokens - workTokensUnique : null;\n\tconst fallbackLabel: \"recent\" | null = fallbackUsed ? \"recent\" : null;\n\tconst modeLabel: \"default\" | \"task\" | \"recall\" = taskMode\n\t\t? \"task\"\n\t\t: recallMode\n\t\t\t? \"recall\"\n\t\t\t: \"default\";\n\n\tconst metrics = {\n\t\ttotal_items: allItems.length,\n\t\tpack_tokens: packTokens,\n\t\tfallback_used: fallbackUsed,\n\t\tfallback: fallbackLabel,\n\t\tlimit: effectiveLimit,\n\t\ttoken_budget: tokenBudget,\n\t\tproject: filters?.project ?? null,\n\t\tpack_item_ids: allItemIds,\n\t\tmode: modeLabel,\n\t\tadded_ids: addedIds,\n\t\tremoved_ids: removedIds,\n\t\tretained_ids: retainedIds,\n\t\tpack_token_delta: packTokenDelta,\n\t\tpack_delta_available: packDeltaAvailable,\n\t\twork_tokens: workTokens,\n\t\twork_tokens_unique: workTokensUnique,\n\t\ttokens_saved: tokensSaved,\n\t\tcompression_ratio: compressionRatio,\n\t\toverhead_tokens: overheadTokens,\n\t\tavoided_work_tokens: avoidedWorkTokensTotal,\n\t\tavoided_work_saved: avoidedWorkSaved,\n\t\tavoided_work_ratio: avoidedWorkRatio,\n\t\tavoided_work_known_items: avoidedKnownItems,\n\t\tavoided_work_unknown_items: avoidedUnknownItems,\n\t\tavoided_work_sources: avoidedWorkSources,\n\t\twork_source: workSourceLabel,\n\t\twork_usage_items: workUsageItems,\n\t\twork_estimate_items: workEstimateItems,\n\t\tsavings_reliable:\n\t\t\tavoidedKnownItems + avoidedUnknownItems > 0 ? avoidedKnownItems >= avoidedUnknownItems : true,\n\t\tsources: { fts: ftsCount, semantic: semanticCount, fuzzy: 0 },\n\t};\n\n\trecordPackUsage(store, metrics);\n\n\treturn {\n\t\tcontext,\n\t\titems: allItems,\n\t\titem_ids: allItemIds,\n\t\tpack_text: packText,\n\t\tmetrics,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Async pack builder (with semantic search)\n// ---------------------------------------------------------------------------\n\n/**\n * Build a memory pack with semantic candidate merging.\n *\n * This is the async version that runs `semanticSearch` against the\n * sqlite-vec `memory_vectors` table, then merges those candidates\n * with FTS results via the sync `buildMemoryPack`.\n *\n * Callers that don't want/need async can still use the sync\n * `buildMemoryPack` directly — semantic candidates simply won't\n * be included.\n */\nexport async function buildMemoryPackAsync(\n\tstore: StoreHandle & { db: Database },\n\tcontext: string,\n\tlimit = 10,\n\ttokenBudget: number | null = null,\n\tfilters?: MemoryFilters,\n): Promise<PackResponse> {\n\t// Run semantic search (returns [] when embeddings unavailable)\n\tlet semResults: MemoryResult[] = [];\n\ttry {\n\t\tconst raw = await semanticSearch(store.db, context, limit, {\n\t\t\tproject: filters?.project,\n\t\t});\n\t\tsemResults = raw.map((r) => {\n\t\t\t// Parse metadata_json if present, matching FTS result shape\n\t\t\tlet metadata: Record<string, unknown> = {};\n\t\t\tif (r.metadata_json) {\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(r.metadata_json) as unknown;\n\t\t\t\t\tif (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\t\t\tmetadata = parsed as Record<string, unknown>;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid JSON metadata — use empty object\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tid: r.id,\n\t\t\t\tkind: r.kind,\n\t\t\t\ttitle: r.title,\n\t\t\t\tbody_text: r.body_text,\n\t\t\t\tconfidence: r.confidence,\n\t\t\t\tcreated_at: r.created_at,\n\t\t\t\tupdated_at: r.updated_at,\n\t\t\t\ttags_text: r.tags_text,\n\t\t\t\tscore: r.score,\n\t\t\t\tsession_id: r.session_id,\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t});\n\t} catch {\n\t\t// Semantic search failure is non-fatal — fall through to FTS-only\n\t}\n\n\treturn buildMemoryPack(store, context, limit, tokenBudget, filters, semResults);\n}\n","/**\n * Raw event flush orchestration — bridge between the sweeper and the ingest pipeline.\n *\n * Ports the core flush logic from codemem/raw_event_flush.py.\n *\n * Reads unflushed raw events for a session, creates a batch record,\n * builds an IngestPayload, runs it through the ingest pipeline,\n * and updates flush state on success (or records failure details).\n */\n\nimport { type IngestOptions, ingest } from \"./ingest-pipeline.js\";\nimport type { IngestPayload, SessionContext } from \"./ingest-types.js\";\nimport { ObserverAuthError } from \"./observer-client.js\";\nimport type { MemoryStore } from \"./store.js\";\n\nconst EXTRACTOR_VERSION = \"raw_events_v1\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction truncateErrorMessage(message: string, limit = 280): string {\n\tconst text = message.replace(/\\s+/g, \" \").trim();\n\tif (text.length <= limit) return text;\n\treturn `${text.slice(0, limit - 3).trimEnd()}...`;\n}\n\nfunction providerDisplayName(provider: string | null | undefined): string {\n\tconst normalized = (provider ?? \"\").trim().toLowerCase();\n\tif (normalized === \"openai\") return \"OpenAI\";\n\tif (normalized === \"anthropic\") return \"Anthropic\";\n\tif (normalized) return normalized.charAt(0).toUpperCase() + normalized.slice(1);\n\treturn \"Observer\";\n}\n\nfunction summarizeFlushFailure(exc: Error, provider: string | null | undefined): string {\n\tconst providerTitle = providerDisplayName(provider);\n\tconst rawMessage = String(exc.message ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\n\tif (exc instanceof ObserverAuthError) {\n\t\treturn `${providerTitle} authentication failed. Refresh credentials and retry.`;\n\t}\n\tif (exc.name === \"TimeoutError\" || rawMessage.includes(\"timeout\")) {\n\t\treturn `${providerTitle} request timed out during raw-event processing.`;\n\t}\n\tif (\n\t\trawMessage === \"observer failed during raw-event flush\" ||\n\t\trawMessage === \"observer produced no storable output for raw-event flush\"\n\t) {\n\t\treturn `${providerTitle} returned no usable output for raw-event processing.`;\n\t}\n\tif (/parse|xml|json/i.test(rawMessage)) {\n\t\treturn `${providerTitle} response could not be processed.`;\n\t}\n\treturn `${providerTitle} processing failed during raw-event ingestion.`;\n}\n\n// ---------------------------------------------------------------------------\n// Session context builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build session context from raw events — extracts prompt count, tool count,\n * duration, files modified/read, and first user prompt.\n *\n * Port of build_session_context() from raw_event_flush.py.\n */\nexport function buildSessionContext(events: Record<string, unknown>[]): SessionContext {\n\tlet promptCount = 0;\n\tlet toolCount = 0;\n\n\tfor (const e of events) {\n\t\tif (e.type === \"user_prompt\") promptCount++;\n\t\tif (e.type === \"tool.execute.after\") toolCount++;\n\t}\n\n\tconst tsValues: number[] = [];\n\tfor (const e of events) {\n\t\tconst ts = e.timestamp_wall_ms;\n\t\tif (ts == null) continue;\n\t\tconst num = Number(ts);\n\t\tif (!Number.isFinite(num)) continue;\n\t\ttsValues.push(num);\n\t}\n\tlet durationMs = 0;\n\tif (tsValues.length > 0) {\n\t\tlet minTs = tsValues[0]!;\n\t\tlet maxTs = tsValues[0]!;\n\t\tfor (let i = 1; i < tsValues.length; i++) {\n\t\t\tconst v = tsValues[i]!;\n\t\t\tif (v < minTs) minTs = v;\n\t\t\tif (v > maxTs) maxTs = v;\n\t\t}\n\t\tdurationMs = Math.max(0, maxTs - minTs);\n\t}\n\n\tconst filesModified = new Set<string>();\n\tconst filesRead = new Set<string>();\n\tfor (const e of events) {\n\t\tif (e.type !== \"tool.execute.after\") continue;\n\t\tconst tool = String(e.tool ?? \"\").toLowerCase();\n\t\tconst args = e.args;\n\t\tif (args == null || typeof args !== \"object\") continue;\n\t\tconst filePath =\n\t\t\t(args as Record<string, unknown>).filePath ?? (args as Record<string, unknown>).path;\n\t\tif (typeof filePath !== \"string\" || !filePath) continue;\n\t\tif (tool === \"write\" || tool === \"edit\") filesModified.add(filePath);\n\t\tif (tool === \"read\") filesRead.add(filePath);\n\t}\n\n\tlet firstPrompt: string | undefined;\n\tfor (const e of events) {\n\t\tif (e.type !== \"user_prompt\") continue;\n\t\tconst text = e.prompt_text;\n\t\tif (typeof text === \"string\" && text.trim()) {\n\t\t\tfirstPrompt = text.trim();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn {\n\t\tfirstPrompt,\n\t\tpromptCount,\n\t\ttoolCount,\n\t\tdurationMs,\n\t\tfilesModified: [...filesModified].sort(),\n\t\tfilesRead: [...filesRead].sort(),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Main flush function\n// ---------------------------------------------------------------------------\n\nexport interface FlushRawEventsOptions {\n\topencodeSessionId: string;\n\tsource?: string;\n\tcwd?: string | null;\n\tproject?: string | null;\n\tstartedAt?: string | null;\n\tmaxEvents?: number | null;\n\tallowEmptyFlush?: boolean;\n}\n\n/**\n * Flush raw events for a single session through the ingest pipeline.\n *\n * 1. Reads unflushed raw events from the store\n * 2. Creates/claims a flush batch for idempotency\n * 3. Builds session context and IngestPayload\n * 4. Calls ingest()\n * 5. Updates flush state on success; records failure details on error\n *\n * Port of flush_raw_events() from raw_event_flush.py.\n */\nexport async function flushRawEvents(\n\tstore: MemoryStore,\n\tingestOpts: IngestOptions,\n\topts: FlushRawEventsOptions,\n): Promise<{ flushed: number; updatedState: number }> {\n\tlet { source = \"opencode\", cwd, project, startedAt } = opts;\n\tconst { opencodeSessionId, maxEvents } = opts;\n\tconst allowEmptyFlush = opts.allowEmptyFlush ?? false;\n\n\tsource = (source ?? \"\").trim().toLowerCase() || \"opencode\";\n\n\t// Resolve session metadata for missing fields\n\tconst meta = store.rawEventSessionMeta(opencodeSessionId, source);\n\tif (cwd == null) cwd = (meta.cwd as string) ?? process.cwd();\n\tif (project == null) project = (meta.project as string) ?? null;\n\tif (startedAt == null) startedAt = (meta.started_at as string) ?? null;\n\n\t// Read unflushed events\n\tconst lastFlushed = store.rawEventFlushState(opencodeSessionId, source);\n\tconst events = store.rawEventsSinceBySeq(opencodeSessionId, source, lastFlushed, maxEvents);\n\tif (events.length === 0) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Extract event sequence range\n\tconst eventSeqs: number[] = [];\n\tfor (const e of events) {\n\t\tconst seq = e.event_seq;\n\t\tif (seq == null) continue;\n\t\tconst num = Number(seq);\n\t\tif (Number.isFinite(num)) eventSeqs.push(num);\n\t}\n\tif (eventSeqs.length === 0) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\tlet startEventSeq = eventSeqs[0]!;\n\tlet lastEventSeq = eventSeqs[0]!;\n\tfor (let i = 1; i < eventSeqs.length; i++) {\n\t\tconst v = eventSeqs[i]!;\n\t\tif (v < startEventSeq) startEventSeq = v;\n\t\tif (v > lastEventSeq) lastEventSeq = v;\n\t}\n\tif (lastEventSeq < startEventSeq) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Get or create flush batch (idempotency guard)\n\tconst { batchId, status } = store.getOrCreateRawEventFlushBatch(\n\t\topencodeSessionId,\n\t\tsource,\n\t\tstartEventSeq,\n\t\tlastEventSeq,\n\t\tEXTRACTOR_VERSION,\n\t);\n\n\t// If already completed, just advance flush state\n\tif (status === \"completed\") {\n\t\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\t\treturn { flushed: 0, updatedState: 1 };\n\t}\n\n\t// Claim the batch (atomic lock)\n\tif (!store.claimRawEventFlushBatch(batchId)) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Build session context\n\tconst sessionContext: SessionContext = buildSessionContext(events);\n\tsessionContext.opencodeSessionId = opencodeSessionId;\n\tsessionContext.source = source;\n\tsessionContext.streamId = opencodeSessionId;\n\tsessionContext.flusher = \"raw_events\";\n\tsessionContext.flushBatch = {\n\t\tbatch_id: batchId,\n\t\tstart_event_seq: startEventSeq,\n\t\tend_event_seq: lastEventSeq,\n\t};\n\n\t// Build ingest payload\n\tconst payload: IngestPayload = {\n\t\tcwd: cwd ?? undefined,\n\t\tproject: project ?? undefined,\n\t\tstartedAt: startedAt ?? new Date().toISOString(),\n\t\tevents,\n\t\tsessionContext,\n\t};\n\n\t// Run ingest pipeline\n\ttry {\n\t\tawait ingest(payload, store, ingestOpts);\n\t} catch (exc) {\n\t\t// Record failure details on the batch\n\t\tconst err = exc instanceof Error ? exc : new Error(String(exc));\n\t\tif (\n\t\t\tallowEmptyFlush &&\n\t\t\terr.message === \"observer produced no storable output for raw-event flush\"\n\t\t) {\n\t\t\tstore.updateRawEventFlushBatchStatus(batchId, \"completed\");\n\t\t\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\t\t\treturn { flushed: events.length, updatedState: 1 };\n\t\t}\n\t\tconst provider = ingestOpts.observer?.getStatus?.()?.provider as string | undefined;\n\t\tconst message = truncateErrorMessage(summarizeFlushFailure(err, provider));\n\t\tstore.recordRawEventFlushBatchFailure(batchId, {\n\t\t\tmessage,\n\t\t\terrorType: err instanceof ObserverAuthError ? \"ObserverAuthError\" : err.name,\n\t\t\tobserverProvider: provider ?? null,\n\t\t\tobserverModel: (ingestOpts.observer?.getStatus?.()?.model as string) ?? null,\n\t\t\tobserverRuntime: null,\n\t\t});\n\t\tthrow exc;\n\t}\n\n\t// Success — mark batch completed and advance flush state\n\tstore.updateRawEventFlushBatchStatus(batchId, \"completed\");\n\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\treturn { flushed: events.length, updatedState: 1 };\n}\n","/**\n * RawEventSweeper — periodically processes raw events into memories.\n *\n * Ports codemem/viewer_raw_events.py RawEventSweeper class.\n *\n * Uses setInterval (Node single-threaded) instead of Python threads.\n * The sweeper takes a shared MemoryStore and an IngestOptions provider.\n *\n * Each tick():\n * 1. Check if enabled\n * 2. Check auth backoff\n * 3. Purge old events (if retention configured)\n * 4. Mark stuck batches as error\n * 5. Flush sessions with pending queue entries\n * 6. Flush idle sessions with unflushed events\n * 7. Handle auth errors by setting backoff\n */\n\nimport type { IngestOptions } from \"./ingest-pipeline.js\";\nimport { ObserverAuthError } from \"./observer-client.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport { flushRawEvents } from \"./raw-event-flush.js\";\nimport type { MemoryStore } from \"./store.js\";\n\n/** Back off after an auth error. 60s gives OpenCode time to refresh its\n * OAuth token while staying longer than the default 30s sweep interval. */\nconst AUTH_BACKOFF_S = 60;\n\n// ---------------------------------------------------------------------------\n// Env helpers — read config from env vars matching Python exactly\n// ---------------------------------------------------------------------------\n\nfunction envInt(name: string, fallback: number): number {\n\tconst value = process.env[name];\n\tif (value == null) return fallback;\n\tconst parsed = Number.parseInt(value, 10);\n\treturn Number.isFinite(parsed) ? parsed : fallback;\n}\n\nfunction envBoolDisabled(name: string): boolean {\n\tconst value = (process.env[name] ?? \"1\").trim().toLowerCase();\n\treturn value === \"0\" || value === \"false\" || value === \"off\";\n}\n\n// ---------------------------------------------------------------------------\n// RawEventSweeper\n// ---------------------------------------------------------------------------\n\nexport class RawEventSweeper {\n\tprivate store: MemoryStore;\n\tprivate ingestOpts: IngestOptions;\n\tprivate active = false;\n\tprivate running = false; // reentrancy guard — prevents overlapping ticks\n\tprivate currentTick: Promise<void> | null = null;\n\tprivate wakeHandle: ReturnType<typeof setTimeout> | null = null;\n\tprivate loopHandle: ReturnType<typeof setTimeout> | null = null;\n\tprivate autoFlushTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate sessionFlushing = new Set<string>();\n\tprivate autoFlushPending = new Set<string>();\n\tprivate autoFlushPromises = new Set<Promise<void>>();\n\tprivate authBackoffUntil = 0; // epoch seconds\n\tprivate authErrorLogged = false;\n\n\tconstructor(store: MemoryStore, ingestOpts: IngestOptions) {\n\t\tthis.store = store;\n\t\tthis.ingestOpts = ingestOpts;\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Config readers (from env vars, matching Python)\n\t// -----------------------------------------------------------------------\n\n\tprivate enabled(): boolean {\n\t\treturn !envBoolDisabled(\"CODEMEM_RAW_EVENTS_SWEEPER\");\n\t}\n\n\tprivate intervalMs(): number {\n\t\tconst envValue = process.env.CODEMEM_RAW_EVENTS_SWEEPER_INTERVAL_MS;\n\t\tif (envValue != null) {\n\t\t\tconst parsed = Number.parseInt(envValue, 10);\n\t\t\treturn Number.isFinite(parsed) ? Math.max(1000, parsed) : 30_000;\n\t\t}\n\t\tconst configValue = readCodememConfigFile().raw_events_sweeper_interval_s;\n\t\tconst configSeconds =\n\t\t\ttypeof configValue === \"number\"\n\t\t\t\t? configValue\n\t\t\t\t: typeof configValue === \"string\"\n\t\t\t\t\t? Number.parseInt(configValue, 10)\n\t\t\t\t\t: Number.NaN;\n\t\tif (Number.isFinite(configSeconds) && configSeconds > 0) {\n\t\t\treturn Math.max(1000, configSeconds * 1000);\n\t\t}\n\t\treturn 30_000;\n\t}\n\n\tprivate idleMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_SWEEPER_IDLE_MS\", 120_000);\n\t}\n\n\tprivate limit(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_SWEEPER_LIMIT\", 25);\n\t}\n\n\tprivate workerMaxEvents(): number | null {\n\t\tconst parsed = envInt(\"CODEMEM_RAW_EVENTS_WORKER_MAX_EVENTS\", 250);\n\t\treturn parsed <= 0 ? null : parsed;\n\t}\n\n\tprivate retentionMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_RETENTION_MS\", 0);\n\t}\n\n\tprivate autoFlushEnabled(): boolean {\n\t\treturn (process.env.CODEMEM_RAW_EVENTS_AUTO_FLUSH ?? \"\").trim() === \"1\";\n\t}\n\n\tprivate debounceMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_DEBOUNCE_MS\", 60_000);\n\t}\n\n\tprivate stuckBatchMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_STUCK_BATCH_MS\", 300_000);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Auth backoff\n\t// -----------------------------------------------------------------------\n\n\tprivate handleAuthError(exc: ObserverAuthError): void {\n\t\tthis.authBackoffUntil = Date.now() / 1000 + AUTH_BACKOFF_S;\n\t\tif (!this.authErrorLogged) {\n\t\t\tthis.authErrorLogged = true;\n\t\t\tconst msg =\n\t\t\t\t`codemem: observer auth error — backing off for ${AUTH_BACKOFF_S}s. ` +\n\t\t\t\t`Refresh your provider credentials or update observer_provider in settings. ` +\n\t\t\t\t`(${exc.message})`;\n\t\t\tconsole.error(msg);\n\t\t}\n\t}\n\n\t/**\n\t * Reset the auth backoff and wake the worker.\n\t * Call this after credentials are refreshed.\n\t */\n\tresetAuthBackoff(): void {\n\t\tthis.authBackoffUntil = 0;\n\t\tthis.authErrorLogged = false;\n\t\tthis.wake();\n\t}\n\n\t/**\n\t * Return the current auth backoff status.\n\t */\n\tauthBackoffStatus(): { active: boolean; remainingS: number } {\n\t\tconst now = Date.now() / 1000;\n\t\tconst remaining = Math.max(0, Math.round(this.authBackoffUntil - now));\n\t\treturn { active: remaining > 0, remainingS: remaining };\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Lifecycle\n\t// -----------------------------------------------------------------------\n\n\t/**\n\t * Start the sweeper loop.\n\t * Uses a self-scheduling async loop (sleep → tick → sleep) to prevent\n\t * overlapping ticks. This mirrors the Python threading pattern where\n\t * the thread sleeps, runs tick() synchronously, then sleeps again.\n\t * No-op if sweeper is disabled or already running.\n\t */\n\tstart(): void {\n\t\tif (!this.enabled()) return;\n\t\tif (this.active) return;\n\t\tthis.active = true;\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Stop the sweeper. Cancels the next scheduled tick and waits for any\n\t * in-progress tick to finish before returning.\n\t */\n\tasync stop(): Promise<void> {\n\t\tthis.active = false;\n\t\tif (this.loopHandle != null) {\n\t\t\tclearTimeout(this.loopHandle);\n\t\t\tthis.loopHandle = null;\n\t\t}\n\t\tif (this.wakeHandle != null) {\n\t\t\tclearTimeout(this.wakeHandle);\n\t\t\tthis.wakeHandle = null;\n\t\t}\n\t\tfor (const timer of this.autoFlushTimers.values()) clearTimeout(timer);\n\t\tthis.autoFlushTimers.clear();\n\t\tif (this.currentTick != null) {\n\t\t\tawait this.currentTick;\n\t\t}\n\t\tif (this.autoFlushPromises.size > 0) {\n\t\t\tawait Promise.allSettled([...this.autoFlushPromises]);\n\t\t}\n\t}\n\n\t/**\n\t * Notify the sweeper that config changed.\n\t * Schedules an extra tick after a short delay.\n\t */\n\tnotifyConfigChanged(): void {\n\t\tthis.wake();\n\t}\n\n\t/**\n\t * Notify the debounced auto-flush path that new events arrived.\n\t * Mirrors Python's RawEventAutoFlusher.note_activity() behavior.\n\t */\n\tnudge(opencodeSessionId: string, source = \"opencode\"): void {\n\t\tif (!this.autoFlushEnabled()) return;\n\t\tif (Date.now() / 1000 < this.authBackoffUntil) return;\n\t\tconst streamId = opencodeSessionId.trim();\n\t\tif (!streamId) return;\n\t\tconst sourceNorm = source.trim().toLowerCase() || \"opencode\";\n\t\tconst key = `${sourceNorm}:${streamId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = this.debounceMs();\n\t\tif (delayMs <= 0) {\n\t\t\tthis.trackAutoFlush(this.flushNow(streamId, sourceNorm));\n\t\t\treturn;\n\t\t}\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) clearTimeout(existing);\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t\tthis.trackAutoFlush(this.flushNow(streamId, sourceNorm));\n\t\t}, delayMs);\n\t\tif (typeof timer === \"object\" && \"unref\" in timer) timer.unref();\n\t\tthis.autoFlushTimers.set(key, timer);\n\t}\n\n\tprivate trackAutoFlush(promise: Promise<void>): void {\n\t\tthis.autoFlushPromises.add(promise);\n\t\tvoid promise.finally(() => {\n\t\t\tthis.autoFlushPromises.delete(promise);\n\t\t});\n\t}\n\n\tprivate scheduleAutoFlush(opencodeSessionId: string, source: string): void {\n\t\tconst key = `${source}:${opencodeSessionId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = this.debounceMs();\n\t\tif (delayMs <= 0) {\n\t\t\tthis.trackAutoFlush(this.flushNow(opencodeSessionId, source));\n\t\t\treturn;\n\t\t}\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) clearTimeout(existing);\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t\tthis.trackAutoFlush(this.flushNow(opencodeSessionId, source));\n\t\t}, delayMs);\n\t\tif (typeof timer === \"object\" && \"unref\" in timer) timer.unref();\n\t\tthis.autoFlushTimers.set(key, timer);\n\t}\n\n\tprivate async flushNow(opencodeSessionId: string, source: string): Promise<void> {\n\t\tif (Date.now() / 1000 < this.authBackoffUntil) return;\n\t\tconst key = `${source}:${opencodeSessionId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tthis.sessionFlushing.add(key);\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t}\n\t\ttry {\n\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\topencodeSessionId,\n\t\t\t\tsource,\n\t\t\t\tcwd: null,\n\t\t\t\tproject: null,\n\t\t\t\tstartedAt: null,\n\t\t\t\tmaxEvents: null,\n\t\t\t\tallowEmptyFlush: true,\n\t\t\t});\n\t\t} catch (exc) {\n\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.error(\n\t\t\t\t`codemem: raw event auto flush failed for ${opencodeSessionId}:`,\n\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.sessionFlushing.delete(key);\n\t\t\tif (this.autoFlushPending.delete(key)) {\n\t\t\t\tthis.scheduleAutoFlush(opencodeSessionId, source);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Schedule the next tick after the configured interval. */\n\tprivate scheduleNext(): void {\n\t\tif (!this.active) return;\n\t\tthis.loopHandle = setTimeout(async () => {\n\t\t\tthis.loopHandle = null;\n\t\t\tawait this.runTick();\n\t\t\tthis.scheduleNext();\n\t\t}, this.intervalMs());\n\t\tif (typeof this.loopHandle === \"object\" && \"unref\" in this.loopHandle) {\n\t\t\tthis.loopHandle.unref();\n\t\t}\n\t}\n\n\t/** Execute a tick with reentrancy protection. */\n\tprivate async runTick(): Promise<void> {\n\t\tif (this.running) return;\n\t\tthis.running = true;\n\t\tthis.currentTick = (async () => {\n\t\t\ttry {\n\t\t\t\tawait this.tick();\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"codemem: sweeper tick failed unexpectedly:\", err);\n\t\t\t} finally {\n\t\t\t\tthis.running = false;\n\t\t\t\tthis.currentTick = null;\n\t\t\t}\n\t\t})();\n\t\tawait this.currentTick;\n\t}\n\n\tprivate wake(): void {\n\t\tif (!this.active) return;\n\t\t// Schedule a near-immediate extra tick (with reentrancy guard)\n\t\tif (this.wakeHandle != null) {\n\t\t\tclearTimeout(this.wakeHandle);\n\t\t}\n\t\tthis.wakeHandle = setTimeout(async () => {\n\t\t\tthis.wakeHandle = null;\n\t\t\tawait this.runTick();\n\t\t}, 100);\n\t\tif (typeof this.wakeHandle === \"object\" && \"unref\" in this.wakeHandle) {\n\t\t\tthis.wakeHandle.unref();\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Tick — one sweep cycle\n\t// -----------------------------------------------------------------------\n\n\t/**\n\t * Execute one sweep cycle. Public for testing.\n\t *\n\t * 1. Check enabled / auth backoff\n\t * 2. Purge old events\n\t * 3. Mark stuck batches\n\t * 4. Flush pending queue sessions\n\t * 5. Flush idle sessions\n\t */\n\tasync tick(): Promise<void> {\n\t\tif (!this.enabled()) return;\n\n\t\t// Skip while backing off from auth error\n\t\tconst now = Date.now() / 1000;\n\t\tif (now < this.authBackoffUntil) return;\n\n\t\t// Backoff expired — reset so next auth error gets logged again\n\t\tif (this.authErrorLogged) {\n\t\t\tthis.authErrorLogged = false;\n\t\t}\n\n\t\tconst nowMs = Date.now();\n\t\tconst idleBefore = nowMs - this.idleMs();\n\n\t\t// Purge old events if retention configured\n\t\tconst retentionMs = this.retentionMs();\n\t\tif (retentionMs > 0) {\n\t\t\tthis.store.purgeRawEvents(retentionMs);\n\t\t}\n\n\t\t// Mark stuck batches as error\n\t\tconst stuckMs = this.stuckBatchMs();\n\t\tif (stuckMs > 0) {\n\t\t\tconst cutoff = new Date(nowMs - stuckMs).toISOString();\n\t\t\tthis.store.markStuckRawEventBatchesAsError(cutoff, 100);\n\t\t}\n\n\t\tconst maxEvents = this.workerMaxEvents();\n\t\tconst sessionLimit = this.limit();\n\t\tconst drained = new Set<string>();\n\n\t\t// Phase 1: Flush sessions with pending queue entries\n\t\tconst queueSessions = this.store.rawEventSessionsWithPendingQueue(sessionLimit);\n\t\tfor (const item of queueSessions) {\n\t\t\tconst { source, streamId } = item;\n\t\t\tif (!streamId) continue;\n\t\t\tconst key = `${source}:${streamId}`;\n\t\t\tif (this.sessionFlushing.has(key)) continue;\n\t\t\tthis.sessionFlushing.add(key);\n\n\t\t\ttry {\n\t\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\t\topencodeSessionId: streamId,\n\t\t\t\t\tsource,\n\t\t\t\t\tcwd: null,\n\t\t\t\t\tproject: null,\n\t\t\t\t\tstartedAt: null,\n\t\t\t\t\tmaxEvents,\n\t\t\t\t\tallowEmptyFlush: true,\n\t\t\t\t});\n\t\t\t\tdrained.add(`${source}:${streamId}`);\n\t\t\t} catch (exc) {\n\t\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\t\treturn; // Stop all flush work during auth backoff\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t`codemem: raw event queue worker flush failed for ${streamId}:`,\n\t\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis.sessionFlushing.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Phase 2: Flush idle sessions with unflushed events\n\t\tconst idleSessions = this.store.rawEventSessionsPendingIdleFlush(idleBefore, sessionLimit);\n\t\tfor (const item of idleSessions) {\n\t\t\tconst { source, streamId } = item;\n\t\t\tif (!streamId) continue;\n\t\t\tif (drained.has(`${source}:${streamId}`)) continue;\n\t\t\tconst key = `${source}:${streamId}`;\n\t\t\tif (this.sessionFlushing.has(key)) continue;\n\t\t\tthis.sessionFlushing.add(key);\n\n\t\t\ttry {\n\t\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\t\topencodeSessionId: streamId,\n\t\t\t\t\tsource,\n\t\t\t\t\tcwd: null,\n\t\t\t\t\tproject: null,\n\t\t\t\t\tstartedAt: null,\n\t\t\t\t\tmaxEvents,\n\t\t\t\t\tallowEmptyFlush: true,\n\t\t\t\t});\n\t\t\t} catch (exc) {\n\t\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\t\treturn; // Stop all flush work during auth backoff\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t`codemem: raw event sweeper flush failed for ${streamId}:`,\n\t\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis.sessionFlushing.delete(key);\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * Sync replication: cursor tracking, op chunking, and payload extraction.\n *\n * Uses Drizzle ORM for all SQL queries so that column mismatches are\n * compile-time errors instead of silent data-loss bugs at runtime.\n *\n * Keeps replication functions decoupled from MemoryStore by accepting\n * a raw Database handle. Ported from codemem/sync/replication.py.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { and, eq, gt, isNotNull, isNull, like, or, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { fromJson, fromJsonStrict, toJson, toJsonNullable } from \"./db.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport * as schema from \"./schema.js\";\nimport type { ReplicationOp } from \"./types.js\";\n\ntype MemoryItemRow = typeof schema.memoryItems.$inferSelect;\n\ninterface MemoryPayload {\n\tsession_id: number | null;\n\tkind: string | null;\n\ttitle: string | null;\n\tsubtitle: string | null;\n\tbody_text: string | null;\n\tconfidence: number | null;\n\ttags_text: string | null;\n\tactive: number | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tmetadata_json: Record<string, unknown>;\n\tactor_id: string | null;\n\tactor_display_name: string | null;\n\tvisibility: string | null;\n\tworkspace_id: string | null;\n\tworkspace_kind: string | null;\n\torigin_device_id: string | null;\n\torigin_source: string | null;\n\ttrust_state: string | null;\n\tnarrative: string | null;\n\tfacts: unknown;\n\tconcepts: unknown;\n\tfiles_read: unknown;\n\tfiles_modified: unknown;\n\tuser_prompt_id: number | null;\n\tprompt_number: number | null;\n\tdeleted_at: string | null;\n\trev: number;\n\timport_key: string | null;\n}\n\nfunction asNumberOrNull(value: unknown): number | null {\n\tif (value == null) return null;\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"string\" && value.trim().length > 0) {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn null;\n}\n\nfunction asStringOrNull(value: unknown): string | null {\n\tif (value == null) return null;\n\treturn String(value);\n}\n\nfunction parseMemoryPayload(op: ReplicationOp, errors: string[]): MemoryPayload | null {\n\tif (!op.payload_json) return null;\n\tlet raw: Record<string, unknown>;\n\ttry {\n\t\traw = fromJsonStrict(op.payload_json);\n\t} catch (err) {\n\t\terrors.push(\n\t\t\t`op ${op.op_id}: skipped — payload_json is not a valid object: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t\treturn null;\n\t}\n\tconst metadataRaw = raw.metadata_json;\n\tconst metadata_json =\n\t\tmetadataRaw && typeof metadataRaw === \"object\" && !Array.isArray(metadataRaw)\n\t\t\t? { ...(metadataRaw as Record<string, unknown>) }\n\t\t\t: {};\n\treturn {\n\t\tsession_id: asNumberOrNull(raw.session_id),\n\t\tkind: asStringOrNull(raw.kind),\n\t\ttitle: asStringOrNull(raw.title),\n\t\tsubtitle: asStringOrNull(raw.subtitle),\n\t\tbody_text: asStringOrNull(raw.body_text),\n\t\tconfidence: asNumberOrNull(raw.confidence),\n\t\ttags_text: asStringOrNull(raw.tags_text),\n\t\tactive: asNumberOrNull(raw.active),\n\t\tcreated_at: asStringOrNull(raw.created_at),\n\t\tupdated_at: asStringOrNull(raw.updated_at),\n\t\tmetadata_json,\n\t\tactor_id: asStringOrNull(raw.actor_id),\n\t\tactor_display_name: asStringOrNull(raw.actor_display_name),\n\t\tvisibility: asStringOrNull(raw.visibility),\n\t\tworkspace_id: asStringOrNull(raw.workspace_id),\n\t\tworkspace_kind: asStringOrNull(raw.workspace_kind),\n\t\torigin_device_id: asStringOrNull(raw.origin_device_id),\n\t\torigin_source: asStringOrNull(raw.origin_source),\n\t\ttrust_state: asStringOrNull(raw.trust_state),\n\t\tnarrative: asStringOrNull(raw.narrative),\n\t\tfacts: raw.facts ?? null,\n\t\tconcepts: raw.concepts ?? null,\n\t\tfiles_read: raw.files_read ?? null,\n\t\tfiles_modified: raw.files_modified ?? null,\n\t\tuser_prompt_id: asNumberOrNull(raw.user_prompt_id),\n\t\tprompt_number: asNumberOrNull(raw.prompt_number),\n\t\tdeleted_at: asStringOrNull(raw.deleted_at),\n\t\trev: asNumberOrNull(raw.rev) ?? 0,\n\t\timport_key: asStringOrNull(raw.import_key),\n\t};\n}\n\nfunction mergePayloadMetadata(\n\tmetadata: Record<string, unknown>,\n\tclockDeviceId: string,\n): Record<string, unknown> {\n\treturn {\n\t\t...metadata,\n\t\tclock_device_id: clockDeviceId,\n\t};\n}\n\nfunction clockDeviceIdFromMetadataJson(raw: string | null): string {\n\tconst metadata = fromJson(raw);\n\treturn typeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id : \"\";\n}\n\n// Op chunking\n\n/**\n * Split replication ops into batches that fit within a byte-size limit.\n *\n * Each batch is serialized as `{\"ops\": [...]}` and must not exceed maxBytes\n * when UTF-8 encoded. Throws if a single op exceeds the limit.\n */\nexport function chunkOpsBySize(ops: ReplicationOp[], maxBytes: number): ReplicationOp[][] {\n\tconst encoder = new TextEncoder();\n\t// Overhead: {\"ops\":[]} = 9 bytes, comma between elements = 1 byte\n\tconst WRAPPER_OVERHEAD = 9;\n\tconst COMMA_OVERHEAD = 1;\n\n\tconst batches: ReplicationOp[][] = [];\n\tlet current: ReplicationOp[] = [];\n\tlet currentBytes = WRAPPER_OVERHEAD;\n\n\tfor (const op of ops) {\n\t\tconst opBytes = encoder.encode(JSON.stringify(op)).byteLength;\n\t\tconst addedBytes = current.length === 0 ? opBytes : opBytes + COMMA_OVERHEAD;\n\n\t\tif (currentBytes + addedBytes <= maxBytes) {\n\t\t\tcurrent.push(op);\n\t\t\tcurrentBytes += addedBytes;\n\t\t\tcontinue;\n\t\t}\n\t\tif (current.length === 0) {\n\t\t\tthrow new Error(\"single op exceeds size limit\");\n\t\t}\n\t\tbatches.push(current);\n\t\tcurrent = [op];\n\t\tcurrentBytes = WRAPPER_OVERHEAD + opBytes;\n\t\tif (currentBytes > maxBytes) {\n\t\t\tthrow new Error(\"single op exceeds size limit\");\n\t\t}\n\t}\n\tif (current.length > 0) {\n\t\tbatches.push(current);\n\t}\n\treturn batches;\n}\n\n// Cursor read/write\n\n/**\n * Read the replication cursor for a peer device.\n *\n * Returns `[lastApplied, lastAcked]` — both null when no cursor exists.\n */\nexport function getReplicationCursor(\n\tdb: Database,\n\tpeerDeviceId: string,\n): [lastApplied: string | null, lastAcked: string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({\n\t\t\tlast_applied_cursor: schema.replicationCursors.last_applied_cursor,\n\t\t\tlast_acked_cursor: schema.replicationCursors.last_acked_cursor,\n\t\t})\n\t\t.from(schema.replicationCursors)\n\t\t.where(eq(schema.replicationCursors.peer_device_id, peerDeviceId))\n\t\t.get();\n\n\tif (!row) return [null, null];\n\treturn [row.last_applied_cursor, row.last_acked_cursor];\n}\n\n/**\n * Insert or update the replication cursor for a peer device.\n *\n * Uses COALESCE on update so callers can set only one of the two cursors\n * without clobbering the other.\n */\nexport function setReplicationCursor(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: { lastApplied?: string | null; lastAcked?: string | null } = {},\n): void {\n\tconst now = new Date().toISOString();\n\tconst lastApplied = options.lastApplied ?? null;\n\tconst lastAcked = options.lastAcked ?? null;\n\n\tconst d = drizzle(db, { schema });\n\t// Atomic UPSERT — avoids TOCTOU race with concurrent sync workers.\n\t// Drizzle's onConflictDoUpdate doesn't support COALESCE(excluded.col, col)\n\t// natively, so we use raw SQL for the SET expressions.\n\td.insert(schema.replicationCursors)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tlast_applied_cursor: lastApplied,\n\t\t\tlast_acked_cursor: lastAcked,\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.replicationCursors.peer_device_id,\n\t\t\tset: {\n\t\t\t\tlast_applied_cursor: sql`COALESCE(excluded.last_applied_cursor, ${schema.replicationCursors.last_applied_cursor})`,\n\t\t\t\tlast_acked_cursor: sql`COALESCE(excluded.last_acked_cursor, ${schema.replicationCursors.last_acked_cursor})`,\n\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n}\n\n// Payload extraction\n\n/**\n * Extract replication ops from a parsed JSON payload.\n *\n * Returns an empty array if the payload is not an object or lacks an `ops` array.\n */\n/** Required fields for a valid replication op. */\nconst REQUIRED_OP_FIELDS = [\n\t\"op_id\",\n\t\"entity_type\",\n\t\"entity_id\",\n\t\"op_type\",\n\t\"clock_rev\",\n\t\"clock_updated_at\",\n\t\"clock_device_id\",\n\t\"device_id\",\n\t\"created_at\",\n] as const;\n\n/**\n * Extract and validate replication ops from a parsed JSON payload.\n *\n * Returns an empty array if the payload is not an object or lacks an `ops` array.\n * Silently filters out ops missing required fields to prevent garbage data\n * from entering the replication pipeline.\n */\nexport function extractReplicationOps(payload: unknown): ReplicationOp[] {\n\tif (typeof payload !== \"object\" || payload === null) return [];\n\tconst obj = payload as Record<string, unknown>;\n\tconst ops = obj.ops;\n\tif (!Array.isArray(ops)) return [];\n\n\treturn ops.filter((op): op is ReplicationOp => {\n\t\tif (typeof op !== \"object\" || op === null) return false;\n\t\tconst record = op as Record<string, unknown>;\n\t\treturn REQUIRED_OP_FIELDS.every(\n\t\t\t(field) => record[field] !== undefined && record[field] !== null,\n\t\t);\n\t});\n}\n\n// Clock comparison\n\n/**\n * Build a clock tuple from individual fields.\n *\n * Clock tuples enable lexicographic comparison for last-writer-wins\n * conflict resolution: higher rev wins, tiebreak on updated_at, then device_id.\n */\nexport function clockTuple(\n\trev: number,\n\tupdatedAt: string,\n\tdeviceId: string,\n): [number, string, string] {\n\treturn [rev, updatedAt, deviceId];\n}\n\n/**\n * Return true if `candidate` clock is strictly newer than `existing`.\n *\n * Comparison order: rev (higher wins) → updated_at → device_id.\n * Mirrors Python's `_is_newer_clock` which relies on tuple ordering.\n */\nexport function isNewerClock(\n\tcandidate: [number, string, string],\n\texisting: [number, string, string],\n): boolean {\n\tif (candidate[0] !== existing[0]) return candidate[0] > existing[0];\n\tif (candidate[1] !== existing[1]) return candidate[1] > existing[1];\n\treturn candidate[2] > existing[2];\n}\n\n// Record a replication op\n\n/**\n * Generate and INSERT a single replication op for a memory item.\n *\n * Reads the memory item's clock fields (rev, updated_at, metadata_json)\n * from the DB, builds the op row, and inserts into `replication_ops`.\n * Returns the generated op_id (UUID).\n *\n * Uses Drizzle's typed select so all memory_items columns are captured\n * automatically — no hand-maintained column list to go out of sync.\n */\nexport function recordReplicationOp(\n\tdb: Database,\n\topts: {\n\t\tmemoryId: number;\n\t\topType: \"upsert\" | \"delete\";\n\t\tdeviceId: string;\n\t\tpayload?: Record<string, unknown>;\n\t},\n): string {\n\tconst d = drizzle(db, { schema });\n\tconst opId = randomUUID();\n\tconst now = new Date().toISOString();\n\n\t// Read the full memory row — typed via Drizzle schema\n\tconst row = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(eq(schema.memoryItems.id, opts.memoryId))\n\t\t.get();\n\n\tconst rev = Number(row?.rev ?? 0);\n\tconst updatedAt = row?.updated_at ?? now;\n\tconst entityId = row?.import_key ?? String(opts.memoryId);\n\tconst metadata = fromJson(row?.metadata_json);\n\tconst clockDeviceId =\n\t\ttypeof metadata.clock_device_id === \"string\" && metadata.clock_device_id.trim().length > 0\n\t\t\t? metadata.clock_device_id\n\t\t\t: opts.deviceId;\n\n\t// Build payload from the memory row so peers can reconstruct the full item.\n\t// Explicit payload override takes precedence (used by tests).\n\tlet payloadJson: string | null;\n\tif (opts.payload) {\n\t\tpayloadJson = toJson(opts.payload);\n\t} else if (row && opts.opType === \"upsert\") {\n\t\t// Parse JSON-string columns so they round-trip as objects, not double-encoded strings\n\t\tconst parseSqliteJson = (val: string | null | undefined): unknown => {\n\t\t\tif (typeof val !== \"string\") return val ?? null;\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(val);\n\t\t\t} catch {\n\t\t\t\treturn val;\n\t\t\t}\n\t\t};\n\n\t\tpayloadJson = toJson({\n\t\t\tsession_id: row.session_id,\n\t\t\tkind: row.kind,\n\t\t\ttitle: row.title,\n\t\t\tsubtitle: row.subtitle,\n\t\t\tbody_text: row.body_text,\n\t\t\tconfidence: row.confidence,\n\t\t\ttags_text: row.tags_text,\n\t\t\tactive: row.active,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\tmetadata_json: parseSqliteJson(row.metadata_json),\n\t\t\tactor_id: row.actor_id,\n\t\t\tactor_display_name: row.actor_display_name,\n\t\t\tvisibility: row.visibility,\n\t\t\tworkspace_id: row.workspace_id,\n\t\t\tworkspace_kind: row.workspace_kind,\n\t\t\torigin_device_id: row.origin_device_id,\n\t\t\torigin_source: row.origin_source,\n\t\t\ttrust_state: row.trust_state,\n\t\t\tfacts: parseSqliteJson(row.facts),\n\t\t\tnarrative: row.narrative,\n\t\t\tconcepts: parseSqliteJson(row.concepts),\n\t\t\tfiles_read: parseSqliteJson(row.files_read),\n\t\t\tfiles_modified: parseSqliteJson(row.files_modified),\n\t\t\tuser_prompt_id: row.user_prompt_id,\n\t\t\tprompt_number: row.prompt_number,\n\t\t\tdeleted_at: row.deleted_at,\n\t\t});\n\t} else {\n\t\tpayloadJson = null;\n\t}\n\n\td.insert(schema.replicationOps)\n\t\t.values({\n\t\t\top_id: opId,\n\t\t\tentity_type: \"memory_item\",\n\t\t\tentity_id: entityId,\n\t\t\top_type: opts.opType,\n\t\t\tpayload_json: payloadJson,\n\t\t\tclock_rev: rev,\n\t\t\tclock_updated_at: updatedAt,\n\t\t\tclock_device_id: clockDeviceId,\n\t\t\tdevice_id: opts.deviceId,\n\t\t\tcreated_at: now,\n\t\t})\n\t\t.run();\n\n\treturn opId;\n}\n\n// Load replication ops with cursor pagination\n\n/** Parse a `created_at|op_id` cursor into its two components. */\nfunction parseCursor(cursor: string): [createdAt: string, opId: string] | null {\n\tconst idx = cursor.indexOf(\"|\");\n\tif (idx < 0) return null;\n\treturn [cursor.slice(0, idx), cursor.slice(idx + 1)];\n}\n\n/** Build a `created_at|op_id` cursor string. */\nfunction computeCursor(createdAt: string, opId: string): string {\n\treturn `${createdAt}|${opId}`;\n}\n\n/**\n * Load replication ops created after `cursor`, ordered by (created_at, op_id).\n *\n * Returns `[ops, nextCursor]` where nextCursor is the cursor for the last\n * returned row (or null if no rows matched). The cursor format is\n * `created_at|op_id`.\n */\nexport function loadReplicationOpsSince(\n\tdb: Database,\n\tcursor: string | null,\n\tlimit = 100,\n\tdeviceId?: string,\n): [ReplicationOp[], string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst t = schema.replicationOps;\n\tconst conditions = [];\n\n\tif (cursor) {\n\t\tconst parsed = parseCursor(cursor);\n\t\tif (parsed) {\n\t\t\tconst [createdAt, opId] = parsed;\n\t\t\tconditions.push(\n\t\t\t\tor(gt(t.created_at, createdAt), and(eq(t.created_at, createdAt), gt(t.op_id, opId))),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (deviceId) {\n\t\tconditions.push(or(eq(t.device_id, deviceId), eq(t.device_id, \"local\")));\n\t}\n\n\tconst whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n\tconst rows = d\n\t\t.select()\n\t\t.from(t)\n\t\t.where(whereClause)\n\t\t.orderBy(t.created_at, t.op_id)\n\t\t.limit(limit)\n\t\t.all();\n\n\tconst ops: ReplicationOp[] = rows.map((r) => ({\n\t\top_id: r.op_id,\n\t\tentity_type: r.entity_type,\n\t\tentity_id: r.entity_id,\n\t\top_type: r.op_type,\n\t\tpayload_json: r.payload_json,\n\t\tclock_rev: r.clock_rev,\n\t\tclock_updated_at: r.clock_updated_at,\n\t\tclock_device_id: r.clock_device_id,\n\t\tdevice_id: r.device_id,\n\t\tcreated_at: r.created_at,\n\t}));\n\n\tlet nextCursor: string | null = null;\n\tif (rows.length > 0) {\n\t\tconst last = rows.at(-1);\n\t\tif (last) {\n\t\t\tnextCursor = computeCursor(last.created_at, last.op_id);\n\t\t}\n\t}\n\n\treturn [ops, nextCursor];\n}\n\nfunction cleanList(values: unknown): string[] {\n\tif (!Array.isArray(values)) return [];\n\tconst out: string[] = [];\n\tfor (const raw of values) {\n\t\tconst value = String(raw ?? \"\").trim();\n\t\tif (value) out.push(value);\n\t}\n\treturn out;\n}\n\nfunction parseStringList(value: unknown): string[] {\n\tif (Array.isArray(value)) return cleanList(value);\n\tif (typeof value === \"string\") {\n\t\treturn value\n\t\t\t.split(\",\")\n\t\t\t.map((part) => part.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn [];\n}\n\nfunction parseJsonList(valuesJson: string | null | undefined): string[] {\n\tif (!valuesJson) return [];\n\ttry {\n\t\treturn cleanList(JSON.parse(valuesJson));\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction projectBasename(value: string | null | undefined): string {\n\tconst raw = String(value ?? \"\")\n\t\t.trim()\n\t\t.replaceAll(\"\\\\\", \"/\");\n\tif (!raw) return \"\";\n\tconst parts = raw.split(\"/\").filter(Boolean);\n\treturn parts.length > 0 ? (parts[parts.length - 1] ?? \"\") : \"\";\n}\n\nfunction effectiveSyncProjectFilters(\n\tdb: Database,\n\tpeerDeviceId: string | null,\n): { include: string[]; exclude: string[] } {\n\tconst d = drizzle(db, { schema });\n\tconst config = readCodememConfigFile();\n\tconst includeOverride = process.env.CODEMEM_SYNC_PROJECTS_INCLUDE;\n\tconst excludeOverride = process.env.CODEMEM_SYNC_PROJECTS_EXCLUDE;\n\tconst globalInclude =\n\t\tincludeOverride !== undefined\n\t\t\t? parseStringList(includeOverride)\n\t\t\t: parseStringList(config.sync_projects_include);\n\tconst globalExclude =\n\t\texcludeOverride !== undefined\n\t\t\t? parseStringList(excludeOverride)\n\t\t\t: parseStringList(config.sync_projects_exclude);\n\tif (!peerDeviceId) return { include: globalInclude, exclude: globalExclude };\n\n\tconst row = d\n\t\t.select({\n\t\t\tprojects_include_json: schema.syncPeers.projects_include_json,\n\t\t\tprojects_exclude_json: schema.syncPeers.projects_exclude_json,\n\t\t})\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.limit(1)\n\t\t.get();\n\tif (!row) return { include: globalInclude, exclude: globalExclude };\n\n\tconst hasOverride = row.projects_include_json != null || row.projects_exclude_json != null;\n\tif (!hasOverride) {\n\t\treturn { include: globalInclude, exclude: globalExclude };\n\t}\n\n\treturn {\n\t\tinclude: parseJsonList(row.projects_include_json),\n\t\texclude: parseJsonList(row.projects_exclude_json),\n\t};\n}\n\nfunction syncProjectAllowed(\n\tdb: Database,\n\tproject: string | null,\n\tpeerDeviceId: string | null,\n): boolean {\n\tconst { include, exclude } = effectiveSyncProjectFilters(db, peerDeviceId);\n\tconst projectName = String(project ?? \"\").trim();\n\tconst basename = projectBasename(projectName);\n\n\tif (exclude.some((item) => item === projectName || item === basename)) return false;\n\tif (include.length === 0) return true;\n\treturn include.some((item) => item === projectName || item === basename);\n}\n\nfunction syncVisibilityAllowed(payload: Record<string, unknown> | null): boolean {\n\tif (!payload) return false;\n\tlet visibility = String(payload.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tconst metadata =\n\t\tpayload.metadata_json &&\n\t\ttypeof payload.metadata_json === \"object\" &&\n\t\t!Array.isArray(payload.metadata_json)\n\t\t\t? (payload.metadata_json as Record<string, unknown>)\n\t\t\t: {};\n\tconst metadataVisibility = String(metadata.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (!visibility && metadataVisibility) {\n\t\tvisibility = metadataVisibility;\n\t}\n\n\tif (!visibility) {\n\t\tlet workspaceKind = String(payload.workspace_kind ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tlet workspaceId = String(payload.workspace_id ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tif (!workspaceKind)\n\t\t\tworkspaceKind = String(metadata.workspace_kind ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\tif (!workspaceId)\n\t\t\tworkspaceId = String(metadata.workspace_id ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\tif (workspaceKind === \"shared\" || workspaceId.startsWith(\"shared:\")) {\n\t\t\tvisibility = \"shared\";\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn visibility === \"shared\";\n}\n\nfunction peerClaimedLocalActor(db: Database, peerDeviceId: string | null): boolean {\n\tif (!peerDeviceId) return false;\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ claimed_local_actor: schema.syncPeers.claimed_local_actor })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.limit(1)\n\t\t.get();\n\treturn Boolean(row?.claimed_local_actor);\n}\n\nfunction parsePayload(payloadJson: string | null): Record<string, unknown> | null {\n\tif (!payloadJson || !payloadJson.trim()) return null;\n\ttry {\n\t\tconst parsed = JSON.parse(payloadJson) as unknown;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n\t\treturn parsed as Record<string, unknown>;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport interface FilterReplicationSkipped {\n\treason: \"visibility_filter\" | \"project_filter\";\n\top_id: string;\n\tcreated_at: string;\n\tentity_type: string;\n\tentity_id: string;\n\tskipped_count: number;\n\tproject?: string | null;\n\tvisibility?: string | null;\n}\n\n/**\n * Filter replication ops for peer sync scopes.\n *\n * Returns ops that pass project/visibility rules, a cursor for the last\n * processed op (including skipped ops), and skipped metadata when filtering\n * removed one or more ops.\n */\nexport function filterReplicationOpsForSyncWithStatus(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tpeerDeviceId: string | null,\n): [ReplicationOp[], string | null, FilterReplicationSkipped | null] {\n\tconst allowed: ReplicationOp[] = [];\n\tlet nextCursor: string | null = null;\n\tlet skippedCount = 0;\n\tlet firstSkipped: FilterReplicationSkipped | null = null;\n\tfor (const op of ops) {\n\t\tif (op.entity_type === \"memory_item\") {\n\t\t\tconst payload = parsePayload(op.payload_json);\n\t\t\tif (op.op_type === \"delete\" && payload == null) {\n\t\t\t\tallowed.push(op);\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!syncVisibilityAllowed(payload) && !peerClaimedLocalActor(db, peerDeviceId)) {\n\t\t\t\tskippedCount += 1;\n\t\t\t\tif (!firstSkipped) {\n\t\t\t\t\tfirstSkipped = {\n\t\t\t\t\t\treason: \"visibility_filter\",\n\t\t\t\t\t\top_id: op.op_id,\n\t\t\t\t\t\tcreated_at: op.created_at,\n\t\t\t\t\t\tentity_type: op.entity_type,\n\t\t\t\t\t\tentity_id: op.entity_id,\n\t\t\t\t\t\tvisibility: typeof payload?.visibility === \"string\" ? String(payload.visibility) : null,\n\t\t\t\t\t\tskipped_count: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst project =\n\t\t\t\ttypeof payload?.project === \"string\" && payload.project.trim()\n\t\t\t\t\t? payload.project.trim()\n\t\t\t\t\t: null;\n\t\t\tif (!syncProjectAllowed(db, project, peerDeviceId)) {\n\t\t\t\tskippedCount += 1;\n\t\t\t\tif (!firstSkipped) {\n\t\t\t\t\tfirstSkipped = {\n\t\t\t\t\t\treason: \"project_filter\",\n\t\t\t\t\t\top_id: op.op_id,\n\t\t\t\t\t\tcreated_at: op.created_at,\n\t\t\t\t\t\tentity_type: op.entity_type,\n\t\t\t\t\t\tentity_id: op.entity_id,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tskipped_count: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tallowed.push(op);\n\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t}\n\n\tif (firstSkipped) {\n\t\tfirstSkipped.skipped_count = skippedCount;\n\t}\n\n\treturn [allowed, nextCursor, firstSkipped];\n}\n\nexport function filterReplicationOpsForSync(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tpeerDeviceId: string | null,\n): [ReplicationOp[], string | null] {\n\tconst [allowed, nextCursor] = filterReplicationOpsForSyncWithStatus(db, ops, peerDeviceId);\n\treturn [allowed, nextCursor];\n}\n\n// Apply inbound replication ops\n\nexport interface ApplyResult {\n\tapplied: number;\n\tskipped: number;\n\tconflicts: number;\n\terrors: string[];\n}\n\nconst LEGACY_IMPORT_KEY_OLD_RE = /^legacy:memory_item:(.+)$/;\n\n/**\n * Rewrite legacy import keys into globally unique, device-scoped keys.\n *\n * Older databases may contain keys like `legacy:memory_item:<id>`, which can\n * collide across peers. This rewrites them to\n * `legacy:<device_id>:memory_item:<id>`.\n */\nexport function migrateLegacyImportKeys(db: Database, limit = 2000): number {\n\tconst d = drizzle(db, { schema });\n\tconst deviceRow = d\n\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst localDeviceId = String(deviceRow?.device_id ?? \"\").trim();\n\tif (!localDeviceId) return 0;\n\n\tconst rows = d\n\t\t.select({\n\t\t\tid: schema.memoryItems.id,\n\t\t\timport_key: schema.memoryItems.import_key,\n\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t})\n\t\t.from(schema.memoryItems)\n\t\t.where(\n\t\t\tor(\n\t\t\t\tisNull(schema.memoryItems.import_key),\n\t\t\t\tsql`TRIM(${schema.memoryItems.import_key}) = ''`,\n\t\t\t\tlike(schema.memoryItems.import_key, \"legacy:memory_item:%\"),\n\t\t\t),\n\t\t)\n\t\t.orderBy(schema.memoryItems.id)\n\t\t.limit(limit)\n\t\t.all();\n\n\tif (rows.length === 0) return 0;\n\n\tlet updated = 0;\n\tfor (const row of rows) {\n\t\tconst memoryId = Number(row.id);\n\t\tconst current = String(row.import_key ?? \"\").trim();\n\t\tconst metadata = fromJson(row.metadata_json);\n\t\tconst clockDeviceId =\n\t\t\ttypeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id.trim() : \"\";\n\n\t\tlet canonical = \"\";\n\t\tif (!current) {\n\t\t\tcanonical = `legacy:${localDeviceId}:memory_item:${memoryId}`;\n\t\t} else {\n\t\t\tconst match = current.match(LEGACY_IMPORT_KEY_OLD_RE);\n\t\t\tif (!match) continue;\n\t\t\tconst suffix = match[1] ?? \"\";\n\t\t\tconst origin = clockDeviceId && clockDeviceId !== \"local\" ? clockDeviceId : localDeviceId;\n\t\t\tcanonical = origin ? `legacy:${origin}:memory_item:${suffix}` : \"\";\n\t\t}\n\n\t\tif (!canonical || canonical === current) continue;\n\n\t\tconst existing = d\n\t\t\t.select({ id: schema.memoryItems.id })\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(eq(schema.memoryItems.import_key, canonical))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (existing && Number(existing.id) !== memoryId) {\n\t\t\tcontinue;\n\t\t}\n\n\t\td.update(schema.memoryItems)\n\t\t\t.set({ import_key: canonical })\n\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t.run();\n\t\tupdated++;\n\t}\n\n\treturn updated;\n}\n\n/**\n * Generate replication ops for rows that predate replication.\n *\n * Prioritizes delete/tombstone ops first, then active upserts, and only\n * generates ops when a matching op for the same entity/rev/op_type is missing.\n */\nexport function backfillReplicationOps(db: Database, limit = 200): number {\n\tif (limit <= 0) return 0;\n\n\tmigrateLegacyImportKeys(db, 2000);\n\n\tconst d = drizzle(db, { schema });\n\tconst deviceRow = d\n\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst localDeviceId = String(deviceRow?.device_id ?? \"\").trim();\n\n\tconst deletedRows = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(\n\t\t\tand(\n\t\t\t\tor(isNotNull(schema.memoryItems.deleted_at), eq(schema.memoryItems.active, 0)),\n\t\t\t\tsql`NOT EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM replication_ops ro\n\t\t\t\t\tWHERE ro.entity_type = 'memory_item'\n\t\t\t\t\t AND ro.entity_id = ${schema.memoryItems.import_key}\n\t\t\t\t\t AND ro.op_type = 'delete'\n\t\t\t\t\t AND ro.clock_rev = COALESCE(${schema.memoryItems.rev}, 0)\n\t\t\t\t)`,\n\t\t\t),\n\t\t)\n\t\t.orderBy(schema.memoryItems.updated_at)\n\t\t.limit(limit)\n\t\t.all();\n\n\tconst remaining = Math.max(0, limit - deletedRows.length);\n\tlet rows = deletedRows;\n\tif (remaining > 0) {\n\t\tconst upsertRows = d\n\t\t\t.select()\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNull(schema.memoryItems.deleted_at),\n\t\t\t\t\teq(schema.memoryItems.active, 1),\n\t\t\t\t\tsql`NOT EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM replication_ops ro\n\t\t\t\t\t\tWHERE ro.entity_type = 'memory_item'\n\t\t\t\t\t\t AND ro.entity_id = ${schema.memoryItems.import_key}\n\t\t\t\t\t\t AND ro.op_type = 'upsert'\n\t\t\t\t\t\t AND ro.clock_rev = COALESCE(${schema.memoryItems.rev}, 0)\n\t\t\t\t\t)`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.memoryItems.updated_at)\n\t\t\t.limit(remaining)\n\t\t\t.all();\n\t\trows = [...rows, ...upsertRows];\n\t}\n\n\tif (rows.length === 0) return 0;\n\n\tconst now = new Date().toISOString();\n\tlet inserted = 0;\n\n\tfor (const row of rows) {\n\t\tconst rowId = Number(row.id ?? 0);\n\t\tif (!rowId) continue;\n\n\t\tconst metadata =\n\t\t\ttypeof row.metadata_json === \"string\"\n\t\t\t\t? fromJson(row.metadata_json)\n\t\t\t\t: fromJsonNullableLike(row.metadata_json);\n\t\tconst metadataClockDeviceId =\n\t\t\ttypeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id.trim() : \"\";\n\t\tconst originDeviceId =\n\t\t\tmetadataClockDeviceId && metadataClockDeviceId !== \"local\"\n\t\t\t\t? metadataClockDeviceId\n\t\t\t\t: localDeviceId;\n\n\t\tlet importKey = String(row.import_key ?? \"\").trim();\n\t\tif (!importKey) {\n\t\t\tif (!originDeviceId) continue;\n\t\t\timportKey = `legacy:${originDeviceId}:memory_item:${rowId}`;\n\t\t\td.update(schema.memoryItems)\n\t\t\t\t.set({ import_key: importKey })\n\t\t\t\t.where(eq(schema.memoryItems.id, rowId))\n\t\t\t\t.run();\n\t\t}\n\n\t\tconst rev = Number(row.rev ?? 0);\n\t\tconst active = Number(row.active ?? 1);\n\t\tconst deletedAt = String(row.deleted_at ?? \"\").trim();\n\t\tconst opType: \"upsert\" | \"delete\" = deletedAt || active === 0 ? \"delete\" : \"upsert\";\n\t\tconst opId = `backfill:memory_item:${importKey}:${rev}:${opType}`;\n\n\t\tconst exists = d\n\t\t\t.select({ one: sql<number>`1` })\n\t\t\t.from(schema.replicationOps)\n\t\t\t.where(eq(schema.replicationOps.op_id, opId))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (exists) continue;\n\n\t\tconst clockDeviceId = originDeviceId;\n\t\tif (!clockDeviceId) continue;\n\t\tconst payload = buildPayloadFromMemoryRow(row);\n\n\t\td.insert(schema.replicationOps)\n\t\t\t.values({\n\t\t\t\top_id: opId,\n\t\t\t\tentity_type: \"memory_item\",\n\t\t\t\tentity_id: importKey,\n\t\t\t\top_type: opType,\n\t\t\t\tpayload_json: toJson(payload),\n\t\t\t\tclock_rev: rev,\n\t\t\t\tclock_updated_at: String(row.updated_at ?? now),\n\t\t\t\tclock_device_id: clockDeviceId,\n\t\t\t\tdevice_id: clockDeviceId,\n\t\t\t\tcreated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoNothing()\n\t\t\t.run();\n\n\t\tinserted++;\n\t}\n\n\treturn inserted;\n}\n\nfunction fromJsonNullableLike(value: unknown): Record<string, unknown> {\n\tif (typeof value === \"string\") return fromJson(value);\n\tif (value && typeof value === \"object\") return value as Record<string, unknown>;\n\treturn {};\n}\n\nfunction buildPayloadFromMemoryRow(row: MemoryItemRow): MemoryPayload {\n\tconst parseSqliteJson = (val: unknown): unknown => {\n\t\tif (typeof val !== \"string\") return val ?? null;\n\t\ttry {\n\t\t\treturn JSON.parse(val);\n\t\t} catch {\n\t\t\treturn val;\n\t\t}\n\t};\n\tconst metadataParsed = parseSqliteJson(row.metadata_json);\n\tconst metadataJson =\n\t\tmetadataParsed && typeof metadataParsed === \"object\" && !Array.isArray(metadataParsed)\n\t\t\t? (metadataParsed as Record<string, unknown>)\n\t\t\t: {};\n\n\treturn {\n\t\tsession_id: row.session_id ?? null,\n\t\tkind: row.kind ?? null,\n\t\ttitle: row.title ?? null,\n\t\tsubtitle: row.subtitle ?? null,\n\t\tbody_text: row.body_text ?? null,\n\t\tconfidence: row.confidence ?? null,\n\t\ttags_text: row.tags_text ?? null,\n\t\tactive: row.active ?? null,\n\t\tcreated_at: row.created_at ?? null,\n\t\tupdated_at: row.updated_at ?? null,\n\t\tmetadata_json: metadataJson,\n\t\tactor_id: row.actor_id ?? null,\n\t\tactor_display_name: row.actor_display_name ?? null,\n\t\tvisibility: row.visibility ?? null,\n\t\tworkspace_id: row.workspace_id ?? null,\n\t\tworkspace_kind: row.workspace_kind ?? null,\n\t\torigin_device_id: row.origin_device_id ?? null,\n\t\torigin_source: row.origin_source ?? null,\n\t\ttrust_state: row.trust_state ?? null,\n\t\tnarrative: row.narrative ?? null,\n\t\tfacts: parseSqliteJson(row.facts),\n\t\tconcepts: parseSqliteJson(row.concepts),\n\t\tfiles_read: parseSqliteJson(row.files_read),\n\t\tfiles_modified: parseSqliteJson(row.files_modified),\n\t\tuser_prompt_id: row.user_prompt_id ?? null,\n\t\tprompt_number: row.prompt_number ?? null,\n\t\tdeleted_at: row.deleted_at ?? null,\n\t\trev: row.rev ?? 0,\n\t\timport_key: row.import_key ?? null,\n\t};\n}\n\n/**\n * Apply inbound replication ops from a remote peer.\n *\n * Runs all ops in a single transaction. For each op:\n * - Skips if device_id matches localDeviceId (don't re-apply own ops)\n * - Skips if op_id already exists (idempotent)\n * - For upsert: finds existing memory by import_key; if existing has a newer\n * clock, counts as conflict and skips; otherwise INSERT or UPDATE\n * - For delete: soft-deletes (active=0, deleted_at) by import_key\n * - Records the applied op in replication_ops\n */\nexport function applyReplicationOps(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tlocalDeviceId: string,\n): ApplyResult {\n\tconst d = drizzle(db, { schema });\n\tconst result: ApplyResult = { applied: 0, skipped: 0, conflicts: 0, errors: [] };\n\n\tconst applyAll = db.transaction(() => {\n\t\tfor (const op of ops) {\n\t\t\ttry {\n\t\t\t\t// Skip own ops\n\t\t\t\tif (op.device_id === localDeviceId) {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Idempotent: skip if already applied\n\t\t\t\tconst existing = d\n\t\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t\t.from(schema.replicationOps)\n\t\t\t\t\t.where(eq(schema.replicationOps.op_id, op.op_id))\n\t\t\t\t\t.get();\n\t\t\t\tif (existing) {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (op.op_type === \"upsert\") {\n\t\t\t\t\tconst importKey = op.entity_id;\n\t\t\t\t\tconst memRow = d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tid: schema.memoryItems.id,\n\t\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\t\tupdated_at: schema.memoryItems.updated_at,\n\t\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t.get();\n\n\t\t\t\t\tif (memRow) {\n\t\t\t\t\t\tconst existingClockDeviceId = clockDeviceIdFromMetadataJson(memRow.metadata_json);\n\t\t\t\t\t\tconst existingClock = clockTuple(\n\t\t\t\t\t\t\tmemRow.rev ?? 0,\n\t\t\t\t\t\t\tmemRow.updated_at ?? \"\",\n\t\t\t\t\t\t\texistingClockDeviceId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst opClock = clockTuple(op.clock_rev, op.clock_updated_at, op.clock_device_id);\n\n\t\t\t\t\t\tif (!isNewerClock(opClock, existingClock)) {\n\t\t\t\t\t\t\tresult.conflicts++;\n\t\t\t\t\t\t\t// Still record the op so we don't re-process it\n\t\t\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update existing row from payload — skip if malformed\n\t\t\t\t\t\tconst payload = parseMemoryPayload(op, result.errors);\n\t\t\t\t\t\tif (!payload) continue;\n\t\t\t\t\t\tconst metaObj = mergePayloadMetadata(payload.metadata_json, op.clock_device_id);\n\n\t\t\t\t\t\td.update(schema.memoryItems)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tkind: sql`COALESCE(${payload.kind}, ${schema.memoryItems.kind})`,\n\t\t\t\t\t\t\t\ttitle: sql`COALESCE(${payload.title}, ${schema.memoryItems.title})`,\n\t\t\t\t\t\t\t\tsubtitle: payload.subtitle,\n\t\t\t\t\t\t\t\tbody_text: sql`COALESCE(${payload.body_text}, ${schema.memoryItems.body_text})`,\n\t\t\t\t\t\t\t\tconfidence: sql`COALESCE(${payload.confidence != null ? Number(payload.confidence) : null}, ${schema.memoryItems.confidence})`,\n\t\t\t\t\t\t\t\ttags_text: sql`COALESCE(${payload.tags_text}, ${schema.memoryItems.tags_text})`,\n\t\t\t\t\t\t\t\tactive: sql`COALESCE(${payload.active != null ? Number(payload.active) : null}, ${schema.memoryItems.active})`,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t\tmetadata_json: toJson(metaObj),\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tdeleted_at: payload.deleted_at,\n\t\t\t\t\t\t\t\tactor_id: sql`COALESCE(${payload.actor_id}, ${schema.memoryItems.actor_id})`,\n\t\t\t\t\t\t\t\tactor_display_name: sql`COALESCE(${payload.actor_display_name}, ${schema.memoryItems.actor_display_name})`,\n\t\t\t\t\t\t\t\tvisibility: sql`COALESCE(${payload.visibility}, ${schema.memoryItems.visibility})`,\n\t\t\t\t\t\t\t\tworkspace_id: sql`COALESCE(${payload.workspace_id}, ${schema.memoryItems.workspace_id})`,\n\t\t\t\t\t\t\t\tworkspace_kind: sql`COALESCE(${payload.workspace_kind}, ${schema.memoryItems.workspace_kind})`,\n\t\t\t\t\t\t\t\torigin_device_id: sql`COALESCE(${payload.origin_device_id}, ${schema.memoryItems.origin_device_id})`,\n\t\t\t\t\t\t\t\torigin_source: sql`COALESCE(${payload.origin_source}, ${schema.memoryItems.origin_source})`,\n\t\t\t\t\t\t\t\ttrust_state: sql`COALESCE(${payload.trust_state}, ${schema.memoryItems.trust_state})`,\n\t\t\t\t\t\t\t\tnarrative: payload.narrative,\n\t\t\t\t\t\t\t\tfacts: toJsonNullable(payload.facts),\n\t\t\t\t\t\t\t\tconcepts: toJsonNullable(payload.concepts),\n\t\t\t\t\t\t\t\tfiles_read: toJsonNullable(payload.files_read),\n\t\t\t\t\t\t\t\tfiles_modified: toJsonNullable(payload.files_modified),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Insert new memory item — skip if malformed\n\t\t\t\t\t\tconst payload = parseMemoryPayload(op, result.errors);\n\t\t\t\t\t\tif (!payload) continue;\n\t\t\t\t\t\tconst sessionId = ensureSessionForReplication(d, null, op.clock_updated_at);\n\t\t\t\t\t\tconst metaObj = mergePayloadMetadata(payload.metadata_json, op.clock_device_id);\n\n\t\t\t\t\t\td.insert(schema.memoryItems)\n\t\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\t\tsession_id: sessionId,\n\t\t\t\t\t\t\t\tkind: payload.kind ?? \"discovery\",\n\t\t\t\t\t\t\t\ttitle: payload.title ?? \"\",\n\t\t\t\t\t\t\t\tsubtitle: payload.subtitle,\n\t\t\t\t\t\t\t\tbody_text: payload.body_text ?? \"\",\n\t\t\t\t\t\t\t\tconfidence: payload.confidence != null ? Number(payload.confidence) : 0.5,\n\t\t\t\t\t\t\t\ttags_text: payload.tags_text ?? \"\",\n\t\t\t\t\t\t\t\tactive: payload.active != null ? Number(payload.active) : 1,\n\t\t\t\t\t\t\t\tcreated_at: payload.created_at ?? op.clock_updated_at,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t\tmetadata_json: toJson(metaObj),\n\t\t\t\t\t\t\t\timport_key: importKey,\n\t\t\t\t\t\t\t\tdeleted_at: payload.deleted_at,\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tactor_id: payload.actor_id,\n\t\t\t\t\t\t\t\tactor_display_name: payload.actor_display_name,\n\t\t\t\t\t\t\t\tvisibility: payload.visibility,\n\t\t\t\t\t\t\t\tworkspace_id: payload.workspace_id,\n\t\t\t\t\t\t\t\tworkspace_kind: payload.workspace_kind,\n\t\t\t\t\t\t\t\torigin_device_id: payload.origin_device_id,\n\t\t\t\t\t\t\t\torigin_source: payload.origin_source,\n\t\t\t\t\t\t\t\ttrust_state: payload.trust_state,\n\t\t\t\t\t\t\t\tnarrative: payload.narrative,\n\t\t\t\t\t\t\t\tfacts: toJsonNullable(payload.facts),\n\t\t\t\t\t\t\t\tconcepts: toJsonNullable(payload.concepts),\n\t\t\t\t\t\t\t\tfiles_read: toJsonNullable(payload.files_read),\n\t\t\t\t\t\t\t\tfiles_modified: toJsonNullable(payload.files_modified),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t}\n\t\t\t\t} else if (op.op_type === \"delete\") {\n\t\t\t\t\tconst importKey = op.entity_id;\n\t\t\t\t\tconst existingForDelete = d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tid: schema.memoryItems.id,\n\t\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\t\tupdated_at: schema.memoryItems.updated_at,\n\t\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t.limit(1)\n\t\t\t\t\t\t.get();\n\n\t\t\t\t\tif (existingForDelete) {\n\t\t\t\t\t\t// Clock-compare: only delete if the incoming op is newer\n\t\t\t\t\t\tconst existingClock = clockTuple(\n\t\t\t\t\t\t\texistingForDelete.rev ?? 1,\n\t\t\t\t\t\t\texistingForDelete.updated_at ?? \"\",\n\t\t\t\t\t\t\tclockDeviceIdFromMetadataJson(existingForDelete.metadata_json),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst incomingClock = clockTuple(op.clock_rev, op.clock_updated_at, op.clock_device_id);\n\t\t\t\t\t\tif (!isNewerClock(incomingClock, existingClock)) {\n\t\t\t\t\t\t\tresult.conflicts++;\n\t\t\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\t\t\td.update(schema.memoryItems)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tactive: 0,\n\t\t\t\t\t\t\t\tdeleted_at: sql`COALESCE(${schema.memoryItems.deleted_at}, ${now})`,\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(eq(schema.memoryItems.id, existingForDelete.id))\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t}\n\t\t\t\t\t// If import_key not found, record the op as a tombstone for future resolution\n\t\t\t\t} else {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Record the applied op\n\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\tresult.applied++;\n\t\t\t} catch (err) {\n\t\t\t\tresult.errors.push(`op ${op.op_id}: ${err instanceof Error ? err.message : String(err)}`);\n\t\t\t}\n\t\t}\n\t});\n\n\tapplyAll();\n\treturn result;\n}\n\n/** Insert a replication op row into the replication_ops table (ignore on conflict). */\nfunction insertReplicationOpRow(d: ReturnType<typeof drizzle>, op: ReplicationOp): void {\n\td.insert(schema.replicationOps)\n\t\t.values({\n\t\t\top_id: op.op_id,\n\t\t\tentity_type: op.entity_type,\n\t\t\tentity_id: op.entity_id,\n\t\t\top_type: op.op_type,\n\t\t\tpayload_json: op.payload_json,\n\t\t\tclock_rev: op.clock_rev,\n\t\t\tclock_updated_at: op.clock_updated_at,\n\t\t\tclock_device_id: op.clock_device_id,\n\t\t\tdevice_id: op.device_id,\n\t\t\tcreated_at: op.created_at,\n\t\t})\n\t\t.onConflictDoNothing()\n\t\t.run();\n}\n\n/**\n * Ensure a session row exists for replication inserts.\n * Creates a minimal session if one doesn't exist yet.\n */\nfunction ensureSessionForReplication(\n\td: ReturnType<typeof drizzle>,\n\tsessionId: number | null,\n\tcreatedAt: string,\n): number {\n\tif (sessionId != null) {\n\t\tconst row = d\n\t\t\t.select({ id: schema.sessions.id })\n\t\t\t.from(schema.sessions)\n\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t.get();\n\t\tif (row) return sessionId;\n\t}\n\t// Create a new session for replicated data\n\tconst now = createdAt || new Date().toISOString();\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: now,\n\t\t\tuser: \"sync\",\n\t\t\ttool_version: \"sync_replication\",\n\t\t\tmetadata_json: toJson({ source: \"sync\" }),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"session insert returned no id\");\n\treturn id;\n}\n","/**\n * MemoryStore — TypeScript port of codemem/store/_store.py (Phase 1 CRUD).\n *\n * During Phase 1, Python owns DDL/schema. This TS runtime reads and writes data\n * but does NOT create or migrate tables. The assertSchemaReady() call verifies\n * the schema was initialized by Python before any operations.\n *\n * Methods ported: get, remember, forget, recent, recentByKinds, stats,\n * updateMemoryVisibility, close.\n *\n * NOT ported yet: pack, usage tracking, vectors, provenance resolution,\n * memory_owned_by_self check.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { statSync } from \"node:fs\";\nimport { and, desc, eq, gt, inArray, isNotNull, lt, lte, or, type SQL, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport {\n\tassertSchemaReady,\n\tbackupOnFirstAccess,\n\tconnect,\n\tDEFAULT_DB_PATH,\n\tensureAdditiveSchemaCompatibility,\n\tfromJson,\n\tisEmbeddingDisabled,\n\tloadSqliteVec,\n\tresolveDbPath,\n\ttableExists,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nimport { buildFilterClauses } from \"./filters.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport { buildMemoryPack, buildMemoryPackAsync } from \"./pack.js\";\nimport * as schema from \"./schema.js\";\nimport {\n\ttype ExplainOptions,\n\texplain as explainFn,\n\tsearch as searchFn,\n\ttimeline as timelineFn,\n} from \"./search.js\";\nimport { recordReplicationOp } from \"./sync-replication.js\";\nimport type {\n\tExplainResponse,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tPackResponse,\n\tStoreStats,\n\tTimelineItemResponse,\n} from \"./types.js\";\nimport { storeVectors } from \"./vectors.js\";\n\n// Memory kind validation (mirrors codemem/memory_kinds.py)\n\nconst ALLOWED_MEMORY_KINDS = new Set([\n\t\"discovery\",\n\t\"change\",\n\t\"feature\",\n\t\"bugfix\",\n\t\"refactor\",\n\t\"decision\",\n\t\"exploration\",\n]);\n\n/** Normalize and validate a memory kind. Throws on invalid kinds. */\nfunction validateMemoryKind(kind: string): string {\n\tconst normalized = kind.trim().toLowerCase();\n\tif (!ALLOWED_MEMORY_KINDS.has(normalized)) {\n\t\tthrow new Error(\n\t\t\t`Invalid memory kind \"${kind}\". Allowed: ${[...ALLOWED_MEMORY_KINDS].join(\", \")}`,\n\t\t);\n\t}\n\treturn normalized;\n}\n\n// Helpers\n\n/** ISO 8601 timestamp in UTC. */\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction countQuestionPlaceholders(clause: string): number {\n\treturn (clause.match(/\\?/g) ?? []).length;\n}\n\nfunction sqlFromParameterizedClause(clause: string, params: unknown[]): SQL {\n\tconst parts = clause.split(\"?\");\n\tlet acc: SQL = sql.raw(parts[0] ?? \"\");\n\tfor (let i = 1; i < parts.length; i++) {\n\t\tacc = sql`${acc}${params[i - 1]}${sql.raw(parts[i] ?? \"\")}`;\n\t}\n\treturn acc;\n}\n\nfunction buildWhereSql(clauses: string[], params: unknown[]): SQL {\n\tconst sqlClauses: SQL[] = [];\n\tlet cursor = 0;\n\tfor (const clause of clauses) {\n\t\tconst count = countQuestionPlaceholders(clause);\n\t\tconst clauseParams = params.slice(cursor, cursor + count);\n\t\tsqlClauses.push(sqlFromParameterizedClause(clause, clauseParams));\n\t\tcursor += count;\n\t}\n\tif (cursor !== params.length) {\n\t\tthrow new Error(\"filter parameter mismatch while building SQL clauses\");\n\t}\n\tif (sqlClauses.length === 1) return sqlClauses[0] ?? sql`1=1`;\n\tconst combined = and(...sqlClauses);\n\tif (!combined) throw new Error(\"failed to combine filter SQL clauses\");\n\treturn combined;\n}\n\n/** Trim a string value, returning null for empty/non-string. Matches Python's _clean_optional_str. */\nfunction cleanStr(value: unknown): string | null {\n\tif (value == null || typeof value !== \"string\") return null;\n\tconst trimmed = value.trim();\n\treturn trimmed.length > 0 ? trimmed : null;\n}\n\nfunction parseJsonList(value: unknown): string[] {\n\tif (typeof value !== \"string\" || !value.trim()) return [];\n\ttry {\n\t\tconst parsed = JSON.parse(value);\n\t\treturn Array.isArray(parsed)\n\t\t\t? parsed\n\t\t\t\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t\t.map((item) => item.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction projectBasename(value: string | null | undefined): string {\n\tconst raw = cleanStr(value);\n\tif (!raw) return \"\";\n\tconst parts = raw.replace(/\\\\/g, \"/\").split(\"/\").filter(Boolean);\n\treturn parts[parts.length - 1] ?? raw;\n}\n\nconst LEGACY_SYNC_ACTOR_DISPLAY_NAME = \"Legacy synced peer\";\nconst LEGACY_SHARED_WORKSPACE_ID = \"shared:legacy\";\n\n/**\n * Parse a row's metadata_json string into a plain object.\n * Returns a new MemoryItemResponse with metadata_json as a parsed object.\n */\nfunction parseMetadata(row: MemoryItem): MemoryItemResponse {\n\tconst { metadata_json, ...rest } = row;\n\treturn { ...rest, metadata_json: fromJson(metadata_json) };\n}\n\n// MemoryStore\n\nexport class MemoryStore {\n\treadonly db: Database;\n\treadonly dbPath: string;\n\treadonly deviceId: string;\n\treadonly actorId: string;\n\treadonly actorDisplayName: string;\n\tprivate readonly pendingVectorWrites = new Set<Promise<void>>();\n\n\t/** Lazy Drizzle ORM wrapper — shares the same better-sqlite3 connection. */\n\tprivate _drizzle: ReturnType<typeof drizzle> | null = null;\n\tprivate get d() {\n\t\tif (!this._drizzle) this._drizzle = drizzle(this.db, { schema });\n\t\treturn this._drizzle;\n\t}\n\n\tconstructor(dbPath: string = DEFAULT_DB_PATH) {\n\t\tthis.dbPath = resolveDbPath(dbPath);\n\t\tbackupOnFirstAccess(this.dbPath);\n\t\tthis.db = connect(this.dbPath);\n\t\ttry {\n\t\t\tloadSqliteVec(this.db);\n\t\t\tassertSchemaReady(this.db);\n\t\t\tensureAdditiveSchemaCompatibility(this.db);\n\t\t} catch (err) {\n\t\t\tthis.db.close();\n\t\t\tthrow err;\n\t\t}\n\n\t\t// Resolve device ID: env var → sync_device table → stable \"local\" fallback.\n\t\t// Python uses exactly this order and fallback.\n\t\tconst envDeviceId = process.env.CODEMEM_DEVICE_ID?.trim();\n\t\tif (envDeviceId) {\n\t\t\tthis.deviceId = envDeviceId;\n\t\t} else {\n\t\t\t// Guard: sync_device may not exist in older/minimal schemas\n\t\t\tlet dbDeviceId: string | undefined;\n\t\t\ttry {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t\t\t\t.from(schema.syncDevice)\n\t\t\t\t\t.limit(1)\n\t\t\t\t\t.get();\n\t\t\t\tdbDeviceId = row?.device_id;\n\t\t\t} catch {\n\t\t\t\t// Table doesn't exist — fall through to stable default\n\t\t\t}\n\t\t\tthis.deviceId = dbDeviceId ?? \"local\";\n\t\t}\n\n\t\t// Resolve actor identity — matches Python load_config() precedence:\n\t\t// config file, then env override, then local fallbacks.\n\t\tconst config = readCodememConfigFile();\n\t\tconst configActorId = Object.hasOwn(process.env, \"CODEMEM_ACTOR_ID\")\n\t\t\t? cleanStr(process.env.CODEMEM_ACTOR_ID)\n\t\t\t: (cleanStr(config.actor_id) ?? null);\n\t\tthis.actorId = configActorId || `local:${this.deviceId}`;\n\n\t\tconst configDisplayName = Object.hasOwn(process.env, \"CODEMEM_ACTOR_DISPLAY_NAME\")\n\t\t\t? cleanStr(process.env.CODEMEM_ACTOR_DISPLAY_NAME)\n\t\t\t: (cleanStr(config.actor_display_name) ?? null);\n\t\tthis.actorDisplayName =\n\t\t\tconfigDisplayName || process.env.USER?.trim() || process.env.USERNAME?.trim() || this.actorId;\n\t}\n\n\tprivate enqueueVectorWrite(memoryId: number, title: string, bodyText: string): void {\n\t\tif (this.db.inTransaction) return;\n\t\tlet op: Promise<void> | null = null;\n\t\top = storeVectors(this.db, memoryId, title, bodyText)\n\t\t\t.catch(() => {\n\t\t\t\t// Non-fatal — keep memory writes resilient when embeddings are unavailable\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (op) this.pendingVectorWrites.delete(op);\n\t\t\t});\n\t\tthis.pendingVectorWrites.add(op);\n\t}\n\n\tasync flushPendingVectorWrites(): Promise<void> {\n\t\tif (this.pendingVectorWrites.size === 0) return;\n\t\tawait Promise.allSettled([...this.pendingVectorWrites]);\n\t}\n\n\t// get\n\n\t/**\n\t * Fetch a single memory item by ID.\n\t * Returns null if not found (does not filter by active status).\n\t */\n\tget(memoryId: number): MemoryItemResponse | null {\n\t\tconst row = this.d\n\t\t\t.select()\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t.get() as MemoryItem | undefined;\n\t\tif (!row) return null;\n\t\treturn parseMetadata(row);\n\t}\n\n\t// startSession / endSession\n\n\t/**\n\t * Create a new session row. Returns the session ID.\n\t * Matches Python's store.start_session().\n\t */\n\tstartSession(opts: {\n\t\tcwd?: string;\n\t\tproject?: string | null;\n\t\tgitRemote?: string | null;\n\t\tgitBranch?: string | null;\n\t\tuser?: string;\n\t\ttoolVersion?: string;\n\t\tmetadata?: Record<string, unknown>;\n\t}): number {\n\t\tconst now = nowIso();\n\t\tconst rows = this.d\n\t\t\t.insert(schema.sessions)\n\t\t\t.values({\n\t\t\t\tstarted_at: now,\n\t\t\t\tcwd: opts.cwd ?? process.cwd(),\n\t\t\t\tproject: opts.project ?? null,\n\t\t\t\tgit_remote: opts.gitRemote ?? null,\n\t\t\t\tgit_branch: opts.gitBranch ?? null,\n\t\t\t\tuser: opts.user ?? process.env.USER ?? \"unknown\",\n\t\t\t\ttool_version: opts.toolVersion ?? \"manual\",\n\t\t\t\tmetadata_json: toJson(opts.metadata ?? {}),\n\t\t\t})\n\t\t\t.returning({ id: schema.sessions.id })\n\t\t\t.all();\n\t\tconst id = rows[0]?.id;\n\t\tif (id == null) throw new Error(\"session insert returned no id\");\n\t\treturn id;\n\t}\n\n\t/**\n\t * End a session by recording ended_at.\n\t * FIX: merges incoming metadata with existing instead of replacing.\n\t * No-op if session doesn't exist.\n\t */\n\tendSession(sessionId: number, metadata?: Record<string, unknown>): void {\n\t\tconst now = nowIso();\n\t\tif (metadata) {\n\t\t\t// Read existing metadata and merge — prevents clobbering earlier fields\n\t\t\tconst existing = this.d\n\t\t\t\t.select({ metadata_json: schema.sessions.metadata_json })\n\t\t\t\t.from(schema.sessions)\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.get();\n\t\t\tconst merged = { ...fromJson(existing?.metadata_json), ...metadata };\n\t\t\tthis.d\n\t\t\t\t.update(schema.sessions)\n\t\t\t\t.set({ ended_at: now, metadata_json: toJson(merged) })\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.run();\n\t\t} else {\n\t\t\tthis.d\n\t\t\t\t.update(schema.sessions)\n\t\t\t\t.set({ ended_at: now })\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.run();\n\t\t}\n\t}\n\n\t// remember\n\n\t/**\n\t * Create a new memory item. Returns the new memory ID.\n\t *\n\t * Validates and normalizes the kind. Resolves provenance fields (actor_id,\n\t * visibility, workspace_id, trust_state) matching Python's\n\t * _resolve_memory_provenance logic.\n\t */\n\tremember(\n\t\tsessionId: number,\n\t\tkind: string,\n\t\ttitle: string,\n\t\tbodyText: string,\n\t\tconfidence = 0.5,\n\t\ttags?: string[],\n\t\tmetadata?: Record<string, unknown>,\n\t): number {\n\t\tconst validKind = validateMemoryKind(kind);\n\t\tconst now = nowIso();\n\t\tconst tagsText = tags ? [...new Set(tags)].sort().join(\" \") : \"\";\n\t\tconst metaPayload = { ...(metadata ?? {}) };\n\n\t\tmetaPayload.clock_device_id ??= this.deviceId;\n\t\tconst importKey = (metaPayload.import_key as string) || randomUUID();\n\t\tmetaPayload.import_key = importKey;\n\n\t\t// Extract dedicated columns from metadata before they get buried in metadata_json\n\t\tconst subtitle = typeof metaPayload.subtitle === \"string\" ? metaPayload.subtitle : null;\n\t\tconst narrative = typeof metaPayload.narrative === \"string\" ? metaPayload.narrative : null;\n\t\tconst facts = Array.isArray(metaPayload.facts) ? metaPayload.facts : null;\n\t\tconst concepts = Array.isArray(metaPayload.concepts) ? metaPayload.concepts : null;\n\t\tconst filesRead = Array.isArray(metaPayload.files_read) ? metaPayload.files_read : null;\n\t\tconst filesModified = Array.isArray(metaPayload.files_modified)\n\t\t\t? metaPayload.files_modified\n\t\t\t: null;\n\t\tconst promptNumber =\n\t\t\ttypeof metaPayload.prompt_number === \"number\" ? metaPayload.prompt_number : null;\n\t\tconst userPromptId =\n\t\t\ttypeof metaPayload.user_prompt_id === \"number\" ? metaPayload.user_prompt_id : null;\n\n\t\t// Resolve provenance fields\n\t\tconst provenance = this.resolveProvenance(metaPayload);\n\n\t\tconst rows = this.d\n\t\t\t.insert(schema.memoryItems)\n\t\t\t.values({\n\t\t\t\tsession_id: sessionId,\n\t\t\t\tkind: validKind,\n\t\t\t\ttitle,\n\t\t\t\tsubtitle,\n\t\t\t\tbody_text: bodyText,\n\t\t\t\tconfidence,\n\t\t\t\ttags_text: tagsText,\n\t\t\t\tactive: 1,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t\tmetadata_json: toJson(metaPayload),\n\t\t\t\tactor_id: provenance.actor_id,\n\t\t\t\tactor_display_name: provenance.actor_display_name,\n\t\t\t\tvisibility: provenance.visibility,\n\t\t\t\tworkspace_id: provenance.workspace_id,\n\t\t\t\tworkspace_kind: provenance.workspace_kind,\n\t\t\t\torigin_device_id: provenance.origin_device_id,\n\t\t\t\torigin_source: provenance.origin_source,\n\t\t\t\ttrust_state: provenance.trust_state,\n\t\t\t\tnarrative,\n\t\t\t\tfacts: toJsonNullable(facts),\n\t\t\t\tconcepts: toJsonNullable(concepts),\n\t\t\t\tfiles_read: toJsonNullable(filesRead),\n\t\t\t\tfiles_modified: toJsonNullable(filesModified),\n\t\t\t\tprompt_number: promptNumber,\n\t\t\t\tuser_prompt_id: userPromptId,\n\t\t\t\tdeleted_at: null,\n\t\t\t\trev: 1,\n\t\t\t\timport_key: importKey,\n\t\t\t})\n\t\t\t.returning({ id: schema.memoryItems.id })\n\t\t\t.all();\n\n\t\tconst memoryId = rows[0]?.id;\n\t\tif (memoryId == null) throw new Error(\"memory insert returned no id\");\n\n\t\t// Record replication op for sync propagation\n\t\ttry {\n\t\t\trecordReplicationOp(this.db, { memoryId, opType: \"upsert\", deviceId: this.deviceId });\n\t\t} catch {\n\t\t\t// Non-fatal — don't block memory creation\n\t\t}\n\n\t\tthis.enqueueVectorWrite(memoryId, title, bodyText);\n\n\t\treturn memoryId;\n\t}\n\n\t// provenance resolution\n\n\t/**\n\t * Resolve provenance fields for a new memory, matching Python's\n\t * _resolve_memory_provenance. Uses metadata overrides when present,\n\t * falls back to store-level defaults.\n\t */\n\tprivate resolveProvenance(metadata: Record<string, unknown>): {\n\t\tactor_id: string | null;\n\t\tactor_display_name: string | null;\n\t\tvisibility: string;\n\t\tworkspace_id: string;\n\t\tworkspace_kind: string;\n\t\torigin_device_id: string;\n\t\torigin_source: string | null;\n\t\ttrust_state: string;\n\t} {\n\t\tconst clean = (v: unknown): string | null => {\n\t\t\tif (v == null) return null;\n\t\t\tconst s = String(v).trim();\n\t\t\treturn s.length > 0 ? s : null;\n\t\t};\n\n\t\tconst actorId = clean(metadata.actor_id) ?? this.actorId;\n\t\tconst actorDisplayName = clean(metadata.actor_display_name) ?? this.actorDisplayName;\n\n\t\tconst explicitWorkspaceKind = clean(metadata.workspace_kind);\n\t\tconst explicitWorkspaceId = clean(metadata.workspace_id);\n\n\t\t// Visibility defaults to \"shared\" (matches Python behavior)\n\t\tlet visibility = clean(metadata.visibility);\n\t\tif (!visibility || (visibility !== \"private\" && visibility !== \"shared\")) {\n\t\t\tif (explicitWorkspaceKind === \"shared\" || explicitWorkspaceId?.startsWith(\"shared:\")) {\n\t\t\t\tvisibility = \"shared\";\n\t\t\t} else {\n\t\t\t\tvisibility = \"shared\";\n\t\t\t}\n\t\t}\n\n\t\t// Workspace kind derives from visibility\n\t\tlet workspaceKind = explicitWorkspaceKind ?? \"shared\";\n\t\tif (workspaceKind !== \"personal\" && workspaceKind !== \"shared\") {\n\t\t\tworkspaceKind = visibility === \"shared\" ? \"shared\" : \"personal\";\n\t\t} else if (visibility === \"shared\") {\n\t\t\tworkspaceKind = \"shared\";\n\t\t} else if (visibility === \"private\") {\n\t\t\tworkspaceKind = \"personal\";\n\t\t}\n\n\t\t// Workspace ID with fallback — matches Python's _default_workspace_id\n\t\tconst workspaceId =\n\t\t\texplicitWorkspaceId ??\n\t\t\t(workspaceKind === \"personal\" ? `personal:${actorId}` : \"shared:default\");\n\n\t\tconst originDeviceId = clean(metadata.origin_device_id) ?? this.deviceId;\n\t\tconst originSource = clean(metadata.origin_source) ?? clean(metadata.source) ?? null;\n\t\tconst trustState = clean(metadata.trust_state) ?? \"trusted\";\n\n\t\treturn {\n\t\t\tactor_id: actorId,\n\t\t\tactor_display_name: actorDisplayName,\n\t\t\tvisibility,\n\t\t\tworkspace_id: workspaceId,\n\t\t\tworkspace_kind: workspaceKind,\n\t\t\torigin_device_id: originDeviceId,\n\t\t\torigin_source: originSource,\n\t\t\ttrust_state: trustState,\n\t\t};\n\t}\n\n\t// ownership check\n\n\t/**\n\t * Check if a memory item is owned by this actor/device.\n\t * Port of Python's memory_owned_by_self().\n\t *\n\t * Python checks:\n\t * 1. actor_id == self.actor_id → owned\n\t * 2. origin_device_id in claimed_same_actor_peers → owned\n\t * 3. actor_id in legacy sync actor ids → owned\n\t */\n\tmemoryOwnedBySelf(item: MemoryItem | MemoryResult | Record<string, unknown>): boolean {\n\t\tconst rec = item as Record<string, unknown>;\n\t\t// Check top-level columns first (MemoryItem / DB row),\n\t\t// then metadata dict (MemoryResult from search populates provenance there).\n\t\tconst meta = (rec.metadata ?? {}) as Record<string, unknown>;\n\n\t\tconst actorId = cleanStr(rec.actor_id) ?? cleanStr(meta.actor_id);\n\t\tif (actorId === this.actorId) return true;\n\n\t\tconst deviceId = cleanStr(rec.origin_device_id) ?? cleanStr(meta.origin_device_id);\n\t\tif (deviceId === this.deviceId) return true;\n\n\t\tconst claimedPeers = new Set(this.sameActorPeerIds());\n\t\tif (deviceId && claimedPeers.has(deviceId)) return true;\n\n\t\tconst legacyActorIds = new Set(this.claimedSameActorLegacyActorIds());\n\t\tif (actorId && legacyActorIds.has(actorId)) return true;\n\n\t\treturn false;\n\t}\n\n\t// forget\n\n\t/**\n\t * Soft-delete a memory item (set active = 0, record deleted_at).\n\t * Updates metadata_json with clock_device_id for replication tracing.\n\t * No-op if the memory doesn't exist.\n\t */\n\tforget(memoryId: number): void {\n\t\tthis.db\n\t\t\t.transaction(() => {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select({\n\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t})\n\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.get();\n\t\t\t\tif (!row) return;\n\n\t\t\t\tconst meta = fromJson(row.metadata_json);\n\t\t\t\tmeta.clock_device_id = this.deviceId;\n\n\t\t\t\tconst now = nowIso();\n\t\t\t\tconst rev = (row.rev ?? 0) + 1;\n\n\t\t\t\tthis.d\n\t\t\t\t\t.update(schema.memoryItems)\n\t\t\t\t\t.set({\n\t\t\t\t\t\tactive: 0,\n\t\t\t\t\t\tdeleted_at: now,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\tmetadata_json: toJson(meta),\n\t\t\t\t\t\trev,\n\t\t\t\t\t})\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.run();\n\n\t\t\t\ttry {\n\t\t\t\t\trecordReplicationOp(this.db, { memoryId, opType: \"delete\", deviceId: this.deviceId });\n\t\t\t\t} catch {\n\t\t\t\t\t// Non-fatal\n\t\t\t\t}\n\t\t\t})\n\t\t\t.immediate();\n\t}\n\n\t// recent\n\n\t/**\n\t * Return recent active memories, newest first.\n\t * Supports optional filters via buildFilterClauses.\n\t */\n\trecent(limit = 10, filters?: MemoryFilters | null, offset = 0): MemoryItemResponse[] {\n\t\tconst baseClauses = [\"memory_items.active = 1\"];\n\t\tconst filterResult = buildFilterClauses(filters);\n\t\tconst allClauses = [...baseClauses, ...filterResult.clauses];\n\t\tconst whereSql = buildWhereSql(allClauses, filterResult.params);\n\n\t\t// Note: joinSessions is set by the project filter (not yet ported).\n\t\t// Once project filtering lands, it will trigger the sessions JOIN.\n\t\tconst fromSql = filterResult.joinSessions\n\t\t\t? sql.raw(\"memory_items JOIN sessions ON sessions.id = memory_items.session_id\")\n\t\t\t: sql.raw(\"memory_items\");\n\n\t\tconst rows = this.d.all<MemoryItem>(\n\t\t\tsql`SELECT memory_items.* FROM ${fromSql}\n\t\t\t\tWHERE ${whereSql}\n\t\t\t\tORDER BY created_at DESC\n\t\t\t\tLIMIT ${limit} OFFSET ${Math.max(offset, 0)}`,\n\t\t);\n\n\t\treturn rows.map((row) => parseMetadata(row));\n\t}\n\n\t// recentByKinds\n\n\t/**\n\t * Return recent active memories filtered to specific kinds, newest first.\n\t */\n\trecentByKinds(\n\t\tkinds: string[],\n\t\tlimit = 10,\n\t\tfilters?: MemoryFilters | null,\n\t\toffset = 0,\n\t): MemoryItemResponse[] {\n\t\tconst kindsList = kinds.filter((k) => k.length > 0);\n\t\tif (kindsList.length === 0) return [];\n\n\t\tconst kindPlaceholders = kindsList.map(() => \"?\").join(\", \");\n\t\tconst baseClauses = [\"memory_items.active = 1\", `memory_items.kind IN (${kindPlaceholders})`];\n\t\tconst filterResult = buildFilterClauses(filters);\n\t\tconst allClauses = [...baseClauses, ...filterResult.clauses];\n\t\tconst params = [...kindsList, ...filterResult.params];\n\t\tconst whereSql = buildWhereSql(allClauses, params);\n\n\t\tconst fromSql = filterResult.joinSessions\n\t\t\t? sql.raw(\"memory_items JOIN sessions ON sessions.id = memory_items.session_id\")\n\t\t\t: sql.raw(\"memory_items\");\n\n\t\tconst rows = this.d.all<MemoryItem>(\n\t\t\tsql`SELECT memory_items.* FROM ${fromSql}\n\t\t\t\tWHERE ${whereSql}\n\t\t\t\tORDER BY created_at DESC\n\t\t\t\tLIMIT ${limit} OFFSET ${Math.max(offset, 0)}`,\n\t\t);\n\n\t\treturn rows.map((row) => parseMetadata(row));\n\t}\n\n\t// stats\n\n\t/**\n\t * Return database statistics matching the Python stats() output shape.\n\t */\n\tstats(): StoreStats {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Drizzle table union type is unwieldy\n\t\tconst countRows = (tbl: any) =>\n\t\t\tthis.d.select({ c: sql<number>`COUNT(*)` }).from(tbl).get()?.c ?? 0;\n\n\t\tconst totalMemories = countRows(schema.memoryItems);\n\t\tconst activeMemories =\n\t\t\tthis.d\n\t\t\t\t.select({ c: sql<number>`COUNT(*)` })\n\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t.where(eq(schema.memoryItems.active, 1))\n\t\t\t\t.get()?.c ?? 0;\n\t\tconst sessions = countRows(schema.sessions);\n\t\tconst artifacts = countRows(schema.artifacts);\n\t\tconst rawEvents = countRows(schema.rawEvents);\n\n\t\tlet vectorCount = 0;\n\t\tif (!isEmbeddingDisabled() && tableExists(this.db, \"memory_vectors\")) {\n\t\t\ttry {\n\t\t\t\tconst row = this.d.get<{ c: number | null }>(sql`SELECT COUNT(*) AS c FROM memory_vectors`);\n\t\t\t\tvectorCount = row?.c ?? 0;\n\t\t\t} catch {\n\t\t\t\tvectorCount = 0;\n\t\t\t}\n\t\t}\n\t\tconst vectorCoverage = activeMemories > 0 ? Math.min(1, vectorCount / activeMemories) : 0;\n\n\t\tconst tagsFilled =\n\t\t\tthis.d\n\t\t\t\t.select({ c: sql<number>`COUNT(*)` })\n\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t.where(and(eq(schema.memoryItems.active, 1), sql`TRIM(tags_text) != ''`))\n\t\t\t\t.get()?.c ?? 0;\n\t\tconst tagsCoverage = activeMemories > 0 ? Math.min(1, tagsFilled / activeMemories) : 0;\n\n\t\tlet sizeBytes = 0;\n\t\ttry {\n\t\t\tsizeBytes = statSync(this.dbPath).size;\n\t\t} catch {\n\t\t\t// File may not exist yet or be inaccessible\n\t\t}\n\n\t\t// Usage stats\n\t\tconst usageRows = this.d\n\t\t\t.select({\n\t\t\t\tevent: schema.usageEvents.event,\n\t\t\t\tcount: sql<number>`COUNT(*)`,\n\t\t\t\ttokens_read: sql<number | null>`SUM(tokens_read)`,\n\t\t\t\ttokens_written: sql<number | null>`SUM(tokens_written)`,\n\t\t\t\ttokens_saved: sql<number | null>`SUM(COALESCE(tokens_saved, 0))`,\n\t\t\t})\n\t\t\t.from(schema.usageEvents)\n\t\t\t.groupBy(schema.usageEvents.event)\n\t\t\t.orderBy(desc(sql`COUNT(*)`))\n\t\t\t.all();\n\n\t\tconst usageEvents = usageRows.map((r) => ({\n\t\t\tevent: r.event,\n\t\t\tcount: r.count,\n\t\t\ttokens_read: r.tokens_read ?? 0,\n\t\t\ttokens_written: r.tokens_written ?? 0,\n\t\t\ttokens_saved: r.tokens_saved ?? 0,\n\t\t}));\n\n\t\tconst totalEvents = usageEvents.reduce((s, e) => s + e.count, 0);\n\t\tconst totalTokensRead = usageEvents.reduce((s, e) => s + e.tokens_read, 0);\n\t\tconst totalTokensWritten = usageEvents.reduce((s, e) => s + e.tokens_written, 0);\n\t\tconst totalTokensSaved = usageEvents.reduce((s, e) => s + e.tokens_saved, 0);\n\n\t\treturn {\n\t\t\tidentity: {\n\t\t\t\tdevice_id: this.deviceId,\n\t\t\t\tactor_id: this.actorId,\n\t\t\t\tactor_display_name: this.actorDisplayName,\n\t\t\t},\n\t\t\tdatabase: {\n\t\t\t\tpath: this.dbPath,\n\t\t\t\tsize_bytes: sizeBytes,\n\t\t\t\tsessions,\n\t\t\t\tmemory_items: totalMemories,\n\t\t\t\tactive_memory_items: activeMemories,\n\t\t\t\tartifacts,\n\t\t\t\tvector_rows: vectorCount,\n\t\t\t\tvector_coverage: vectorCoverage,\n\t\t\t\ttags_filled: tagsFilled,\n\t\t\t\ttags_coverage: tagsCoverage,\n\t\t\t\traw_events: rawEvents,\n\t\t\t},\n\t\t\tusage: {\n\t\t\t\tevents: usageEvents,\n\t\t\t\ttotals: {\n\t\t\t\t\tevents: totalEvents,\n\t\t\t\t\ttokens_read: totalTokensRead,\n\t\t\t\t\ttokens_written: totalTokensWritten,\n\t\t\t\t\ttokens_saved: totalTokensSaved,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// updateMemoryVisibility\n\n\t/**\n\t * Update the visibility of an active memory item.\n\t * Throws if visibility is invalid, memory not found, memory is inactive,\n\t * or memory is not owned by this device/actor.\n\t */\n\tupdateMemoryVisibility(memoryId: number, visibility: string): MemoryItemResponse {\n\t\tconst cleaned = visibility.trim();\n\t\tif (cleaned !== \"private\" && cleaned !== \"shared\") {\n\t\t\tthrow new Error(\"visibility must be private or shared\");\n\t\t}\n\n\t\treturn this.db\n\t\t\t.transaction(() => {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t.where(and(eq(schema.memoryItems.id, memoryId), eq(schema.memoryItems.active, 1)))\n\t\t\t\t\t.get() as MemoryItem | undefined;\n\t\t\t\tif (!row) {\n\t\t\t\t\tthrow new Error(\"memory not found\");\n\t\t\t\t}\n\n\t\t\t\tif (!this.memoryOwnedBySelf(row)) {\n\t\t\t\t\tthrow new Error(\"memory not owned by this device\");\n\t\t\t\t}\n\n\t\t\t\tconst rowActorId = cleanStr(row.actor_id) ?? this.actorId;\n\t\t\t\tconst workspaceKind = cleaned === \"shared\" ? \"shared\" : \"personal\";\n\t\t\t\tconst workspaceId =\n\t\t\t\t\tcleaned === \"shared\" && row.workspace_id?.startsWith(\"shared:\")\n\t\t\t\t\t\t? row.workspace_id\n\t\t\t\t\t\t: workspaceKind === \"personal\"\n\t\t\t\t\t\t\t? `personal:${rowActorId}`\n\t\t\t\t\t\t\t: \"shared:default\";\n\n\t\t\t\tconst meta = fromJson(row.metadata_json);\n\t\t\t\tmeta.visibility = cleaned;\n\t\t\t\tmeta.workspace_kind = workspaceKind;\n\t\t\t\tmeta.workspace_id = workspaceId;\n\t\t\t\tmeta.clock_device_id = this.deviceId;\n\n\t\t\t\tconst now = nowIso();\n\t\t\t\tconst rev = (row.rev ?? 0) + 1;\n\n\t\t\t\tthis.d\n\t\t\t\t\t.update(schema.memoryItems)\n\t\t\t\t\t.set({\n\t\t\t\t\t\tvisibility: cleaned,\n\t\t\t\t\t\tworkspace_kind: workspaceKind,\n\t\t\t\t\t\tworkspace_id: workspaceId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\tmetadata_json: toJson(meta),\n\t\t\t\t\t\trev,\n\t\t\t\t\t})\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.run();\n\n\t\t\t\ttry {\n\t\t\t\t\trecordReplicationOp(this.db, {\n\t\t\t\t\t\tmemoryId,\n\t\t\t\t\t\topType: \"upsert\",\n\t\t\t\t\t\tdeviceId: this.deviceId,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Non-fatal\n\t\t\t\t}\n\n\t\t\t\tconst updated = this.get(memoryId);\n\t\t\t\tif (!updated) {\n\t\t\t\t\tthrow new Error(\"memory not found after update\");\n\t\t\t\t}\n\t\t\t\treturn updated;\n\t\t\t})\n\t\t\t.immediate();\n\t}\n\n\t// search\n\n\t/**\n\t * Full-text search for memories using FTS5.\n\t *\n\t * Delegates to search.ts to keep the search logic decoupled.\n\t * Results are ranked by BM25 score, recency, and kind bonus.\n\t */\n\tsearch(query: string, limit = 10, filters?: MemoryFilters): MemoryResult[] {\n\t\treturn searchFn(this, query, limit, filters);\n\t}\n\n\t// timeline\n\n\t/**\n\t * Return a chronological window of memories around an anchor.\n\t *\n\t * Finds an anchor by memoryId or query, then fetches neighbors\n\t * in the same session. Delegates to search.ts.\n\t */\n\ttimeline(\n\t\tquery?: string | null,\n\t\tmemoryId?: number | null,\n\t\tdepthBefore = 3,\n\t\tdepthAfter = 3,\n\t\tfilters?: MemoryFilters | null,\n\t): TimelineItemResponse[] {\n\t\treturn timelineFn(this, query, memoryId, depthBefore, depthAfter, filters);\n\t}\n\n\t// explain\n\n\t/**\n\t * Explain search results with scoring breakdown.\n\t *\n\t * Returns detailed scoring components for each result, merging\n\t * query-based and ID-based lookups. Delegates to search.ts.\n\t */\n\texplain(\n\t\tquery?: string | null,\n\t\tids?: unknown[] | null,\n\t\tlimit = 10,\n\t\tfilters?: MemoryFilters | null,\n\t\toptions?: ExplainOptions,\n\t): ExplainResponse {\n\t\treturn explainFn(this, query, ids, limit, filters, options);\n\t}\n\n\t// buildMemoryPack\n\n\t/**\n\t * Build a formatted memory pack from search results.\n\t *\n\t * Categorizes memories into summary/timeline/observations sections,\n\t * with optional token budgeting. Delegates to pack.ts.\n\t */\n\tbuildMemoryPack(\n\t\tcontext: string,\n\t\tlimit?: number,\n\t\ttokenBudget?: number | null,\n\t\tfilters?: MemoryFilters,\n\t): PackResponse {\n\t\treturn buildMemoryPack(this, context, limit, tokenBudget ?? null, filters);\n\t}\n\n\t/**\n\t * Build a memory pack with semantic candidate merging.\n\t *\n\t * Async version that runs vector KNN search via sqlite-vec and merges\n\t * semantic candidates with FTS results. Falls back to FTS-only when\n\t * embeddings are disabled or unavailable.\n\t */\n\tasync buildMemoryPackAsync(\n\t\tcontext: string,\n\t\tlimit?: number,\n\t\ttokenBudget?: number | null,\n\t\tfilters?: MemoryFilters,\n\t): Promise<PackResponse> {\n\t\treturn buildMemoryPackAsync(this, context, limit, tokenBudget ?? null, filters);\n\t}\n\n\t// Raw event helpers\n\n\t/**\n\t * Normalize source/streamId to match Python's _normalize_stream_identity().\n\t * Trims whitespace, lowercases source, defaults to \"opencode\".\n\t */\n\tprivate normalizeStreamIdentity(source: string, streamId: string): [string, string] {\n\t\tconst s = source.trim().toLowerCase() || \"opencode\";\n\t\tconst sid = streamId.trim();\n\t\tif (!sid) throw new Error(\"stream_id is required\");\n\t\treturn [s, sid];\n\t}\n\n\t// Raw event query methods (ports from codemem/store/raw_events.py)\n\n\t/**\n\t * Find sessions that have unflushed events and have been idle long enough.\n\t * Port of raw_event_sessions_pending_idle_flush().\n\t */\n\trawEventSessionsPendingIdleFlush(\n\t\tidleBeforeTsWallMs: number,\n\t\tlimit = 25,\n\t): { source: string; streamId: string }[] {\n\t\tconst maxEvents = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventSessions.source,\n\t\t\t\tstream_id: schema.rawEventSessions.stream_id,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNotNull(schema.rawEventSessions.last_seen_ts_wall_ms),\n\t\t\t\t\tlte(schema.rawEventSessions.last_seen_ts_wall_ms, idleBeforeTsWallMs),\n\t\t\t\t\tgt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.rawEventSessions.last_seen_ts_wall_ms)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\treturn rows\n\t\t\t.filter((row) => row.stream_id)\n\t\t\t.map((row) => ({\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstreamId: String(row.stream_id ?? \"\"),\n\t\t\t}));\n\t}\n\n\t/**\n\t * Find sessions that have pending/failed flush batches with unflushed events.\n\t * Port of raw_event_sessions_with_pending_queue().\n\t */\n\trawEventSessionsWithPendingQueue(limit = 25): { source: string; streamId: string }[] {\n\t\tconst pendingBatches = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventFlushBatches.source,\n\t\t\t\tstream_id: schema.rawEventFlushBatches.stream_id,\n\t\t\t\toldest_pending_update: sql<string>`MIN(${schema.rawEventFlushBatches.updated_at})`.as(\n\t\t\t\t\t\"oldest_pending_update\",\n\t\t\t\t),\n\t\t\t})\n\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t.where(inArray(schema.rawEventFlushBatches.status, [\"pending\", \"failed\", \"started\", \"error\"]))\n\t\t\t.groupBy(schema.rawEventFlushBatches.source, schema.rawEventFlushBatches.stream_id)\n\t\t\t.as(\"pending_batches\");\n\n\t\tconst maxEvents = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = this.d\n\t\t\t.select({ source: pendingBatches.source, stream_id: pendingBatches.stream_id })\n\t\t\t.from(pendingBatches)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, pendingBatches.source),\n\t\t\t\t\teq(maxEvents.stream_id, pendingBatches.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.leftJoin(\n\t\t\t\tschema.rawEventSessions,\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventSessions.source, pendingBatches.source),\n\t\t\t\t\teq(schema.rawEventSessions.stream_id, pendingBatches.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(\n\t\t\t\tgt(maxEvents.max_seq, sql`COALESCE(${schema.rawEventSessions.last_flushed_event_seq}, -1)`),\n\t\t\t)\n\t\t\t.orderBy(sql`${pendingBatches.oldest_pending_update}`)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\treturn rows\n\t\t\t.filter((row) => row.stream_id)\n\t\t\t.map((row) => ({\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstreamId: String(row.stream_id ?? \"\"),\n\t\t\t}));\n\t}\n\n\t/**\n\t * Delete raw events older than max_age_ms. Returns count of deleted raw_events rows.\n\t * Port of purge_raw_events() + purge_raw_events_before().\n\t */\n\tpurgeRawEvents(maxAgeMs: number): number {\n\t\tif (maxAgeMs <= 0) return 0;\n\t\tconst nowMs = Date.now();\n\t\tconst cutoffTsWallMs = nowMs - maxAgeMs;\n\t\tconst cutoffIso = new Date(cutoffTsWallMs).toISOString();\n\n\t\treturn this.db.transaction(() => {\n\t\t\tthis.d\n\t\t\t\t.delete(schema.rawEventIngestSamples)\n\t\t\t\t.where(sql`${schema.rawEventIngestSamples.created_at} < ${cutoffIso}`)\n\t\t\t\t.run();\n\t\t\tconst result = this.d\n\t\t\t\t.delete(schema.rawEvents)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\tisNotNull(schema.rawEvents.ts_wall_ms),\n\t\t\t\t\t\tlt(schema.rawEvents.ts_wall_ms, cutoffTsWallMs),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.run();\n\t\t\treturn Number(result.changes ?? 0);\n\t\t})();\n\t}\n\n\t/**\n\t * Mark stuck flush batches (started/running/pending/claimed) as failed.\n\t * Port of mark_stuck_raw_event_batches_as_error().\n\t */\n\tmarkStuckRawEventBatchesAsError(olderThanIso: string, limit = 100): number {\n\t\tconst now = new Date().toISOString();\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`WITH candidates AS (\n SELECT id\n FROM raw_event_flush_batches\n WHERE status IN ('started', 'running', ?, ?) AND updated_at < ?\n ORDER BY updated_at\n LIMIT ?\n )\n UPDATE raw_event_flush_batches\n SET status = ?,\n updated_at = ?,\n error_message = 'Flush retry timed out.',\n error_type = 'RawEventBatchStuck',\n observer_provider = NULL,\n observer_model = NULL,\n observer_runtime = NULL\n WHERE id IN (SELECT id FROM candidates)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\t\"pending\", // RAW_EVENT_QUEUE_PENDING\n\t\t\t\t\"claimed\", // RAW_EVENT_QUEUE_CLAIMED\n\t\t\t\tolderThanIso,\n\t\t\t\tlimit,\n\t\t\t\t\"failed\", // RAW_EVENT_QUEUE_FAILED\n\t\t\t\tnow,\n\t\t\t);\n\n\t\treturn result.changes;\n\t}\n\n\t// Raw event per-session methods (ports for flush pipeline)\n\n\t/**\n\t * Get session metadata (cwd, project, started_at, etc.) for a raw event stream.\n\t * Port of raw_event_session_meta().\n\t */\n\trawEventSessionMeta(opencodeSessionId: string, source = \"opencode\"): Record<string, unknown> {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tcwd: schema.rawEventSessions.cwd,\n\t\t\t\tproject: schema.rawEventSessions.project,\n\t\t\t\tstarted_at: schema.rawEventSessions.started_at,\n\t\t\t\tlast_seen_ts_wall_ms: schema.rawEventSessions.last_seen_ts_wall_ms,\n\t\t\t\tlast_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.where(and(eq(schema.rawEventSessions.source, s), eq(schema.rawEventSessions.stream_id, sid)))\n\t\t\t.get();\n\t\tif (!row) return {};\n\t\treturn {\n\t\t\tcwd: row.cwd,\n\t\t\tproject: row.project,\n\t\t\tstarted_at: row.started_at,\n\t\t\tlast_seen_ts_wall_ms: row.last_seen_ts_wall_ms,\n\t\t\tlast_flushed_event_seq: row.last_flushed_event_seq,\n\t\t};\n\t}\n\n\t/**\n\t * Get the last flushed event_seq for a session. Returns -1 if no state.\n\t * Port of raw_event_flush_state().\n\t */\n\trawEventFlushState(opencodeSessionId: string, source = \"opencode\"): number {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst row = this.d\n\t\t\t.select({ last_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq })\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.where(and(eq(schema.rawEventSessions.source, s), eq(schema.rawEventSessions.stream_id, sid)))\n\t\t\t.get();\n\t\tif (!row) return -1;\n\t\treturn Number(row.last_flushed_event_seq);\n\t}\n\n\t/**\n\t * Get raw events after a given event_seq, ordered by event_seq ASC.\n\t * Returns enriched event objects with type, timestamps, event_seq, event_id.\n\t * Port of raw_events_since_by_seq().\n\t */\n\trawEventsSinceBySeq(\n\t\topencodeSessionId: string,\n\t\tsource = \"opencode\",\n\t\tafterEventSeq = -1,\n\t\tlimit?: number | null,\n\t): Record<string, unknown>[] {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst baseQuery = this.d\n\t\t\t.select({\n\t\t\t\tevent_seq: schema.rawEvents.event_seq,\n\t\t\t\tevent_type: schema.rawEvents.event_type,\n\t\t\t\tts_wall_ms: schema.rawEvents.ts_wall_ms,\n\t\t\t\tts_mono_ms: schema.rawEvents.ts_mono_ms,\n\t\t\t\tpayload_json: schema.rawEvents.payload_json,\n\t\t\t\tevent_id: schema.rawEvents.event_id,\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEvents.source, s),\n\t\t\t\t\teq(schema.rawEvents.stream_id, sid),\n\t\t\t\t\tgt(schema.rawEvents.event_seq, afterEventSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.rawEvents.event_seq);\n\n\t\tconst rows = limit != null && limit > 0 ? baseQuery.limit(limit).all() : baseQuery.all();\n\n\t\treturn rows.map((row) => {\n\t\t\tconst payload = fromJson(row.payload_json) as Record<string, unknown>;\n\t\t\t// Use || (not ??) to match Python's `or` semantics — empty string falls through\n\t\t\tpayload.type = payload.type || row.event_type;\n\t\t\tpayload.timestamp_wall_ms = row.ts_wall_ms;\n\t\t\tpayload.timestamp_mono_ms = row.ts_mono_ms;\n\t\t\tpayload.event_seq = row.event_seq;\n\t\t\tpayload.event_id = row.event_id;\n\t\t\treturn payload;\n\t\t});\n\t}\n\n\t/**\n\t * Get or create a flush batch record. Returns [batchId, status].\n\t * Port of get_or_create_raw_event_flush_batch().\n\t */\n\tgetOrCreateRawEventFlushBatch(\n\t\topencodeSessionId: string,\n\t\tsource: string,\n\t\tstartEventSeq: number,\n\t\tendEventSeq: number,\n\t\textractorVersion: string,\n\t): { batchId: number; status: string } {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Atomic UPSERT to avoid SELECT+INSERT races. We intentionally do NOT\n\t\t// heartbeat claimed/running batches: their updated_at stays unchanged so\n\t\t// stuck-batch recovery can still age them out.\n\t\tconst t = schema.rawEventFlushBatches;\n\t\tconst row = this.d\n\t\t\t.insert(t)\n\t\t\t.values({\n\t\t\t\tsource: s,\n\t\t\t\tstream_id: sid,\n\t\t\t\topencode_session_id: sid,\n\t\t\t\tstart_event_seq: startEventSeq,\n\t\t\t\tend_event_seq: endEventSeq,\n\t\t\t\textractor_version: extractorVersion,\n\t\t\t\tstatus: \"pending\",\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [t.source, t.stream_id, t.start_event_seq, t.end_event_seq, t.extractor_version],\n\t\t\t\tset: {\n\t\t\t\t\tupdated_at: sql`CASE\n\t\t\t\t\t\tWHEN ${t.status} IN ('claimed', 'running')\n\t\t\t\t\t\tTHEN ${t.updated_at}\n\t\t\t\t\t\tELSE excluded.updated_at\n\t\t\t\t\tEND`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.returning({ id: t.id, status: t.status })\n\t\t\t.get();\n\n\t\tif (!row) throw new Error(\"Failed to create flush batch\");\n\t\t// Canonicalize legacy DB statuses to match Python's _RAW_EVENT_QUEUE_DB_TO_CANONICAL\n\t\tconst rawStatus = String(row.status);\n\t\tconst canonicalStatus =\n\t\t\trawStatus === \"started\"\n\t\t\t\t? \"pending\"\n\t\t\t\t: rawStatus === \"running\"\n\t\t\t\t\t? \"claimed\"\n\t\t\t\t\t: rawStatus === \"error\"\n\t\t\t\t\t\t? \"failed\"\n\t\t\t\t\t\t: rawStatus;\n\t\treturn { batchId: Number(row.id), status: canonicalStatus };\n\t}\n\n\t/**\n\t * Attempt to claim a flush batch for processing.\n\t * Returns true if successfully claimed, false if already claimed/completed.\n\t * Port of claim_raw_event_flush_batch().\n\t */\n\tclaimRawEventFlushBatch(batchId: number): boolean {\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.d\n\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t.set({\n\t\t\t\tstatus: \"claimed\",\n\t\t\t\tupdated_at: now,\n\t\t\t\tattempt_count: sql`${schema.rawEventFlushBatches.attempt_count} + 1`,\n\t\t\t})\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventFlushBatches.id, batchId),\n\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\"pending\", \"failed\", \"started\", \"error\"]),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.returning({ id: schema.rawEventFlushBatches.id })\n\t\t\t.get();\n\t\treturn row != null;\n\t}\n\n\t/**\n\t * Update the status of a flush batch.\n\t * Port of update_raw_event_flush_batch_status().\n\t */\n\tupdateRawEventFlushBatchStatus(batchId: number, status: string): void {\n\t\tconst now = new Date().toISOString();\n\t\tif (status === \"failed\") {\n\t\t\t// Preserve existing error details when marking as failed\n\t\t\tthis.d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({ status, updated_at: now })\n\t\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t\t.run();\n\t\t} else {\n\t\t\t// Clear error details for non-failure statuses\n\t\t\tthis.d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({\n\t\t\t\t\tstatus,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\terror_message: null,\n\t\t\t\t\terror_type: null,\n\t\t\t\t\tobserver_provider: null,\n\t\t\t\t\tobserver_model: null,\n\t\t\t\t\tobserver_runtime: null,\n\t\t\t\t})\n\t\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t\t.run();\n\t\t}\n\t}\n\n\t/**\n\t * Record a flush batch failure with error details.\n\t * Port of record_raw_event_flush_batch_failure().\n\t */\n\trecordRawEventFlushBatchFailure(\n\t\tbatchId: number,\n\t\topts: {\n\t\t\tmessage: string;\n\t\t\terrorType: string;\n\t\t\tobserverProvider?: string | null;\n\t\t\tobserverModel?: string | null;\n\t\t\tobserverRuntime?: string | null;\n\t\t},\n\t): void {\n\t\tconst now = new Date().toISOString();\n\t\tthis.d\n\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t.set({\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tupdated_at: now,\n\t\t\t\terror_message: opts.message,\n\t\t\t\terror_type: opts.errorType,\n\t\t\t\tobserver_provider: opts.observerProvider ?? null,\n\t\t\t\tobserver_model: opts.observerModel ?? null,\n\t\t\t\tobserver_runtime: opts.observerRuntime ?? null,\n\t\t\t})\n\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Update the last flushed event_seq for a session.\n\t * Port of update_raw_event_flush_state().\n\t */\n\tupdateRawEventFlushState(\n\t\topencodeSessionId: string,\n\t\tlastFlushed: number,\n\t\tsource = \"opencode\",\n\t): void {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst now = new Date().toISOString();\n\t\tthis.d\n\t\t\t.insert(schema.rawEventSessions)\n\t\t\t.values({\n\t\t\t\topencode_session_id: sid,\n\t\t\t\tsource: s,\n\t\t\t\tstream_id: sid,\n\t\t\t\tlast_flushed_event_seq: lastFlushed,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [schema.rawEventSessions.source, schema.rawEventSessions.stream_id],\n\t\t\t\tset: {\n\t\t\t\t\topencode_session_id: sql`excluded.opencode_session_id`,\n\t\t\t\t\tlast_flushed_event_seq: sql`excluded.last_flushed_event_seq`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t// Raw event ingestion methods (ports for POST /api/raw-events)\n\n\t/**\n\t * Update ingest stats counters (sample + running totals).\n\t * Port of _update_raw_event_ingest_stats().\n\t */\n\tprivate updateRawEventIngestStats(\n\t\tinserted: number,\n\t\tskippedInvalid: number,\n\t\tskippedDuplicate: number,\n\t\tskippedConflict: number,\n\t): void {\n\t\tconst now = nowIso();\n\t\tconst skippedEvents = skippedInvalid + skippedDuplicate + skippedConflict;\n\t\tthis.d\n\t\t\t.insert(schema.rawEventIngestSamples)\n\t\t\t.values({\n\t\t\t\tcreated_at: now,\n\t\t\t\tinserted_events: inserted,\n\t\t\t\tskipped_invalid: skippedInvalid,\n\t\t\t\tskipped_duplicate: skippedDuplicate,\n\t\t\t\tskipped_conflict: skippedConflict,\n\t\t\t})\n\t\t\t.run();\n\t\tthis.d\n\t\t\t.insert(schema.rawEventIngestStats)\n\t\t\t.values({\n\t\t\t\tid: 1,\n\t\t\t\tinserted_events: inserted,\n\t\t\t\tskipped_events: skippedEvents,\n\t\t\t\tskipped_invalid: skippedInvalid,\n\t\t\t\tskipped_duplicate: skippedDuplicate,\n\t\t\t\tskipped_conflict: skippedConflict,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: schema.rawEventIngestStats.id,\n\t\t\t\tset: {\n\t\t\t\t\tinserted_events: sql`${schema.rawEventIngestStats.inserted_events} + excluded.inserted_events`,\n\t\t\t\t\tskipped_events: sql`${schema.rawEventIngestStats.skipped_events} + excluded.skipped_events`,\n\t\t\t\t\tskipped_invalid: sql`${schema.rawEventIngestStats.skipped_invalid} + excluded.skipped_invalid`,\n\t\t\t\t\tskipped_duplicate: sql`${schema.rawEventIngestStats.skipped_duplicate} + excluded.skipped_duplicate`,\n\t\t\t\t\tskipped_conflict: sql`${schema.rawEventIngestStats.skipped_conflict} + excluded.skipped_conflict`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Record a single raw event. Returns true if inserted, false if duplicate.\n\t * Port of record_raw_event().\n\t */\n\trecordRawEvent(opts: {\n\t\topencodeSessionId: string;\n\t\tsource?: string;\n\t\teventId: string;\n\t\teventType: string;\n\t\tpayload: Record<string, unknown>;\n\t\ttsWallMs?: number | null;\n\t\ttsMonoMs?: number | null;\n\t}): boolean {\n\t\tif (!opts.opencodeSessionId.trim()) throw new Error(\"opencode_session_id is required\");\n\t\tif (!opts.eventId.trim()) throw new Error(\"event_id is required\");\n\t\tif (!opts.eventType.trim()) throw new Error(\"event_type is required\");\n\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\n\t\t\topts.source ?? \"opencode\",\n\t\t\topts.opencodeSessionId,\n\t\t);\n\n\t\treturn this.db.transaction(() => {\n\t\t\tconst now = nowIso();\n\n\t\t\t// Check for duplicate\n\t\t\tconst existing = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEvents.source, source),\n\t\t\t\t\t\teq(schema.rawEvents.stream_id, streamId),\n\t\t\t\t\t\teq(schema.rawEvents.event_id, opts.eventId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (existing != null) {\n\t\t\t\tthis.updateRawEventIngestStats(0, 0, 1, 0);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Ensure session row exists\n\t\t\tconst sessionRow = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (sessionRow == null) {\n\t\t\t\tthis.d\n\t\t\t\t\t.insert(schema.rawEventSessions)\n\t\t\t\t\t.values({\n\t\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\n\t\t\t// Allocate event_seq\n\t\t\tconst seqRow = this.d\n\t\t\t\t.update(schema.rawEventSessions)\n\t\t\t\t.set({\n\t\t\t\t\tlast_received_event_seq: sql`${schema.rawEventSessions.last_received_event_seq} + 1`,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.returning({\n\t\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\t})\n\t\t\t\t.get();\n\t\t\tif (!seqRow) throw new Error(\"Failed to allocate raw event seq\");\n\t\t\tconst eventSeq = Number(seqRow.last_received_event_seq);\n\n\t\t\tthis.d\n\t\t\t\t.insert(schema.rawEvents)\n\t\t\t\t.values({\n\t\t\t\t\tsource,\n\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\tevent_id: opts.eventId,\n\t\t\t\t\tevent_seq: eventSeq,\n\t\t\t\t\tevent_type: opts.eventType,\n\t\t\t\t\tts_wall_ms: opts.tsWallMs ?? null,\n\t\t\t\t\tts_mono_ms: opts.tsMonoMs ?? null,\n\t\t\t\t\tpayload_json: toJson(opts.payload),\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t\tthis.updateRawEventIngestStats(1, 0, 0, 0);\n\t\t\treturn true;\n\t\t})();\n\t}\n\n\t/**\n\t * Record a batch of raw events for a single session. Returns { inserted, skipped }.\n\t * Port of record_raw_events_batch().\n\t */\n\trecordRawEventsBatch(\n\t\topencodeSessionId: string,\n\t\tevents: Record<string, unknown>[],\n\t): { inserted: number; skipped: number } {\n\t\tif (!opencodeSessionId.trim()) throw new Error(\"opencode_session_id is required\");\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\"opencode\", opencodeSessionId);\n\n\t\treturn this.db.transaction(() => {\n\t\t\tconst now = nowIso();\n\n\t\t\t// Ensure session row exists\n\t\t\tconst sessionRow = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (sessionRow == null) {\n\t\t\t\tthis.d\n\t\t\t\t\t.insert(schema.rawEventSessions)\n\t\t\t\t\t.values({\n\t\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\n\t\t\t// Normalize and validate events\n\t\t\tlet skippedInvalid = 0;\n\t\t\tlet skippedDuplicate = 0;\n\t\t\tlet skippedConflict = 0;\n\n\t\t\tinterface NormalizedEvent {\n\t\t\t\teventId: string;\n\t\t\t\teventType: string;\n\t\t\t\tpayload: Record<string, unknown>;\n\t\t\t\ttsWallMs: unknown;\n\t\t\t\ttsMonoMs: unknown;\n\t\t\t}\n\t\t\tconst normalized: NormalizedEvent[] = [];\n\t\t\tconst seenIds = new Set<string>();\n\n\t\t\tfor (const event of events) {\n\t\t\t\tconst eventId = String(event.event_id ?? \"\");\n\t\t\t\tconst eventType = String(event.event_type ?? \"\");\n\t\t\t\tlet payload = event.payload;\n\t\t\t\tif (payload == null || typeof payload !== \"object\" || Array.isArray(payload)) {\n\t\t\t\t\tpayload = {};\n\t\t\t\t}\n\t\t\t\tconst tsWallMs = event.ts_wall_ms;\n\t\t\t\tconst tsMonoMs = event.ts_mono_ms;\n\n\t\t\t\tif (!eventId || !eventType) {\n\t\t\t\t\tskippedInvalid++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (seenIds.has(eventId)) {\n\t\t\t\t\tskippedDuplicate++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenIds.add(eventId);\n\t\t\t\tnormalized.push({\n\t\t\t\t\teventId,\n\t\t\t\t\teventType,\n\t\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\t\ttsWallMs,\n\t\t\t\t\ttsMonoMs,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (normalized.length === 0) {\n\t\t\t\tthis.updateRawEventIngestStats(0, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\t\treturn { inserted: 0, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t\t}\n\n\t\t\t// Check for existing event_ids in chunks\n\t\t\tconst existingIds = new Set<string>();\n\t\t\tconst chunkSize = 500;\n\t\t\tfor (let i = 0; i < normalized.length; i += chunkSize) {\n\t\t\t\tconst chunk = normalized.slice(i, i + chunkSize);\n\t\t\t\tconst chunkEventIds = chunk.map((e) => e.eventId);\n\t\t\t\tconst rows = this.d\n\t\t\t\t\t.select({ event_id: schema.rawEvents.event_id })\n\t\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(\n\t\t\t\t\t\t\teq(schema.rawEvents.source, source),\n\t\t\t\t\t\t\teq(schema.rawEvents.stream_id, streamId),\n\t\t\t\t\t\t\tinArray(schema.rawEvents.event_id, chunkEventIds),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t.all();\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tif (row.event_id) existingIds.add(row.event_id);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst newEvents = normalized.filter((e) => !existingIds.has(e.eventId));\n\t\t\tskippedDuplicate += normalized.length - newEvents.length;\n\n\t\t\tif (newEvents.length === 0) {\n\t\t\t\tthis.updateRawEventIngestStats(0, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\t\treturn { inserted: 0, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t\t}\n\n\t\t\t// Allocate seq range\n\t\t\tconst seqRow = this.d\n\t\t\t\t.update(schema.rawEventSessions)\n\t\t\t\t.set({\n\t\t\t\t\tlast_received_event_seq: sql`${schema.rawEventSessions.last_received_event_seq} + ${newEvents.length}`,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.returning({\n\t\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\t})\n\t\t\t\t.get();\n\t\t\tif (!seqRow) throw new Error(\"Failed to allocate raw event seq\");\n\t\t\tconst endSeq = Number(seqRow.last_received_event_seq);\n\t\t\tconst startSeq = endSeq - newEvents.length + 1;\n\n\t\t\tconst insertRows = newEvents.map((event, offset) => {\n\t\t\t\tconst tsWallMs = typeof event.tsWallMs === \"number\" ? event.tsWallMs : null;\n\t\t\t\tconst tsMonoMs = typeof event.tsMonoMs === \"number\" ? event.tsMonoMs : null;\n\t\t\t\treturn {\n\t\t\t\t\tsource,\n\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\tevent_id: event.eventId,\n\t\t\t\t\tevent_seq: startSeq + offset,\n\t\t\t\t\tevent_type: event.eventType,\n\t\t\t\t\tts_wall_ms: tsWallMs,\n\t\t\t\t\tts_mono_ms: tsMonoMs,\n\t\t\t\t\tpayload_json: toJson(event.payload),\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tconst result = this.d.insert(schema.rawEvents).values(insertRows).onConflictDoNothing().run();\n\t\t\tconst inserted = Number(result.changes ?? 0);\n\t\t\tskippedConflict += newEvents.length - inserted;\n\n\t\t\tthis.updateRawEventIngestStats(inserted, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\treturn { inserted, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t})();\n\t}\n\n\t/**\n\t * UPSERT session metadata (cwd, project, started_at, last_seen_ts_wall_ms).\n\t * Port of update_raw_event_session_meta().\n\t */\n\tupdateRawEventSessionMeta(opts: {\n\t\topencodeSessionId: string;\n\t\tsource?: string;\n\t\tcwd?: string | null;\n\t\tproject?: string | null;\n\t\tstartedAt?: string | null;\n\t\tlastSeenTsWallMs?: number | null;\n\t}): void {\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\n\t\t\topts.source ?? \"opencode\",\n\t\t\topts.opencodeSessionId,\n\t\t);\n\t\tconst now = nowIso();\n\t\tconst t = schema.rawEventSessions;\n\t\tthis.d\n\t\t\t.insert(t)\n\t\t\t.values({\n\t\t\t\topencode_session_id: streamId,\n\t\t\t\tsource,\n\t\t\t\tstream_id: streamId,\n\t\t\t\tcwd: opts.cwd ?? null,\n\t\t\t\tproject: opts.project ?? null,\n\t\t\t\tstarted_at: opts.startedAt ?? null,\n\t\t\t\tlast_seen_ts_wall_ms: opts.lastSeenTsWallMs ?? null,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [t.source, t.stream_id],\n\t\t\t\tset: {\n\t\t\t\t\topencode_session_id: sql`excluded.opencode_session_id`,\n\t\t\t\t\tcwd: sql`COALESCE(excluded.cwd, ${t.cwd})`,\n\t\t\t\t\tproject: sql`COALESCE(excluded.project, ${t.project})`,\n\t\t\t\t\tstarted_at: sql`COALESCE(excluded.started_at, ${t.started_at})`,\n\t\t\t\t\tlast_seen_ts_wall_ms: sql`CASE\n\t\t\t\t\t\tWHEN excluded.last_seen_ts_wall_ms IS NULL THEN ${t.last_seen_ts_wall_ms}\n\t\t\t\t\t\tWHEN ${t.last_seen_ts_wall_ms} IS NULL THEN excluded.last_seen_ts_wall_ms\n\t\t\t\t\t\tWHEN excluded.last_seen_ts_wall_ms > ${t.last_seen_ts_wall_ms} THEN excluded.last_seen_ts_wall_ms\n\t\t\t\t\t\tELSE ${t.last_seen_ts_wall_ms}\n\t\t\t\t\tEND`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Get totals of pending (unflushed) raw events.\n\t * Port of raw_event_backlog_totals().\n\t */\n\trawEventBacklogTotals(): { pending: number; sessions: number } {\n\t\tconst row = this.d.get<{ sessions: number | null; pending: number | null }>(sql`\n\t\t\tWITH max_events AS (\n\t\t\t\tSELECT source, stream_id, MAX(event_seq) AS max_seq\n\t\t\t\tFROM raw_events\n\t\t\t\tGROUP BY source, stream_id\n\t\t\t)\n\t\t\tSELECT\n\t\t\t\tCOUNT(1) AS sessions,\n\t\t\t\tSUM(e.max_seq - s.last_flushed_event_seq) AS pending\n\t\t\tFROM raw_event_sessions s\n\t\t\tJOIN max_events e ON e.source = s.source AND e.stream_id = s.stream_id\n\t\t\tWHERE e.max_seq > s.last_flushed_event_seq\n\t\t`);\n\t\tif (!row) return { sessions: 0, pending: 0 };\n\t\treturn {\n\t\t\tsessions: Number(row.sessions ?? 0),\n\t\t\tpending: Number(row.pending ?? 0),\n\t\t};\n\t}\n\n\t/**\n\t * Get the latest failed flush batch, or null if none.\n\t * Port of latest_raw_event_flush_failure().\n\t */\n\tlatestRawEventFlushFailure(source?: string | null): Record<string, unknown> | null {\n\t\tconst conditions = [inArray(schema.rawEventFlushBatches.status, [\"error\", \"failed\"])];\n\t\tif (source != null) {\n\t\t\tconditions.push(\n\t\t\t\teq(schema.rawEventFlushBatches.source, source.trim().toLowerCase() || \"opencode\"),\n\t\t\t);\n\t\t}\n\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tid: schema.rawEventFlushBatches.id,\n\t\t\t\tsource: schema.rawEventFlushBatches.source,\n\t\t\t\tstream_id: schema.rawEventFlushBatches.stream_id,\n\t\t\t\topencode_session_id: schema.rawEventFlushBatches.opencode_session_id,\n\t\t\t\tstart_event_seq: schema.rawEventFlushBatches.start_event_seq,\n\t\t\t\tend_event_seq: schema.rawEventFlushBatches.end_event_seq,\n\t\t\t\textractor_version: schema.rawEventFlushBatches.extractor_version,\n\t\t\t\tstatus: schema.rawEventFlushBatches.status,\n\t\t\t\tupdated_at: schema.rawEventFlushBatches.updated_at,\n\t\t\t\tattempt_count: schema.rawEventFlushBatches.attempt_count,\n\t\t\t\terror_message: schema.rawEventFlushBatches.error_message,\n\t\t\t\terror_type: schema.rawEventFlushBatches.error_type,\n\t\t\t\tobserver_provider: schema.rawEventFlushBatches.observer_provider,\n\t\t\t\tobserver_model: schema.rawEventFlushBatches.observer_model,\n\t\t\t\tobserver_runtime: schema.rawEventFlushBatches.observer_runtime,\n\t\t\t})\n\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t.where(and(...conditions))\n\t\t\t.orderBy(desc(schema.rawEventFlushBatches.updated_at))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (!row) return null;\n\t\treturn { ...row, status: \"error\" };\n\t}\n\n\tgetSyncDaemonState(): Record<string, unknown> | null {\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tlast_error: schema.syncDaemonState.last_error,\n\t\t\t\tlast_traceback: schema.syncDaemonState.last_traceback,\n\t\t\t\tlast_error_at: schema.syncDaemonState.last_error_at,\n\t\t\t\tlast_ok_at: schema.syncDaemonState.last_ok_at,\n\t\t\t})\n\t\t\t.from(schema.syncDaemonState)\n\t\t\t.where(eq(schema.syncDaemonState.id, 1))\n\t\t\t.get();\n\t\treturn row ? { ...row } : null;\n\t}\n\n\tsameActorPeerIds(): string[] {\n\t\tif (!tableExists(this.db, \"sync_peers\")) return [];\n\t\tconst rows = this.d\n\t\t\t.select({ peer_device_id: schema.syncPeers.peer_device_id })\n\t\t\t.from(schema.syncPeers)\n\t\t\t.where(\n\t\t\t\tor(\n\t\t\t\t\teq(schema.syncPeers.claimed_local_actor, 1),\n\t\t\t\t\teq(schema.syncPeers.actor_id, this.actorId),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.syncPeers.peer_device_id)\n\t\t\t.all();\n\t\treturn rows.map((row) => String(row.peer_device_id ?? \"\").trim()).filter(Boolean);\n\t}\n\n\tclaimedSameActorLegacyActorIds(): string[] {\n\t\treturn this.sameActorPeerIds().map((peerId) => `legacy-sync:${peerId}`);\n\t}\n\n\tclaimableLegacyDeviceIds(): Record<string, unknown>[] {\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\torigin_device_id: schema.memoryItems.origin_device_id,\n\t\t\t\tmemory_count: sql<number>`COUNT(*)`,\n\t\t\t\tlast_seen_at: sql<string | null>`MAX(${schema.memoryItems.created_at})`,\n\t\t\t})\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNotNull(schema.memoryItems.origin_device_id),\n\t\t\t\t\tsql`TRIM(${schema.memoryItems.origin_device_id}) != ''`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} != 'unknown'`,\n\t\t\t\t\tsql`(\n\t\t\t\t\t\t(${schema.memoryItems.actor_id} IS NULL OR TRIM(${schema.memoryItems.actor_id}) = '' OR ${schema.memoryItems.actor_id} LIKE 'legacy-sync:%')\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t${schema.memoryItems.actor_id} IS NULL\n\t\t\t\t\t\t\tOR TRIM(${schema.memoryItems.actor_id}) = ''\n\t\t\t\t\t\t\tOR ${schema.memoryItems.actor_id} LIKE 'legacy-sync:%'\n\t\t\t\t\t\t\tOR ${schema.memoryItems.actor_display_name} = ${LEGACY_SYNC_ACTOR_DISPLAY_NAME}\n\t\t\t\t\t\t\tOR ${schema.memoryItems.workspace_id} = ${LEGACY_SHARED_WORKSPACE_ID}\n\t\t\t\t\t\t\tOR ${schema.memoryItems.trust_state} = 'legacy_unknown'\n\t\t\t\t\t\t)\n\t\t\t\t\t)`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} != ${this.deviceId}`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} NOT IN (\n\t\t\t\t\t\tSELECT ${schema.syncPeers.peer_device_id}\n\t\t\t\t\t\tFROM ${schema.syncPeers}\n\t\t\t\t\t\tWHERE ${schema.syncPeers.peer_device_id} IS NOT NULL\n\t\t\t\t\t)`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.groupBy(schema.memoryItems.origin_device_id)\n\t\t\t.orderBy(\n\t\t\t\tdesc(sql`MAX(${schema.memoryItems.created_at})`),\n\t\t\t\tschema.memoryItems.origin_device_id,\n\t\t\t)\n\t\t\t.all();\n\t\treturn rows\n\t\t\t.filter((row) => cleanStr(row.origin_device_id))\n\t\t\t.map((row) => ({\n\t\t\t\torigin_device_id: String(row.origin_device_id),\n\t\t\t\tmemory_count: Number(row.memory_count ?? 0),\n\t\t\t\tlast_seen_at: row.last_seen_at ?? null,\n\t\t\t}));\n\t}\n\n\tprivate effectiveSyncProjectFilters(peerDeviceId?: string | null): {\n\t\tinclude: string[];\n\t\texclude: string[];\n\t} {\n\t\tconst config = readCodememConfigFile();\n\t\tlet include = parseJsonList(JSON.stringify(config.sync_projects_include ?? []));\n\t\tlet exclude = parseJsonList(JSON.stringify(config.sync_projects_exclude ?? []));\n\t\tif (peerDeviceId) {\n\t\t\tconst row = this.d\n\t\t\t\t.select({\n\t\t\t\t\tprojects_include_json: schema.syncPeers.projects_include_json,\n\t\t\t\t\tprojects_exclude_json: schema.syncPeers.projects_exclude_json,\n\t\t\t\t})\n\t\t\t\t.from(schema.syncPeers)\n\t\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t\t.limit(1)\n\t\t\t\t.get();\n\t\t\tif (row) {\n\t\t\t\tif (row.projects_include_json != null) include = parseJsonList(row.projects_include_json);\n\t\t\t\tif (row.projects_exclude_json != null) exclude = parseJsonList(row.projects_exclude_json);\n\t\t\t}\n\t\t}\n\t\treturn { include, exclude };\n\t}\n\n\tprivate syncProjectAllowed(project: string | null, peerDeviceId?: string | null): boolean {\n\t\tconst { include, exclude } = this.effectiveSyncProjectFilters(peerDeviceId);\n\t\tconst name = cleanStr(project);\n\t\tconst basename = projectBasename(name);\n\t\tif (include.length > 0 && !include.some((item) => item === name || item === basename))\n\t\t\treturn false;\n\t\tif (exclude.some((item) => item === name || item === basename)) return false;\n\t\treturn true;\n\t}\n\n\tsharingReviewSummary(project?: string | null): Record<string, unknown>[] {\n\t\tconst selectedProject = cleanStr(project);\n\t\tconst claimedPeers = this.sameActorPeerIds();\n\t\tconst legacyActorIds = this.claimedSameActorLegacyActorIds();\n\t\tconst ownershipConditions = [eq(schema.memoryItems.actor_id, this.actorId)];\n\t\tif (claimedPeers.length > 0) {\n\t\t\townershipConditions.push(inArray(schema.memoryItems.origin_device_id, claimedPeers));\n\t\t}\n\t\tif (legacyActorIds.length > 0) {\n\t\t\townershipConditions.push(inArray(schema.memoryItems.actor_id, legacyActorIds));\n\t\t}\n\t\tconst ownershipWhere =\n\t\t\townershipConditions.length === 1 ? ownershipConditions[0] : or(...ownershipConditions);\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\tpeer_device_id: schema.syncPeers.peer_device_id,\n\t\t\t\tname: schema.syncPeers.name,\n\t\t\t\tactor_id: schema.syncPeers.actor_id,\n\t\t\t\tactor_display_name: schema.actors.display_name,\n\t\t\t\tproject: schema.sessions.project,\n\t\t\t\tvisibility: schema.memoryItems.visibility,\n\t\t\t\ttotal: sql<number>`COUNT(*)`,\n\t\t\t})\n\t\t\t.from(schema.syncPeers)\n\t\t\t.leftJoin(schema.actors, eq(schema.actors.actor_id, schema.syncPeers.actor_id))\n\t\t\t.innerJoin(schema.memoryItems, sql`1 = 1`)\n\t\t\t.innerJoin(schema.sessions, eq(schema.sessions.id, schema.memoryItems.session_id))\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.memoryItems.active, 1),\n\t\t\t\t\tisNotNull(schema.syncPeers.actor_id),\n\t\t\t\t\tsql`TRIM(${schema.syncPeers.actor_id}) != ''`,\n\t\t\t\t\tsql`${schema.syncPeers.actor_id} != ${this.actorId}`,\n\t\t\t\t\townershipWhere,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.groupBy(\n\t\t\t\tschema.syncPeers.peer_device_id,\n\t\t\t\tschema.syncPeers.name,\n\t\t\t\tschema.syncPeers.actor_id,\n\t\t\t\tschema.actors.display_name,\n\t\t\t\tschema.sessions.project,\n\t\t\t\tschema.memoryItems.visibility,\n\t\t\t)\n\t\t\t.orderBy(schema.syncPeers.name, schema.syncPeers.peer_device_id)\n\t\t\t.all();\n\t\tconst byPeer = new Map<string, Record<string, unknown>>();\n\t\tfor (const row of rows) {\n\t\t\tconst peerDeviceId = String(row.peer_device_id ?? \"\").trim();\n\t\t\tconst actorId = String(row.actor_id ?? \"\").trim();\n\t\t\tif (!peerDeviceId || !actorId || actorId === this.actorId) continue;\n\t\t\tconst projectName = cleanStr(row.project);\n\t\t\tif (selectedProject) {\n\t\t\t\tconst selectedBase = projectBasename(selectedProject);\n\t\t\t\tconst projectBase = projectBasename(projectName);\n\t\t\t\tif (projectName !== selectedProject && projectBase !== selectedBase) continue;\n\t\t\t}\n\t\t\tif (!this.syncProjectAllowed(projectName, peerDeviceId)) continue;\n\t\t\tconst current = byPeer.get(peerDeviceId) ?? {\n\t\t\t\tpeer_device_id: peerDeviceId,\n\t\t\t\tpeer_name: cleanStr(row.name) ?? peerDeviceId,\n\t\t\t\tactor_id: actorId,\n\t\t\t\tactor_display_name: cleanStr(row.actor_display_name) ?? actorId,\n\t\t\t\tproject: selectedProject,\n\t\t\t\tscope_label: selectedProject ?? \"All allowed projects\",\n\t\t\t\tshareable_count: 0,\n\t\t\t\tprivate_count: 0,\n\t\t\t};\n\t\t\tconst total = Number(row.total ?? 0);\n\t\t\tif (String(row.visibility ?? \"\") === \"private\") {\n\t\t\t\tcurrent.private_count = Number(current.private_count ?? 0) + total;\n\t\t\t} else {\n\t\t\t\tcurrent.shareable_count = Number(current.shareable_count ?? 0) + total;\n\t\t\t}\n\t\t\tbyPeer.set(peerDeviceId, current);\n\t\t}\n\t\tconst results = [...byPeer.values()].map((item) => ({\n\t\t\t...item,\n\t\t\ttotal_count: Number(item.shareable_count ?? 0) + Number(item.private_count ?? 0),\n\t\t}));\n\t\treturn results.sort(\n\t\t\t(a: Record<string, unknown>, b: Record<string, unknown>) =>\n\t\t\t\tString(a.peer_name ?? \"\").localeCompare(String(b.peer_name ?? \"\")) ||\n\t\t\t\tString(a.peer_device_id ?? \"\").localeCompare(String(b.peer_device_id ?? \"\")),\n\t\t);\n\t}\n\n\t// close\n\n\t/** Close the database connection. */\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n","/**\n * Peer discovery and address management for the codemem sync system.\n *\n * Handles address normalization, deduplication, peer address storage,\n * and mDNS stubs. Ported from codemem/sync/discovery.py.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport { eq, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { mergeAddresses, normalizeAddress } from \"./address-utils.js\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\n\n// Re-export for consumers that import from sync-discovery\nexport { addressDedupeKey, mergeAddresses, normalizeAddress } from \"./address-utils.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_SERVICE_TYPE = \"_codemem._tcp.local.\";\n\n// ---------------------------------------------------------------------------\n// Address selection\n// ---------------------------------------------------------------------------\n\n/**\n * Select addresses to dial, preferring mDNS-discovered addresses.\n *\n * mDNS addresses come first, then stored addresses (deduplicated).\n */\nexport function selectDialAddresses(options: { stored: string[]; mdns: string[] }): string[] {\n\tif (options.mdns.length === 0) {\n\t\treturn mergeAddresses(options.stored, []);\n\t}\n\treturn mergeAddresses(options.mdns, options.stored);\n}\n\n// ---------------------------------------------------------------------------\n// Peer address storage (DB)\n// ---------------------------------------------------------------------------\n\n/**\n * Load stored addresses for a peer from the sync_peers table.\n */\nexport function loadPeerAddresses(db: Database, peerDeviceId: string): string[] {\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ addresses_json: schema.syncPeers.addresses_json })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\tif (!row?.addresses_json) return [];\n\ttry {\n\t\tconst raw = JSON.parse(row.addresses_json);\n\t\tif (!Array.isArray(raw)) return [];\n\t\treturn raw.filter((item): item is string => typeof item === \"string\");\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Update stored addresses for a peer, merging with existing.\n *\n * Creates the sync_peers row if it doesn't exist (upsert).\n * Returns the merged address list.\n */\nexport function updatePeerAddresses(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddresses: string[],\n\toptions?: {\n\t\tname?: string;\n\t\tpinnedFingerprint?: string;\n\t\tpublicKey?: string;\n\t},\n): string[] {\n\tconst merged = mergeAddresses(loadPeerAddresses(db, peerDeviceId), addresses);\n\tconst now = new Date().toISOString();\n\tconst addressesJson = JSON.stringify(merged);\n\n\t// Atomic UPSERT — avoids TOCTOU race with concurrent sync workers\n\tconst d = drizzle(db, { schema });\n\td.insert(schema.syncPeers)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tname: options?.name ?? null,\n\t\t\tpinned_fingerprint: options?.pinnedFingerprint ?? null,\n\t\t\tpublic_key: options?.publicKey ?? null,\n\t\t\taddresses_json: addressesJson,\n\t\t\tcreated_at: now,\n\t\t\tlast_seen_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncPeers.peer_device_id,\n\t\t\tset: {\n\t\t\t\tname: sql`COALESCE(excluded.name, ${schema.syncPeers.name})`,\n\t\t\t\tpinned_fingerprint: sql`COALESCE(excluded.pinned_fingerprint, ${schema.syncPeers.pinned_fingerprint})`,\n\t\t\t\tpublic_key: sql`COALESCE(excluded.public_key, ${schema.syncPeers.public_key})`,\n\t\t\t\taddresses_json: sql`excluded.addresses_json`,\n\t\t\t\tlast_seen_at: sql`excluded.last_seen_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n\n\treturn merged;\n}\n\n/**\n * Record a sync attempt in the sync_attempts table and update peer status.\n */\nexport function recordSyncAttempt(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: {\n\t\tok: boolean;\n\t\topsIn?: number;\n\t\topsOut?: number;\n\t\terror?: string;\n\t},\n): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncAttempts)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tstarted_at: now,\n\t\t\tfinished_at: now,\n\t\t\tok: options.ok ? 1 : 0,\n\t\t\tops_in: options.opsIn ?? 0,\n\t\t\tops_out: options.opsOut ?? 0,\n\t\t\terror: options.error ?? null,\n\t\t})\n\t\t.run();\n\n\tif (options.ok) {\n\t\td.update(schema.syncPeers)\n\t\t\t.set({ last_sync_at: now, last_error: null })\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t} else {\n\t\td.update(schema.syncPeers)\n\t\t\t.set({ last_error: options.error ?? null })\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t}\n}\n\n/**\n * Record a successful sync and promote the working address to the front.\n *\n * Returns the reordered address list.\n */\nexport function recordPeerSuccess(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddress: string | null,\n): string[] {\n\tconst addresses = loadPeerAddresses(db, peerDeviceId);\n\tconst normalized = normalizeAddress(address ?? \"\");\n\tlet ordered = addresses;\n\tif (normalized) {\n\t\tconst remaining = addresses.filter((item) => normalizeAddress(item) !== normalized);\n\t\tordered = [normalized, ...remaining];\n\t\tconst d = drizzle(db, { schema });\n\t\td.update(schema.syncPeers)\n\t\t\t.set({\n\t\t\t\taddresses_json: JSON.stringify(ordered),\n\t\t\t\tlast_sync_at: new Date().toISOString(),\n\t\t\t\tlast_error: null,\n\t\t\t})\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t}\n\treturn ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Project filters\n// ---------------------------------------------------------------------------\n\n/**\n * Set per-peer project include/exclude filters.\n *\n * Pass null for both to clear the override (inherit global config).\n */\nexport function setPeerProjectFilter(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: {\n\t\tinclude: string[] | null;\n\t\texclude: string[] | null;\n\t},\n): void {\n\tconst includeJson = options.include === null ? null : JSON.stringify(options.include);\n\tconst excludeJson = options.exclude === null ? null : JSON.stringify(options.exclude);\n\tconst d = drizzle(db, { schema });\n\td.update(schema.syncPeers)\n\t\t.set({ projects_include_json: includeJson, projects_exclude_json: excludeJson })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n/**\n * Set the claimed_local_actor flag for a peer.\n */\nexport function setPeerLocalActorClaim(db: Database, peerDeviceId: string, claimed: boolean): void {\n\tconst d = drizzle(db, { schema });\n\td.update(schema.syncPeers)\n\t\t.set({ claimed_local_actor: claimed ? 1 : 0 })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n// ---------------------------------------------------------------------------\n// mDNS stubs (Phase 1)\n// ---------------------------------------------------------------------------\n\nexport interface MdnsEntry {\n\tname?: string;\n\thost?: string;\n\tport?: number;\n\taddress?: Uint8Array | string;\n\tproperties?: Record<string, string | Uint8Array>;\n}\n\nfunction commandAvailable(command: string): boolean {\n\ttry {\n\t\texecFileSync(\"which\", [command], { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction runDnsSd(args: string[], timeoutMs = 1200): string {\n\ttry {\n\t\treturn execFileSync(\"dns-sd\", args, {\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\ttimeout: timeoutMs,\n\t\t});\n\t} catch (err) {\n\t\tif (err && typeof err === \"object\") {\n\t\t\tconst e = err as { stdout?: string | Buffer; stderr?: string | Buffer };\n\t\t\tconst out = [e.stdout, e.stderr]\n\t\t\t\t.map((part) => {\n\t\t\t\t\tif (typeof part === \"string\") return part;\n\t\t\t\t\tif (part instanceof Buffer) return part.toString(\"utf8\");\n\t\t\t\t\treturn \"\";\n\t\t\t\t})\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\treturn out;\n\t\t}\n\t\treturn \"\";\n\t}\n}\n\nfunction discoverServiceNamesDnsSd(): string[] {\n\tconst output = runDnsSd([\"-B\", \"_codemem._tcp\", \"local.\"], 1200);\n\tif (!output) return [];\n\tconst names = new Set<string>();\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.includes(\"Add\")) continue;\n\t\tlet name = \"\";\n\t\tconst columnMatch = line.match(/\\bAdd\\b.*\\slocal\\.\\s+_codemem\\._tcp\\.\\s+(.+)$/);\n\t\tif (columnMatch) {\n\t\t\tname = String(columnMatch[1] ?? \"\").trim();\n\t\t} else {\n\t\t\tconst legacyMatch = line.match(/\\sAdd\\s+\\S+\\s+\\S+\\s+\\S+\\s+(.+)\\._codemem\\._tcp\\./);\n\t\t\tif (legacyMatch) name = String(legacyMatch[1] ?? \"\").trim();\n\t\t}\n\t\tif (name) names.add(name);\n\t}\n\treturn [...names];\n}\n\nfunction resolveServiceDnsSd(name: string): MdnsEntry | null {\n\tconst output = runDnsSd([\"-L\", name, \"_codemem._tcp\", \"local.\"], 1200);\n\tif (!output) return null;\n\n\tconst hostPortMatch = output.match(/can be reached at\\s+([^:\\s]+)\\.?:(\\d+)/i);\n\tconst host = hostPortMatch?.[1] ? String(hostPortMatch[1]).trim() : \"\";\n\tconst port = hostPortMatch?.[2] ? Number.parseInt(String(hostPortMatch[2]), 10) : 0;\n\n\tconst txtDeviceIdMatch = output.match(/device_id=([^\\s\"',]+)/i);\n\tconst deviceId = txtDeviceIdMatch?.[1] ? String(txtDeviceIdMatch[1]).trim() : \"\";\n\n\tif (!host || !port || Number.isNaN(port)) return null;\n\tconst properties: Record<string, string> = {};\n\tif (deviceId) properties.device_id = deviceId;\n\n\treturn {\n\t\tname,\n\t\thost,\n\t\tport,\n\t\tproperties,\n\t};\n}\n\n/**\n * Check if mDNS discovery is enabled via the CODEMEM_SYNC_MDNS env var.\n */\nexport function mdnsEnabled(): boolean {\n\tconst value = process.env.CODEMEM_SYNC_MDNS;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\";\n}\n\n/**\n * Advertise this device via mDNS.\n *\n * Phase 1 stub: logs the intent and returns a no-op closer.\n * Real implementation will use dns-sd (macOS) or bonjour-service (Linux).\n */\nexport function advertiseMdns(_deviceId: string, _port: number): { close(): void } {\n\tif (!mdnsEnabled()) return { close() {} };\n\tif (process.platform !== \"darwin\") return { close() {} };\n\tif (!commandAvailable(\"dns-sd\")) return { close() {} };\n\n\tconst serviceName = `codemem-${_deviceId.slice(0, 12)}`;\n\tconst child = spawn(\n\t\t\"dns-sd\",\n\t\t[\"-R\", serviceName, \"_codemem._tcp\", \"local.\", String(_port), `device_id=${_deviceId}`],\n\t\t{\n\t\t\tstdio: \"ignore\",\n\t\t\tdetached: false,\n\t\t},\n\t);\n\n\treturn {\n\t\tclose() {\n\t\t\tif (!child.killed) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t} catch {\n\t\t\t\t\t// best effort\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\n/**\n * Discover peers via mDNS.\n *\n * Phase 1 stub: returns an empty array.\n * Real implementation will use dns-sd (macOS) or bonjour-service (Linux).\n */\nexport function discoverPeersViaMdns(): MdnsEntry[] {\n\tif (!mdnsEnabled()) return [];\n\tif (process.platform !== \"darwin\") return [];\n\tif (!commandAvailable(\"dns-sd\")) return [];\n\n\tconst names = discoverServiceNamesDnsSd();\n\tif (names.length === 0) return [];\n\n\tconst entries: MdnsEntry[] = [];\n\tfor (const name of names) {\n\t\tconst resolved = resolveServiceDnsSd(name);\n\t\tif (resolved) entries.push(resolved);\n\t}\n\treturn entries;\n}\n\n/**\n * Extract addresses for a specific peer from mDNS discovery entries.\n */\nexport function mdnsAddressesForPeer(peerDeviceId: string, entries: MdnsEntry[]): string[] {\n\tconst addresses: string[] = [];\n\tfor (const entry of entries) {\n\t\tconst props = entry.properties ?? {};\n\t\tlet deviceId: string | Uint8Array | undefined =\n\t\t\tprops.device_id ??\n\t\t\t((props as Record<string, unknown>).device_id as string | Uint8Array | undefined);\n\t\tif (deviceId == null) continue;\n\t\tif (deviceId instanceof Uint8Array) {\n\t\t\tdeviceId = new TextDecoder().decode(deviceId);\n\t\t}\n\t\tif (deviceId !== peerDeviceId) continue;\n\n\t\tconst port = entry.port ?? 0;\n\t\tif (!port) continue;\n\n\t\t// Prefer entry.address (resolved IP) over entry.host (service name).\n\t\t// mDNS responses often carry a service-name host (e.g. _codemem._tcp.local)\n\t\t// that isn't directly dialable, but include the usable IP in address.\n\t\tconst address = (entry as Record<string, unknown>).address as string | undefined;\n\t\tconst host = entry.host ?? \"\";\n\t\tconst dialHost = address && !address.includes(\".local\") ? address : host;\n\t\tif (dialHost && !dialHost.includes(\"_tcp.local\") && !dialHost.includes(\"_udp.local\")) {\n\t\t\taddresses.push(`${dialHost}:${port}`);\n\t\t}\n\t}\n\treturn addresses;\n}\n","/**\n * Sync pass orchestrator: coordinates a single sync exchange with a peer device.\n *\n * Handles the pull→apply→push cycle for replication ops, with address\n * fallback, fingerprint verification, and cursor tracking.\n * Ported from codemem/sync/sync_pass.py.\n */\n\nimport { and, desc, eq, gt, or } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { buildAuthHeaders } from \"./sync-auth.js\";\nimport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity } from \"./sync-identity.js\";\nimport {\n\tapplyReplicationOps,\n\tbackfillReplicationOps,\n\tchunkOpsBySize,\n\textractReplicationOps,\n\tfilterReplicationOpsForSync,\n\tfilterReplicationOpsForSyncWithStatus,\n\tgetReplicationCursor,\n\tmigrateLegacyImportKeys,\n\tsetReplicationCursor,\n} from \"./sync-replication.js\";\nimport type { ReplicationOp } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default max body size for sync POST requests (1 MiB). */\nconst MAX_SYNC_BODY_BYTES = 1_048_576;\n\n/** Default op fetch/push limit per round. */\nconst DEFAULT_LIMIT = 200;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SyncResult {\n\tok: boolean;\n\terror?: string;\n\taddress?: string;\n\topsIn: number;\n\topsOut: number;\n\taddressErrors: Array<{ address: string; error: string }>;\n}\n\nexport interface SyncPassOptions {\n\tlimit?: number;\n\tkeysDir?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a cursor candidate advances beyond the current position.\n *\n * Cursors are `timestamp|op_id` strings that sort lexicographically.\n */\nexport function cursorAdvances(current: string | null, candidate: string | null): boolean {\n\tif (!candidate) return false;\n\tif (!candidate.includes(\"|\")) return false;\n\tif (!current) return true;\n\treturn candidate > current;\n}\n\n/**\n * Extract an error detail string from a JSON response payload.\n */\nfunction errorDetail(payload: Record<string, unknown> | null): string | null {\n\tif (!payload) return null;\n\tconst error = payload.error;\n\tconst reason = payload.reason;\n\tif (typeof error === \"string\" && typeof reason === \"string\") {\n\t\treturn `${error}:${reason}`;\n\t}\n\tif (typeof error === \"string\") return error;\n\treturn null;\n}\n\n/**\n * Summarize address errors into a single error string.\n */\nfunction summarizeAddressErrors(\n\taddressErrors: Array<{ address: string; error: string }>,\n): string | null {\n\tif (addressErrors.length === 0) return null;\n\tconst parts = addressErrors.map((item) => `${item.address}: ${item.error}`);\n\treturn `all addresses failed | ${parts.join(\" || \")}`;\n}\n\n/**\n * Record a sync attempt in the sync_attempts table.\n */\nfunction recordSyncAttempt(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: { ok: boolean; opsIn?: number; opsOut?: number; error?: string },\n): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncAttempts)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tstarted_at: now,\n\t\t\tfinished_at: now,\n\t\t\tok: options.ok ? 1 : 0,\n\t\t\tops_in: options.opsIn ?? 0,\n\t\t\tops_out: options.opsOut ?? 0,\n\t\t\terror: options.error ?? null,\n\t\t})\n\t\t.run();\n}\n\n/**\n * Update last_sync_at and clear last_error on successful sync.\n */\nfunction recordPeerSuccess(db: Database, peerDeviceId: string): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.update(schema.syncPeers)\n\t\t.set({ last_sync_at: now, last_seen_at: now, last_error: null })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n/**\n * Load outbound replication ops for the local device since a cursor.\n *\n * Returns `[ops, nextCursor]`. The cursor is `created_at|op_id`.\n */\nfunction loadLocalOpsSince(\n\tdb: Database,\n\tcursor: string | null,\n\tdeviceId: string,\n\tlimit: number,\n): [ReplicationOp[], string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst { replicationOps: ops } = schema;\n\tlet rows: ReplicationOp[];\n\tif (cursor) {\n\t\tconst sepIdx = cursor.indexOf(\"|\");\n\t\tconst cursorTs = sepIdx >= 0 ? cursor.slice(0, sepIdx) : cursor;\n\t\tconst cursorOpId = sepIdx >= 0 ? cursor.slice(sepIdx + 1) : \"\";\n\t\trows = d\n\t\t\t.select()\n\t\t\t.from(ops)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(ops.device_id, deviceId),\n\t\t\t\t\tor(\n\t\t\t\t\t\tgt(ops.created_at, cursorTs),\n\t\t\t\t\t\tand(eq(ops.created_at, cursorTs), gt(ops.op_id, cursorOpId)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(ops.created_at, ops.op_id)\n\t\t\t.limit(limit)\n\t\t\t.all() as ReplicationOp[];\n\t} else {\n\t\trows = d\n\t\t\t.select()\n\t\t\t.from(ops)\n\t\t\t.where(eq(ops.device_id, deviceId))\n\t\t\t.orderBy(ops.created_at, ops.op_id)\n\t\t\t.limit(limit)\n\t\t\t.all() as ReplicationOp[];\n\t}\n\n\tif (rows.length === 0) return [[], null];\n\tconst last = rows.at(-1);\n\tif (!last) return [[], null];\n\tconst nextCursor = `${last.created_at}|${last.op_id}`;\n\treturn [rows, nextCursor];\n}\n\n/**\n * Compute a cursor string from a timestamp and op_id.\n * Currently unused — will be needed when real apply logic advances the cursor.\n */\nexport function computeCursor(createdAt: string, opId: string): string {\n\treturn `${createdAt}|${opId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Push ops\n// ---------------------------------------------------------------------------\n\n/**\n * Push ops to a peer endpoint with auth headers.\n *\n * On 413 (too large), splits the batch in half and retries recursively.\n */\nconst MAX_PUSH_SPLIT_DEPTH = 8;\n\nasync function pushOps(\n\tpostUrl: string,\n\tdeviceId: string,\n\tops: ReplicationOp[],\n\tkeysDir?: string,\n\tdepth = 0,\n): Promise<void> {\n\tif (ops.length === 0) return;\n\n\tconst body = { ops };\n\tconst bodyBytes = Buffer.from(JSON.stringify(body), \"utf-8\");\n\tconst headers = buildAuthHeaders({\n\t\tdeviceId,\n\t\tmethod: \"POST\",\n\t\turl: postUrl,\n\t\tbodyBytes,\n\t\tkeysDir,\n\t});\n\tconst [status, payload] = await requestJson(\"POST\", postUrl, {\n\t\theaders,\n\t\tbody,\n\t\tbodyBytes,\n\t});\n\n\tif (status === 200 && payload != null) return;\n\n\tconst detail = errorDetail(payload);\n\tif (\n\t\tstatus === 413 &&\n\t\tops.length > 1 &&\n\t\tdepth < MAX_PUSH_SPLIT_DEPTH &&\n\t\t(detail === \"payload_too_large\" || detail === \"too_many_ops\")\n\t) {\n\t\tconst mid = Math.floor(ops.length / 2);\n\t\tawait pushOps(postUrl, deviceId, ops.slice(0, mid), keysDir, depth + 1);\n\t\tawait pushOps(postUrl, deviceId, ops.slice(mid), keysDir, depth + 1);\n\t\treturn;\n\t}\n\n\tconst suffix = detail ? ` (${status}: ${detail})` : ` (${status})`;\n\tthrow new Error(`peer ops push failed${suffix}`);\n}\n\n// ---------------------------------------------------------------------------\n// Preflight\n// ---------------------------------------------------------------------------\n\n/** Run migration + replication backfill preflight. */\nexport function syncPassPreflight(db: Database): void {\n\tmigrateLegacyImportKeys(db, 2000);\n\tbackfillReplicationOps(db, 200);\n}\n\n// ---------------------------------------------------------------------------\n// Main sync loop\n// ---------------------------------------------------------------------------\n\n/**\n * Execute a single sync exchange with a peer across the given addresses.\n *\n * Tries each address in order. On first success, returns immediately.\n * On all failures, collects address-level errors and returns them.\n */\nexport async function syncOnce(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddresses: string[],\n\toptions?: SyncPassOptions,\n): Promise<SyncResult> {\n\tconst limit = options?.limit ?? DEFAULT_LIMIT;\n\tconst keysDir = options?.keysDir;\n\n\t// Look up pinned fingerprint\n\tconst d = drizzle(db, { schema });\n\tconst pinRow = d\n\t\t.select({ pinned_fingerprint: schema.syncPeers.pinned_fingerprint })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\tconst pinnedFingerprint = pinRow?.pinned_fingerprint ?? \"\";\n\tif (!pinnedFingerprint) {\n\t\treturn { ok: false, error: \"peer not pinned\", opsIn: 0, opsOut: 0, addressErrors: [] };\n\t}\n\n\t// Read cursors\n\tlet [lastApplied, lastAcked] = getReplicationCursor(db, peerDeviceId);\n\n\t// Ensure local device identity\n\tlet deviceId: string;\n\ttry {\n\t\t[deviceId] = ensureDeviceIdentity(db, { keysDir });\n\t} catch (err: unknown) {\n\t\tconst detail = err instanceof Error ? err.message.trim() || err.constructor.name : \"unknown\";\n\t\tconst error = `device identity unavailable: ${detail}`;\n\t\trecordSyncAttempt(db, peerDeviceId, { ok: false, error });\n\t\treturn { ok: false, error, opsIn: 0, opsOut: 0, addressErrors: [] };\n\t}\n\n\tconst addressErrors: Array<{ address: string; error: string }> = [];\n\tlet attemptedAny = false;\n\n\tfor (const address of addresses) {\n\t\tconst baseUrl = buildBaseUrl(address);\n\t\tif (!baseUrl) continue;\n\t\tattemptedAny = true;\n\n\t\ttry {\n\t\t\t// -- 1. Verify peer identity via /v1/status --\n\t\t\tconst statusUrl = `${baseUrl}/v1/status`;\n\t\t\tconst statusHeaders = buildAuthHeaders({\n\t\t\t\tdeviceId,\n\t\t\t\tmethod: \"GET\",\n\t\t\t\turl: statusUrl,\n\t\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\t\tkeysDir,\n\t\t\t});\n\t\t\tconst [statusCode, statusPayload] = await requestJson(\"GET\", statusUrl, {\n\t\t\t\theaders: statusHeaders,\n\t\t\t});\n\t\t\tif (statusCode !== 200 || !statusPayload) {\n\t\t\t\tconst detail = errorDetail(statusPayload);\n\t\t\t\tconst suffix = detail ? ` (${statusCode}: ${detail})` : ` (${statusCode})`;\n\t\t\t\tthrow new Error(`peer status failed${suffix}`);\n\t\t\t}\n\t\t\tif (statusPayload.fingerprint !== pinnedFingerprint) {\n\t\t\t\tthrow new Error(\"peer fingerprint mismatch\");\n\t\t\t}\n\n\t\t\t// -- 2. Pull ops from peer --\n\t\t\tconst query = new URLSearchParams({\n\t\t\t\tsince: lastApplied ?? \"\",\n\t\t\t\tlimit: String(limit),\n\t\t\t}).toString();\n\t\t\tconst getUrl = `${baseUrl}/v1/ops?${query}`;\n\t\t\tconst getHeaders = buildAuthHeaders({\n\t\t\t\tdeviceId,\n\t\t\t\tmethod: \"GET\",\n\t\t\t\turl: getUrl,\n\t\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\t\tkeysDir,\n\t\t\t});\n\t\t\tconst [getStatus, getPayload] = await requestJson(\"GET\", getUrl, {\n\t\t\t\theaders: getHeaders,\n\t\t\t});\n\t\t\tif (getStatus !== 200 || getPayload == null) {\n\t\t\t\tconst detail = errorDetail(getPayload);\n\t\t\t\tconst suffix = detail ? ` (${getStatus}: ${detail})` : ` (${getStatus})`;\n\t\t\t\tthrow new Error(`peer ops fetch failed${suffix}`);\n\t\t\t}\n\t\t\tif (!Array.isArray(getPayload.ops)) {\n\t\t\t\tthrow new Error(\"invalid ops response\");\n\t\t\t}\n\t\t\tconst ops = extractReplicationOps(getPayload);\n\t\t\tconst [inboundOps, inboundCursor] = filterReplicationOpsForSyncWithStatus(\n\t\t\t\tdb,\n\t\t\t\tops,\n\t\t\t\tpeerDeviceId,\n\t\t\t);\n\n\t\t\t// -- 3. Apply incoming ops to local entities --\n\t\t\tconst applied = applyReplicationOps(db, inboundOps, deviceId);\n\n\t\t\tconst inboundCursorCandidate =\n\t\t\t\tinboundCursor ||\n\t\t\t\t(typeof getPayload.next_cursor === \"string\" ? getPayload.next_cursor.trim() : \"\");\n\t\t\tif (cursorAdvances(lastApplied, inboundCursorCandidate)) {\n\t\t\t\tsetReplicationCursor(db, peerDeviceId, { lastApplied: inboundCursorCandidate });\n\t\t\t\tlastApplied = inboundCursorCandidate;\n\t\t\t}\n\n\t\t\t// -- 5. Push local ops to peer --\n\t\t\tconst [outboundWindow, outboundCursor] = loadLocalOpsSince(db, lastAcked, deviceId, limit);\n\t\t\tconst [outboundOps, filteredOutboundCursor] = filterReplicationOpsForSync(\n\t\t\t\tdb,\n\t\t\t\toutboundWindow,\n\t\t\t\tpeerDeviceId,\n\t\t\t);\n\t\t\tconst postUrl = `${baseUrl}/v1/ops`;\n\t\t\tif (outboundOps.length > 0) {\n\t\t\t\tconst batches = chunkOpsBySize(outboundOps, MAX_SYNC_BODY_BYTES);\n\t\t\t\tfor (const batch of batches) {\n\t\t\t\t\tawait pushOps(postUrl, deviceId, batch, keysDir);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst ackCursor = filteredOutboundCursor ?? outboundCursor;\n\t\t\tif (ackCursor && cursorAdvances(lastAcked, ackCursor)) {\n\t\t\t\tsetReplicationCursor(db, peerDeviceId, { lastAcked: ackCursor });\n\t\t\t\tlastAcked = ackCursor;\n\t\t\t}\n\n\t\t\t// -- 6. Record success --\n\t\t\trecordPeerSuccess(db, peerDeviceId);\n\t\t\trecordSyncAttempt(db, peerDeviceId, {\n\t\t\t\tok: true,\n\t\t\t\topsIn: applied.applied,\n\t\t\t\topsOut: outboundOps.length,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\taddress: baseUrl,\n\t\t\t\topsIn: applied.applied,\n\t\t\t\topsOut: outboundOps.length,\n\t\t\t\taddressErrors: [],\n\t\t\t};\n\t\t} catch (err: unknown) {\n\t\t\tconst detail = err instanceof Error ? err.message.trim() || err.constructor.name : \"unknown\";\n\t\t\taddressErrors.push({ address: baseUrl, error: detail });\n\t\t}\n\t}\n\n\t// All addresses failed\n\tlet error = summarizeAddressErrors(addressErrors);\n\tif (!attemptedAny) {\n\t\terror = \"no dialable peer addresses\";\n\t}\n\tif (!error) {\n\t\terror = \"sync failed without diagnostic detail\";\n\t}\n\trecordSyncAttempt(db, peerDeviceId, { ok: false, error });\n\treturn { ok: false, error, opsIn: 0, opsOut: 0, addressErrors };\n}\n\n// ---------------------------------------------------------------------------\n// High-level entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Run a sync pass with a peer, resolving addresses from the database.\n *\n * Loads peer addresses from the sync_peers table and delegates to syncOnce.\n */\nexport async function runSyncPass(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions?: SyncPassOptions,\n): Promise<SyncResult> {\n\t// Load stored addresses for this peer\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ addresses_json: schema.syncPeers.addresses_json })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\n\tlet addresses: string[] = [];\n\tif (row?.addresses_json) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(row.addresses_json);\n\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\taddresses = parsed.filter((a): a is string => typeof a === \"string\");\n\t\t\t}\n\t\t} catch {\n\t\t\t// Malformed JSON — proceed with empty addresses\n\t\t}\n\t}\n\n\treturn syncOnce(db, peerDeviceId, addresses, options);\n}\n\n// ---------------------------------------------------------------------------\n// Connectivity backoff (for daemon use)\n// ---------------------------------------------------------------------------\n\n/** Connectivity error patterns indicating peer is offline. */\nconst CONNECTIVITY_ERROR_PATTERNS = [\n\t\"no route to host\",\n\t\"connection refused\",\n\t\"network is unreachable\",\n\t\"timed out\",\n\t\"name or service not known\",\n\t\"nodename nor servname provided\",\n\t\"errno 65\",\n\t\"errno 61\",\n\t\"errno 60\",\n\t\"errno 111\",\n] as const;\n\nconst PEER_BACKOFF_BASE_S = 120;\nconst PEER_BACKOFF_MAX_S = 1800;\n\n/** Check if an error string indicates a network connectivity failure. */\nexport function isConnectivityError(error: string | null): boolean {\n\tif (!error) return false;\n\tconst lower = error.toLowerCase();\n\treturn CONNECTIVITY_ERROR_PATTERNS.some((pattern) => lower.includes(pattern));\n}\n\n/** Count consecutive recent failures that are connectivity errors. */\nexport function consecutiveConnectivityFailures(\n\tdb: Database,\n\tpeerDeviceId: string,\n\tlimit = 10,\n): number {\n\tconst d = drizzle(db, { schema });\n\tconst rows = d\n\t\t.select({ ok: schema.syncAttempts.ok, error: schema.syncAttempts.error })\n\t\t.from(schema.syncAttempts)\n\t\t.where(eq(schema.syncAttempts.peer_device_id, peerDeviceId))\n\t\t.orderBy(desc(schema.syncAttempts.started_at))\n\t\t.limit(limit)\n\t\t.all();\n\n\tlet count = 0;\n\tfor (const row of rows) {\n\t\tif (row.ok) break;\n\t\tif (isConnectivityError(row.error)) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn count;\n}\n\n/** Calculate backoff duration with jitter to avoid thundering herd. */\nexport function peerBackoffSeconds(consecutiveFailures: number): number {\n\tif (consecutiveFailures <= 1) return 0;\n\tconst exponent = Math.min(consecutiveFailures - 1, 8);\n\tconst base = Math.min(PEER_BACKOFF_BASE_S * 2 ** (exponent - 1), PEER_BACKOFF_MAX_S);\n\t// Add 50% jitter: base * [0.5, 1.0)\n\treturn base * (0.5 + Math.random() * 0.5);\n}\n\n/** Check if a peer should be skipped due to repeated connectivity failures. */\nexport function shouldSkipOfflinePeer(db: Database, peerDeviceId: string): boolean {\n\tconst failures = consecutiveConnectivityFailures(db, peerDeviceId);\n\tif (failures < 2) return false;\n\tconst backoffS = peerBackoffSeconds(failures);\n\tif (backoffS <= 0) return false;\n\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ started_at: schema.syncAttempts.started_at })\n\t\t.from(schema.syncAttempts)\n\t\t.where(eq(schema.syncAttempts.peer_device_id, peerDeviceId))\n\t\t.orderBy(desc(schema.syncAttempts.started_at))\n\t\t.limit(1)\n\t\t.get();\n\n\tif (!row?.started_at) return false;\n\ttry {\n\t\tconst lastAttempt = new Date(row.started_at).getTime();\n\t\tconst now = Date.now();\n\t\tconst elapsedS = (now - lastAttempt) / 1000;\n\t\treturn elapsedS < backoffS;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","/**\n * Sync daemon: periodic background sync with all configured peers.\n *\n * Uses AbortSignal for cancellation and setInterval for periodic ticks.\n * Ported from codemem/sync/daemon.py — HTTP server portion is deferred\n * to the viewer-server Hono routes.\n */\n\nimport { sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { connect as connectDb, resolveDbPath } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { advertiseMdns, mdnsEnabled } from \"./sync-discovery.js\";\nimport { ensureDeviceIdentity } from \"./sync-identity.js\";\nimport { runSyncPass, shouldSkipOfflinePeer, syncPassPreflight } from \"./sync-pass.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SyncDaemonOptions {\n\thost?: string;\n\tport?: number;\n\tintervalS?: number;\n\tdbPath?: string;\n\tkeysDir?: string;\n\tsignal?: AbortSignal;\n}\n\nexport interface SyncTickResult {\n\tok: boolean;\n\tskipped?: boolean;\n\treason?: string;\n\terror?: string;\n\topsIn?: number;\n\topsOut?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Daemon state helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Record a successful daemon tick in sync_daemon_state.\n */\nexport function setSyncDaemonOk(db: Database): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncDaemonState)\n\t\t.values({ id: 1, last_ok_at: now })\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncDaemonState.id,\n\t\t\tset: { last_ok_at: sql`excluded.last_ok_at` },\n\t\t})\n\t\t.run();\n}\n\n/**\n * Record a daemon tick error in sync_daemon_state.\n */\nexport function setSyncDaemonError(db: Database, error: string, traceback?: string): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncDaemonState)\n\t\t.values({\n\t\t\tid: 1,\n\t\t\tlast_error: error,\n\t\t\tlast_traceback: traceback ?? null,\n\t\t\tlast_error_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncDaemonState.id,\n\t\t\tset: {\n\t\t\t\tlast_error: sql`excluded.last_error`,\n\t\t\t\tlast_traceback: sql`excluded.last_traceback`,\n\t\t\t\tlast_error_at: sql`excluded.last_error_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n}\n\n// ---------------------------------------------------------------------------\n// Sync tick\n// ---------------------------------------------------------------------------\n\n/**\n * Run one sync tick: iterate over all enabled peers and sync each.\n *\n * Returns per-peer results. Peers in backoff are skipped.\n */\nexport async function syncDaemonTick(db: Database, keysDir?: string): Promise<SyncTickResult[]> {\n\tconst d = drizzle(db, { schema });\n\tconst rows = d\n\t\t.select({ peer_device_id: schema.syncPeers.peer_device_id })\n\t\t.from(schema.syncPeers)\n\t\t.all();\n\n\t// Skip heavy preflight work when there are no peers configured.\n\t// This keeps startup and idle daemon ticks responsive on large local stores.\n\tif (rows.length === 0) {\n\t\treturn [];\n\t}\n\n\tsyncPassPreflight(db);\n\n\tconst results: SyncTickResult[] = [];\n\tfor (const row of rows) {\n\t\tconst peerDeviceId = row.peer_device_id;\n\n\t\tif (shouldSkipOfflinePeer(db, peerDeviceId)) {\n\t\t\tresults.push({ ok: false, skipped: true, reason: \"peer offline (backoff)\" });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst result = await runSyncPass(db, peerDeviceId, { keysDir });\n\t\tresults.push({\n\t\t\tok: result.ok,\n\t\t\terror: result.error,\n\t\t\topsIn: result.opsIn,\n\t\t\topsOut: result.opsOut,\n\t\t});\n\t}\n\n\treturn results;\n}\n\n// ---------------------------------------------------------------------------\n// Daemon loop\n// ---------------------------------------------------------------------------\n\n/**\n * Run the sync daemon loop.\n *\n * 1. Ensures device identity\n * 2. Starts mDNS advertising (if enabled)\n * 3. Runs an initial sync tick\n * 4. Sets up an interval timer for periodic sync\n * 5. Waits for abort signal to stop\n * 6. Cleans up on exit\n */\nexport async function runSyncDaemon(options?: SyncDaemonOptions): Promise<void> {\n\tconst intervalS = options?.intervalS ?? 120;\n\tconst dbPath = resolveDbPath(options?.dbPath);\n\tconst keysDir = options?.keysDir;\n\tconst signal = options?.signal;\n\n\t// Ensure device identity\n\tconst db = connectDb(dbPath);\n\tlet mdnsHandle: { close(): void } | null = null;\n\ttry {\n\t\tconst [deviceId] = ensureDeviceIdentity(db, { keysDir });\n\n\t\t// Start mDNS advertising if enabled\n\t\tif (mdnsEnabled() && options?.port) {\n\t\t\tmdnsHandle = advertiseMdns(deviceId, options.port);\n\t\t}\n\t} finally {\n\t\tdb.close();\n\t}\n\n\t// Check cancellation before startup tick\n\tif (signal?.aborted) {\n\t\tmdnsHandle?.close();\n\t\treturn;\n\t}\n\n\t// Run initial tick\n\tawait runTickOnce(dbPath, keysDir);\n\n\t// If aborted during initial tick, clean up\n\tif (signal?.aborted) {\n\t\tmdnsHandle?.close();\n\t\treturn;\n\t}\n\n\t// Set up periodic ticks — serialized to avoid overlapping sync passes\n\treturn new Promise<void>((resolve) => {\n\t\tlet tickRunning = false;\n\t\tconst timer = setInterval(() => {\n\t\t\tif (tickRunning) return; // Skip if previous tick still running\n\t\t\ttickRunning = true;\n\t\t\trunTickOnce(dbPath, keysDir).finally(() => {\n\t\t\t\ttickRunning = false;\n\t\t\t});\n\t\t}, intervalS * 1000);\n\n\t\tconst cleanup = () => {\n\t\t\tclearInterval(timer);\n\t\t\tmdnsHandle?.close();\n\t\t\tresolve();\n\t\t};\n\n\t\tif (signal) {\n\t\t\tif (signal.aborted) {\n\t\t\t\tcleanup();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsignal.addEventListener(\"abort\", cleanup, { once: true });\n\t\t}\n\t});\n}\n\n/**\n * Run a single tick, opening and closing a DB connection.\n *\n * Errors are caught and recorded in sync_daemon_state.\n */\nasync function runTickOnce(dbPath: string, keysDir?: string): Promise<void> {\n\tconst db = connectDb(dbPath);\n\ttry {\n\t\tawait syncDaemonTick(db, keysDir);\n\t\tsetSyncDaemonOk(db);\n\t} catch (err: unknown) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconst stack = err instanceof Error ? (err.stack ?? \"\") : \"\";\n\t\tsetSyncDaemonError(db, message, stack);\n\t} finally {\n\t\tdb.close();\n\t}\n}\n","/**\n * AUTO-GENERATED by scripts/generate-test-schema.ts\n *\n * Do not edit manually. Regenerate with:\n * pnpm --filter @codemem/core run generate:test-schema\n */\n\nexport const TEST_SCHEMA_BASE_DDL =\n\t\"CREATE TABLE IF NOT EXISTS `sessions` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`started_at` text NOT NULL,\\n\\t`ended_at` text,\\n\\t`cwd` text,\\n\\t`project` text,\\n\\t`git_remote` text,\\n\\t`git_branch` text,\\n\\t`user` text,\\n\\t`tool_version` text,\\n\\t`metadata_json` text,\\n\\t`import_key` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `artifacts` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer NOT NULL,\\n\\t`kind` text NOT NULL,\\n\\t`path` text,\\n\\t`content_text` text,\\n\\t`content_hash` text,\\n\\t`created_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_artifacts_session_kind` ON `artifacts` (`session_id`,`kind`);\\nCREATE TABLE IF NOT EXISTS `memory_items` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer NOT NULL,\\n\\t`kind` text NOT NULL,\\n\\t`title` text NOT NULL,\\n\\t`subtitle` text,\\n\\t`body_text` text NOT NULL,\\n\\t`confidence` real DEFAULT 0.5,\\n\\t`tags_text` text DEFAULT '',\\n\\t`active` integer DEFAULT 1,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\t`actor_id` text,\\n\\t`actor_display_name` text,\\n\\t`visibility` text,\\n\\t`workspace_id` text,\\n\\t`workspace_kind` text,\\n\\t`origin_device_id` text,\\n\\t`origin_source` text,\\n\\t`trust_state` text,\\n\\t`facts` text,\\n\\t`narrative` text,\\n\\t`concepts` text,\\n\\t`files_read` text,\\n\\t`files_modified` text,\\n\\t`user_prompt_id` integer,\\n\\t`prompt_number` integer,\\n\\t`deleted_at` text,\\n\\t`rev` integer DEFAULT 0,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_memory_items_active_created` ON `memory_items` (`active`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_memory_items_session` ON `memory_items` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `usage_events` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`event` text NOT NULL,\\n\\t`tokens_read` integer DEFAULT 0,\\n\\t`tokens_written` integer DEFAULT 0,\\n\\t`tokens_saved` integer DEFAULT 0,\\n\\t`created_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE set null\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_usage_events_event_created` ON `usage_events` (`event`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_usage_events_session` ON `usage_events` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `raw_events` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`event_id` text,\\n\\t`event_seq` integer NOT NULL,\\n\\t`event_type` text NOT NULL,\\n\\t`ts_wall_ms` integer,\\n\\t`ts_mono_ms` real,\\n\\t`payload_json` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_raw_events_source_stream_seq` ON `raw_events` (`source`,`stream_id`,`event_seq`);\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_raw_events_source_stream_event_id` ON `raw_events` (`source`,`stream_id`,`event_id`);\\nCREATE INDEX IF NOT EXISTS `idx_raw_events_session_seq` ON `raw_events` (`opencode_session_id`,`event_seq`);\\nCREATE INDEX IF NOT EXISTS `idx_raw_events_created` ON `raw_events` (`created_at`);\\nCREATE TABLE IF NOT EXISTS `raw_event_sessions` (\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`cwd` text,\\n\\t`project` text,\\n\\t`started_at` text,\\n\\t`last_seen_ts_wall_ms` integer,\\n\\t`last_received_event_seq` integer DEFAULT -1 NOT NULL,\\n\\t`last_flushed_event_seq` integer DEFAULT -1 NOT NULL,\\n\\t`updated_at` text NOT NULL,\\n\\tPRIMARY KEY(`source`, `stream_id`)\\n);\\n\\nCREATE TABLE IF NOT EXISTS `opencode_sessions` (\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`session_id` integer,\\n\\t`created_at` text NOT NULL,\\n\\tPRIMARY KEY(`source`, `stream_id`),\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_opencode_sessions_session` ON `opencode_sessions` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `raw_event_flush_batches` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`start_event_seq` integer NOT NULL,\\n\\t`end_event_seq` integer NOT NULL,\\n\\t`extractor_version` text NOT NULL,\\n\\t`status` text NOT NULL,\\n\\t`error_message` text,\\n\\t`error_type` text,\\n\\t`observer_provider` text,\\n\\t`observer_model` text,\\n\\t`observer_runtime` text,\\n\\t`attempt_count` integer DEFAULT 0 NOT NULL,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_flush_batches_source_stream_seq_ver` ON `raw_event_flush_batches` (`source`,`stream_id`,`start_event_seq`,`end_event_seq`,`extractor_version`);\\nCREATE INDEX IF NOT EXISTS `idx_flush_batches_session_created` ON `raw_event_flush_batches` (`opencode_session_id`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_flush_batches_status_updated` ON `raw_event_flush_batches` (`status`,`updated_at`);\\nCREATE TABLE IF NOT EXISTS `user_prompts` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`project` text,\\n\\t`prompt_text` text NOT NULL,\\n\\t`prompt_number` integer,\\n\\t`created_at` text NOT NULL,\\n\\t`created_at_epoch` integer NOT NULL,\\n\\t`metadata_json` text,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_session` ON `user_prompts` (`session_id`);\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_project` ON `user_prompts` (`project`);\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_epoch` ON `user_prompts` (`created_at_epoch`);\\nCREATE TABLE IF NOT EXISTS `session_summaries` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`project` text,\\n\\t`request` text,\\n\\t`investigated` text,\\n\\t`learned` text,\\n\\t`completed` text,\\n\\t`next_steps` text,\\n\\t`notes` text,\\n\\t`files_read` text,\\n\\t`files_edited` text,\\n\\t`prompt_number` integer,\\n\\t`created_at` text NOT NULL,\\n\\t`created_at_epoch` integer NOT NULL,\\n\\t`metadata_json` text,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_session` ON `session_summaries` (`session_id`);\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_project` ON `session_summaries` (`project`);\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_epoch` ON `session_summaries` (`created_at_epoch`);\\nCREATE TABLE IF NOT EXISTS `replication_ops` (\\n\\t`op_id` text PRIMARY KEY NOT NULL,\\n\\t`entity_type` text NOT NULL,\\n\\t`entity_id` text NOT NULL,\\n\\t`op_type` text NOT NULL,\\n\\t`payload_json` text,\\n\\t`clock_rev` integer NOT NULL,\\n\\t`clock_updated_at` text NOT NULL,\\n\\t`clock_device_id` text NOT NULL,\\n\\t`device_id` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_replication_ops_created` ON `replication_ops` (`created_at`,`op_id`);\\nCREATE INDEX IF NOT EXISTS `idx_replication_ops_entity` ON `replication_ops` (`entity_type`,`entity_id`);\\nCREATE TABLE IF NOT EXISTS `replication_cursors` (\\n\\t`peer_device_id` text PRIMARY KEY NOT NULL,\\n\\t`last_applied_cursor` text,\\n\\t`last_acked_cursor` text,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_peers` (\\n\\t`peer_device_id` text PRIMARY KEY NOT NULL,\\n\\t`name` text,\\n\\t`pinned_fingerprint` text,\\n\\t`public_key` text,\\n\\t`addresses_json` text,\\n\\t`claimed_local_actor` integer DEFAULT 0 NOT NULL,\\n\\t`actor_id` text,\\n\\t`projects_include_json` text,\\n\\t`projects_exclude_json` text,\\n\\t`created_at` text NOT NULL,\\n\\t`last_seen_at` text,\\n\\t`last_sync_at` text,\\n\\t`last_error` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_nonces` (\\n\\t`nonce` text PRIMARY KEY NOT NULL,\\n\\t`device_id` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_device` (\\n\\t`device_id` text PRIMARY KEY NOT NULL,\\n\\t`public_key` text NOT NULL,\\n\\t`fingerprint` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_attempts` (\\n\\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\\n\\t`peer_device_id` text NOT NULL,\\n\\t`started_at` text NOT NULL,\\n\\t`finished_at` text,\\n\\t`ok` integer NOT NULL,\\n\\t`ops_in` integer NOT NULL,\\n\\t`ops_out` integer NOT NULL,\\n\\t`error` text\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_sync_attempts_peer_started` ON `sync_attempts` (`peer_device_id`,`started_at`);\\nCREATE TABLE IF NOT EXISTS `sync_daemon_state` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`last_error` text,\\n\\t`last_traceback` text,\\n\\t`last_error_at` text,\\n\\t`last_ok_at` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `raw_event_ingest_samples` (\\n\\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\\n\\t`created_at` text NOT NULL,\\n\\t`inserted_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_invalid` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_duplicate` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_conflict` integer DEFAULT 0 NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `raw_event_ingest_stats` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`inserted_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_invalid` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_duplicate` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_conflict` integer DEFAULT 0 NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `actors` (\\n\\t`actor_id` text PRIMARY KEY NOT NULL,\\n\\t`display_name` text NOT NULL,\\n\\t`is_local` integer DEFAULT 0 NOT NULL,\\n\\t`status` text DEFAULT 'active' NOT NULL,\\n\\t`merged_into_actor_id` text,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_actors_is_local` ON `actors` (`is_local`);\\nCREATE INDEX IF NOT EXISTS `idx_actors_status` ON `actors` (`status`);\";\n","/**\n * Test utilities for the codemem TS backend.\n */\n\nimport type { Database } from \"./db.js\";\nimport { SCHEMA_VERSION } from \"./db.js\";\nimport { TEST_SCHEMA_BASE_DDL } from \"./test-schema.generated.js\";\n\nconst TEST_SCHEMA_AUX_DDL = `\nCREATE INDEX IF NOT EXISTS idx_sync_peers_actor_id ON sync_peers(actor_id);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(\n\ttitle, body_text, tags_text,\n\tcontent='memory_items',\n\tcontent_rowid='id'\n);\n\nCREATE TRIGGER IF NOT EXISTS memory_items_ai AFTER INSERT ON memory_items BEGIN\n\tINSERT INTO memory_fts(rowid, title, body_text, tags_text)\n\tVALUES (new.id, new.title, new.body_text, new.tags_text);\nEND;\n\nDROP TRIGGER IF EXISTS memory_items_au;\nCREATE TRIGGER memory_items_au AFTER UPDATE ON memory_items BEGIN\n\tINSERT INTO memory_fts(memory_fts, rowid, title, body_text, tags_text)\n\tVALUES('delete', old.id, old.title, old.body_text, old.tags_text);\n\tINSERT INTO memory_fts(rowid, title, body_text, tags_text)\n\tVALUES (new.id, new.title, new.body_text, new.tags_text);\nEND;\n\nDROP TRIGGER IF EXISTS memory_items_ad;\nCREATE TRIGGER memory_items_ad AFTER DELETE ON memory_items BEGIN\n\tINSERT INTO memory_fts(memory_fts, rowid, title, body_text, tags_text)\n\tVALUES('delete', old.id, old.title, old.body_text, old.tags_text);\nEND;\n`;\n\n/**\n * Create the full schema for test databases.\n */\nexport function initTestSchema(db: Database): void {\n\tdb.exec(TEST_SCHEMA_BASE_DDL);\n\tdb.exec(TEST_SCHEMA_AUX_DDL);\n\tdb.pragma(`user_version = ${SCHEMA_VERSION}`);\n}\n\n/**\n * Insert a minimal test session and return its ID.\n */\nexport function insertTestSession(db: Database): number {\n\tconst now = new Date().toISOString();\n\tconst info = db\n\t\t.prepare(\n\t\t\t\"INSERT INTO sessions(started_at, cwd, project, user, tool_version) VALUES (?, ?, ?, ?, ?)\",\n\t\t)\n\t\t.run(now, \"/tmp/test\", \"test-project\", \"test-user\", \"test\");\n\treturn Number(info.lastInsertRowid);\n}\n\n// Re-export for test convenience\nexport { MemoryStore } from \"./store.js\";\n","/**\n * @codemem/core — store, embeddings, and shared types.\n *\n * This package owns the SQLite store, embedding worker interface,\n * and type definitions shared across the codemem TS backend.\n */\n\nexport const VERSION = \"0.20.3\";\n\nexport * as Api from \"./api-types.js\";\nexport type { ClaudeHookAdapterEvent, ClaudeHookRawEventEnvelope } from \"./claude-hooks.js\";\nexport {\n\tbuildIngestPayloadFromHook,\n\tbuildRawEventEnvelopeFromHook,\n\tMAPPABLE_CLAUDE_HOOK_EVENTS,\n\tmapClaudeHookPayload,\n\tnormalizeProjectLabel,\n\tresolveHookProject,\n} from \"./claude-hooks.js\";\nexport {\n\tcoordinatorCreateInviteAction,\n\tcoordinatorImportInviteAction,\n\tcoordinatorListJoinRequestsAction,\n\tcoordinatorReviewJoinRequestAction,\n} from \"./coordinator-actions.js\";\nexport { createCoordinatorApp } from \"./coordinator-api.js\";\nexport type { InvitePayload } from \"./coordinator-invites.js\";\nexport {\n\tdecodeInvitePayload,\n\tencodeInvitePayload,\n\textractInvitePayload,\n\tinviteLink,\n} from \"./coordinator-invites.js\";\nexport {\n\tcoordinatorEnabled,\n\tcoordinatorStatusSnapshot,\n\tlistCoordinatorJoinRequests,\n\tlookupCoordinatorPeers,\n\treadCoordinatorSyncConfig,\n\tregisterCoordinatorPresence,\n} from \"./coordinator-runtime.js\";\nexport {\n\tCoordinatorStore,\n\tconnectCoordinator,\n\tDEFAULT_COORDINATOR_DB_PATH,\n} from \"./coordinator-store.js\";\nexport type { Database } from \"./db.js\";\nexport {\n\tassertSchemaReady,\n\tbackupOnFirstAccess,\n\tconnect,\n\tDEFAULT_DB_PATH,\n\tfromJson,\n\tfromJsonStrict,\n\tgetSchemaVersion,\n\tisEmbeddingDisabled,\n\tloadSqliteVec,\n\tMIN_COMPATIBLE_SCHEMA,\n\tmigrateLegacyDbPath,\n\tresolveDbPath,\n\tSCHEMA_VERSION,\n\ttableExists,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nexport type { EmbeddingClient } from \"./embeddings.js\";\nexport {\n\t_resetEmbeddingClient,\n\tchunkText,\n\tembedTexts,\n\tgetEmbeddingClient,\n\thashText,\n\tserializeFloat32,\n} from \"./embeddings.js\";\nexport type { ExportOptions, ExportPayload, ImportOptions, ImportResult } from \"./export-import.js\";\nexport {\n\tbuildImportKey,\n\texportMemories,\n\timportMemories,\n\tmergeSummaryMetadata,\n\treadImportPayload,\n} from \"./export-import.js\";\nexport { buildFilterClauses, buildFilterClausesWithContext } from \"./filters.js\";\n// Ingest pipeline\nexport {\n\tbudgetToolEvents,\n\teventToToolEvent,\n\textractAdapterEvent,\n\textractToolEvents,\n\tisInternalMemoryTool,\n\tLOW_SIGNAL_TOOLS,\n\tnormalizeToolName,\n\tprojectAdapterToolEvent,\n} from \"./ingest-events.js\";\nexport { isLowSignalObservation, normalizeObservation } from \"./ingest-filters.js\";\nexport type { IngestOptions } from \"./ingest-pipeline.js\";\nexport { cleanOrphanSessions, ingest, main as ingestMain } from \"./ingest-pipeline.js\";\nexport { buildObserverPrompt } from \"./ingest-prompts.js\";\nexport {\n\tisSensitiveFieldName,\n\tsanitizePayload,\n\tsanitizeToolOutput,\n\tstripPrivate,\n\tstripPrivateObj,\n} from \"./ingest-sanitize.js\";\nexport {\n\tbuildTranscript,\n\tderiveRequest,\n\textractAssistantMessages,\n\textractAssistantUsage,\n\textractPrompts,\n\tfirstSentence,\n\tisTrivialRequest,\n\tnormalizeAdapterEvents,\n\tnormalizeRequestText,\n\tTRIVIAL_REQUESTS,\n} from \"./ingest-transcript.js\";\nexport type {\n\tIngestPayload,\n\tObserverContext,\n\tParsedObservation,\n\tParsedOutput,\n\tParsedSummary,\n\tSessionContext,\n\tToolEvent,\n} from \"./ingest-types.js\";\nexport { hasMeaningfulObservation, parseObserverResponse } from \"./ingest-xml-parser.js\";\nexport { parsePositiveMemoryId, parseStrictInteger } from \"./integers.js\";\nexport type {\n\tBackfillTagsTextOptions,\n\tBackfillTagsTextResult,\n\tDeactivateLowSignalMemoriesOptions,\n\tDeactivateLowSignalResult,\n\tGateResult,\n\tRawEventStatusItem,\n\tRawEventStatusResult,\n\tReliabilityMetrics,\n} from \"./maintenance.js\";\nexport {\n\tbackfillTagsText,\n\tdeactivateLowSignalMemories,\n\tdeactivateLowSignalObservations,\n\tgetRawEventStatus,\n\tgetReliabilityMetrics,\n\tinitDatabase,\n\trawEventsGate,\n\tretryRawEventFailures,\n\tvacuumDatabase,\n} from \"./maintenance.js\";\nexport type { ObserverAuthMaterial } from \"./observer-auth.js\";\nexport {\n\tbuildCodexHeaders,\n\textractOAuthAccess,\n\textractOAuthAccountId,\n\textractOAuthExpires,\n\textractProviderApiKey,\n\tloadOpenCodeOAuthCache,\n\tObserverAuthAdapter,\n\tprobeAvailableCredentials,\n\treadAuthFile,\n\tredactText,\n\trenderObserverHeaders,\n\tresolveOAuthProvider,\n\trunAuthCommand,\n} from \"./observer-auth.js\";\nexport type { ObserverConfig, ObserverResponse, ObserverStatus } from \"./observer-client.js\";\nexport { loadObserverConfig, ObserverAuthError, ObserverClient } from \"./observer-client.js\";\nexport {\n\tCODEMEM_CONFIG_ENV_OVERRIDES,\n\tgetCodememConfigPath,\n\tgetCodememEnvOverrides,\n\tgetOpenCodeProviderConfig,\n\tgetProviderApiKey,\n\tgetProviderBaseUrl,\n\tgetProviderHeaders,\n\tgetProviderOptions,\n\tlistConfiguredOpenCodeProviders,\n\tlistCustomProviders,\n\tlistObserverProviderOptions,\n\tloadOpenCodeConfig,\n\treadCodememConfigFile,\n\tresolveBuiltInProviderDefaultModel,\n\tresolveBuiltInProviderFromModel,\n\tresolveBuiltInProviderModel,\n\tresolveCustomProviderDefaultModel,\n\tresolveCustomProviderFromModel,\n\tresolveCustomProviderModel,\n\tresolvePlaceholder,\n\tstripJsonComments,\n\tstripTrailingCommas,\n\twriteCodememConfigFile,\n} from \"./observer-config.js\";\nexport { buildMemoryPack, buildMemoryPackAsync, estimateTokens } from \"./pack.js\";\nexport {\n\tprojectBasename,\n\tprojectClause,\n\tprojectColumnClause,\n\tprojectMatchesFilter,\n\tresolveProject,\n} from \"./project.js\";\nexport type { FlushRawEventsOptions } from \"./raw-event-flush.js\";\nexport { buildSessionContext, flushRawEvents } from \"./raw-event-flush.js\";\nexport { RawEventSweeper } from \"./raw-event-sweeper.js\";\nexport * as schema from \"./schema.js\";\nexport type { StoreHandle } from \"./search.js\";\nexport {\n\tdedupeOrderedIds,\n\texpandQuery,\n\texplain,\n\tkindBonus,\n\trecencyScore,\n\trerankResults,\n\tsearch,\n\ttimeline,\n} from \"./search.js\";\nexport { MemoryStore } from \"./store.js\";\nexport type {\n\tBuildAuthHeadersOptions,\n\tSignRequestOptions,\n\tVerifySignatureOptions,\n} from \"./sync-auth.js\";\nexport {\n\tbuildAuthHeaders,\n\tbuildCanonicalRequest,\n\tcleanupNonces,\n\tDEFAULT_TIME_WINDOW_S,\n\trecordNonce,\n\tSIGNATURE_VERSION,\n\tsignRequest,\n\tverifySignature,\n} from \"./sync-auth.js\";\nexport type { SyncDaemonOptions, SyncTickResult } from \"./sync-daemon.js\";\nexport {\n\trunSyncDaemon,\n\tsetSyncDaemonError,\n\tsetSyncDaemonOk,\n\tsyncDaemonTick,\n} from \"./sync-daemon.js\";\nexport type { MdnsEntry } from \"./sync-discovery.js\";\nexport {\n\taddressDedupeKey,\n\tadvertiseMdns,\n\tDEFAULT_SERVICE_TYPE,\n\tdiscoverPeersViaMdns,\n\tloadPeerAddresses,\n\tmdnsAddressesForPeer,\n\tmdnsEnabled,\n\tmergeAddresses,\n\tnormalizeAddress,\n\trecordPeerSuccess,\n\trecordSyncAttempt,\n\tselectDialAddresses,\n\tsetPeerLocalActorClaim,\n\tsetPeerProjectFilter,\n\tupdatePeerAddresses,\n} from \"./sync-discovery.js\";\nexport type { RequestJsonOptions } from \"./sync-http-client.js\";\nexport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nexport type { EnsureDeviceIdentityOptions } from \"./sync-identity.js\";\nexport {\n\tensureDeviceIdentity,\n\tfingerprintPublicKey,\n\tgenerateKeypair,\n\tloadPrivateKey,\n\tloadPrivateKeyKeychain,\n\tloadPublicKey,\n\tresolveKeyPaths,\n\tstorePrivateKeyKeychain,\n\tvalidateExistingKeypair,\n} from \"./sync-identity.js\";\nexport type { SyncPassOptions, SyncResult } from \"./sync-pass.js\";\nexport {\n\tconsecutiveConnectivityFailures,\n\tcursorAdvances,\n\tisConnectivityError,\n\tpeerBackoffSeconds,\n\trunSyncPass,\n\tshouldSkipOfflinePeer,\n\tsyncOnce,\n\tsyncPassPreflight,\n} from \"./sync-pass.js\";\nexport type { ApplyResult } from \"./sync-replication.js\";\nexport {\n\tapplyReplicationOps,\n\tbackfillReplicationOps,\n\tchunkOpsBySize,\n\tclockTuple,\n\textractReplicationOps,\n\tfilterReplicationOpsForSync,\n\tfilterReplicationOpsForSyncWithStatus,\n\tgetReplicationCursor,\n\tisNewerClock,\n\tloadReplicationOpsSince,\n\tmigrateLegacyImportKeys,\n\trecordReplicationOp,\n\tsetReplicationCursor,\n} from \"./sync-replication.js\";\nexport { deriveTags, fileTags, normalizeTag } from \"./tags.js\";\n// Test utilities (exported for consumer packages like viewer-server)\nexport { initTestSchema, insertTestSession } from \"./test-utils.js\";\nexport type {\n\tActor,\n\tArtifact,\n\tExplainError,\n\tExplainItem,\n\tExplainResponse,\n\tExplainScoreComponents,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tOpenCodeSession,\n\tPackItem,\n\tPackResponse,\n\tRawEvent,\n\tRawEventFlushBatch,\n\tRawEventIngestSample,\n\tRawEventIngestStats,\n\tRawEventSession,\n\tReplicationClock,\n\tReplicationCursor,\n\tReplicationOp,\n\tSession,\n\tSessionSummary,\n\tStoreStats,\n\tSyncAttempt,\n\tSyncDaemonState,\n\tSyncDevice,\n\tSyncNonce,\n\tSyncPeer,\n\tTimelineItemResponse,\n\tUsageEvent,\n\tUserPrompt,\n} from \"./types.js\";\nexport type {\n\tBackfillVectorsOptions,\n\tBackfillVectorsResult,\n\tSemanticSearchResult,\n} from \"./vectors.js\";\nexport { backfillVectors, semanticSearch, storeVectors } from \"./vectors.js\";\n"],"x_google_ignoreList":[13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["#matchResult","#validatedData","#getDecodedParam","#getAllDecodedParams","#getParamValue","#cachedBody","#rawRequest","#executionCtx","#notFoundHandler","#path","#matchResult","#req","#res","#preparedHeaders","#renderer","#layout","#status","#var","#newResponse","Hono","#path","#addRoute","#notFoundHandler","#clone","#dispatch","#handleError","Node","#index","#children","#varIndex","Node","#root","#context","#middleware","#routes","#buildMatcher","#routers","#routes","#children","#methods","#patterns","#order","#params","#pushHandlerSets","#node","HonoBase"],"sources":["../src/api-types.ts","../src/claude-hooks.ts","../src/coordinator-invites.ts","../src/coordinator-store.ts","../src/observer-config.ts","../src/db.ts","../src/ingest-filters.ts","../src/project.ts","../src/schema.ts","../src/maintenance.ts","../src/sync-http-client.ts","../src/sync-identity.ts","../src/coordinator-actions.ts","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/compose.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/request/constants.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/body.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/url.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/request.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/html.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/context.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/utils/constants.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/hono-base.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/matcher.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/node.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/trie.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/reg-exp-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/smart-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/trie-router/node.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/router/trie-router/router.js","../../../node_modules/.pnpm/hono@4.12.8/node_modules/hono/dist/hono.js","../src/sync-auth.ts","../src/coordinator-api.ts","../src/address-utils.ts","../src/coordinator-runtime.ts","../src/embeddings.ts","../src/export-import.ts","../src/filters.ts","../src/ingest-sanitize.ts","../src/ingest-events.ts","../src/ingest-prompts.ts","../src/ingest-transcript.ts","../src/ingest-xml-parser.ts","../src/tags.ts","../src/vectors.ts","../src/ingest-pipeline.ts","../src/integers.ts","../src/observer-auth.ts","../src/observer-client.ts","../src/search.ts","../src/pack.ts","../src/raw-event-flush.ts","../src/raw-event-sweeper.ts","../src/sync-replication.ts","../src/store.ts","../src/sync-discovery.ts","../src/sync-pass.ts","../src/sync-daemon.ts","../src/test-schema.generated.ts","../src/test-utils.ts","../src/index.ts"],"sourcesContent":["/**\n * Shared HTTP API response types for the codemem viewer endpoints.\n *\n * These types define the JSON contract between the Python viewer backend\n * (codemem/viewer_routes/) and the frontend (viewer_ui/src/lib/api.ts).\n *\n * ⚠️ These types are manually transcribed from Python viewer route handlers.\n * There is no automated schema validation between Python and TypeScript.\n * When modifying Python routes, update these types and add integration tests.\n *\n * Import existing store/entity types where shapes match.\n */\n\nimport type { Actor, MemoryItemResponse, PackResponse, Session, StoreStats } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Shared helpers\n// ---------------------------------------------------------------------------\n\n/** Standard pagination envelope returned by list endpoints. */\nexport interface ApiPagination {\n\tlimit: number;\n\toffset: number;\n\tnext_offset: number | null;\n\thas_more: boolean;\n}\n\n/** Common error shape returned by all endpoints on failure. */\nexport interface ApiErrorResponse {\n\terror: string;\n\tdetail?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Core viewer API responses — stats.py\n// ---------------------------------------------------------------------------\n\n/**\n * GET /api/stats\n *\n * Delegates directly to store.stats(); shape matches StoreStats.\n */\nexport type ApiStatsResponse = StoreStats;\n\n/** Single usage event summary row. */\nexport interface ApiUsageEventSummary {\n\tevent: string;\n\ttotal_tokens_read: number;\n\ttotal_tokens_written: number;\n\ttotal_tokens_saved: number;\n\tcount: number;\n}\n\n/** Usage totals row. */\nexport interface ApiUsageTotals {\n\ttokens_read: number;\n\ttokens_written: number;\n\ttokens_saved: number;\n\tcount: number;\n}\n\n/** Recent pack event row. */\nexport interface ApiRecentPackEvent {\n\tid: number;\n\tsession_id: number | null;\n\tevent: string;\n\ttokens_read: number;\n\ttokens_written: number;\n\ttokens_saved: number;\n\tcreated_at: string;\n\tmetadata_json: Record<string, unknown> | null;\n}\n\n/**\n * GET /api/usage\n *\n * Returns usage breakdown, optionally filtered by project.\n */\nexport interface ApiUsageResponse {\n\tproject: string | null;\n\tevents: ApiUsageEventSummary[];\n\ttotals: ApiUsageTotals;\n\tevents_global: ApiUsageEventSummary[];\n\ttotals_global: ApiUsageTotals;\n\tevents_filtered: ApiUsageEventSummary[] | null;\n\ttotals_filtered: ApiUsageTotals | null;\n\trecent_packs: ApiRecentPackEvent[];\n}\n\n// ---------------------------------------------------------------------------\n// Core viewer API responses — memory.py\n// ---------------------------------------------------------------------------\n\n/** Extended memory item with session + ownership fields attached by the viewer. */\nexport interface ApiMemoryItem extends MemoryItemResponse {\n\tproject?: string;\n\tcwd?: string;\n\towned_by_self?: boolean;\n}\n\n/**\n * GET /api/sessions\n *\n * Returns recent sessions with parsed metadata.\n */\nexport interface ApiSessionsResponse {\n\titems: (Session & { metadata_json: Record<string, unknown> | null })[];\n}\n\n/**\n * GET /api/projects\n *\n * Returns deduplicated, sorted project names.\n */\nexport interface ApiProjectsResponse {\n\tprojects: string[];\n}\n\n/**\n * GET /api/observations (also GET /api/memories — aliased)\n * GET /api/summaries\n *\n * Paginated memory items with session/ownership fields attached.\n */\nexport interface ApiMemoryListResponse {\n\titems: ApiMemoryItem[];\n\tpagination: ApiPagination;\n}\n\n/**\n * GET /api/session\n *\n * Aggregate counts for a project (or global).\n */\nexport interface ApiSessionCountsResponse {\n\ttotal: number;\n\tmemories: number;\n\tartifacts: number;\n\tprompts: number;\n\tobservations: number;\n}\n\n/**\n * GET /api/pack\n *\n * Delegates directly to store.build_memory_pack(); shape matches PackResponse.\n */\nexport type ApiPackResponse = PackResponse;\n\n/**\n * GET /api/memory\n *\n * Returns a list of recent memories, optionally filtered by kind/project/scope.\n */\nexport interface ApiMemoryResponse {\n\titems: ApiMemoryItem[];\n}\n\n/**\n * GET /api/artifacts\n *\n * Returns artifacts for a given session.\n */\nexport interface ApiArtifactsResponse {\n\titems: Record<string, unknown>[];\n}\n\n/**\n * POST /api/memories/visibility — request body.\n */\nexport interface ApiUpdateVisibilityRequest {\n\tmemory_id: number;\n\tvisibility: \"private\" | \"shared\";\n}\n\n/**\n * POST /api/memories/visibility — response.\n */\nexport interface ApiUpdateVisibilityResponse {\n\titem: ApiMemoryItem;\n}\n\n// ---------------------------------------------------------------------------\n// Observer status — observer_status.py\n// ---------------------------------------------------------------------------\n\n/** Active observer runtime status (from observer.get_status()). */\nexport interface ApiObserverActiveStatus {\n\tprovider: string | null;\n\tmodel: string | null;\n\truntime: string | null;\n\tauth: string | null;\n\tlast_error?: string | null;\n}\n\n/** Per-provider credential probe result. */\nexport interface ApiProviderCredential {\n\toauth: boolean;\n\tapi_key: boolean;\n\tsource: string | null;\n}\n\n/** Credential availability — provider-keyed map (from probe_available_credentials()). */\nexport type ApiAvailableCredentials = Record<string, ApiProviderCredential>;\n\n/** Latest flush failure with impact annotation. */\nexport interface ApiFlushFailure {\n\tid: number;\n\tsource: string;\n\tstream_id: string;\n\topencode_session_id: string;\n\tstatus: string;\n\terror_message: string | null;\n\terror_type: string | null;\n\timpact: string | null;\n}\n\n/** Queue status within observer-status. */\nexport interface ApiObserverQueue {\n\tpending: number;\n\tsessions: number;\n\tauth_backoff_active: boolean;\n\tauth_backoff_remaining_s: number;\n}\n\n/**\n * GET /api/observer-status\n */\nexport interface ApiObserverStatusResponse {\n\tactive: ApiObserverActiveStatus | null;\n\tavailable_credentials: ApiAvailableCredentials;\n\tlatest_failure: ApiFlushFailure | null;\n\tqueue: ApiObserverQueue;\n}\n\n// ---------------------------------------------------------------------------\n// Config — config.py\n// ---------------------------------------------------------------------------\n\n/**\n * GET /api/config\n */\nexport interface ApiConfigGetResponse {\n\tpath: string;\n\tconfig: Record<string, unknown>;\n\tdefaults: Record<string, unknown>;\n\teffective: Record<string, unknown>;\n\tenv_overrides: Record<string, string>;\n\tproviders: string[];\n}\n\n/** Manual action suggestion in config save effects. */\nexport interface ApiConfigManualAction {\n\tkind: string;\n\tcommand: string;\n\tlabel: string;\n\treason: string;\n}\n\n/** Sync effect detail in config save response. */\nexport interface ApiConfigSyncEffect {\n\taffected_keys: string[];\n\taction: string | null;\n\treason: string | null;\n\tattempted: boolean;\n\tok: boolean | null;\n\tmessage: string | null;\n\tmanual_action: ApiConfigManualAction | null;\n}\n\n/** Effects block in config save response. */\nexport interface ApiConfigEffects {\n\tsaved_keys: string[];\n\teffective_keys: string[];\n\thot_reloaded_keys: string[];\n\tlive_applied_keys: string[];\n\trestart_required_keys: string[];\n\tignored_by_env_keys: string[];\n\twarnings: string[];\n\tsync: ApiConfigSyncEffect;\n\tmanual_actions: ApiConfigManualAction[];\n}\n\n/**\n * POST /api/config — request body.\n * Accepts a direct updates object, or wrapped as { config: {...} }.\n * Python unwraps: `updates = payload.get(\"config\") if \"config\" in payload else payload`\n */\nexport type ApiConfigSaveRequest = Record<string, unknown>;\n\n/**\n * POST /api/config — response.\n */\nexport interface ApiConfigSaveResponse {\n\tpath: string;\n\tconfig: Record<string, unknown>;\n\teffective: Record<string, unknown>;\n\teffects: ApiConfigEffects;\n}\n\n// ---------------------------------------------------------------------------\n// Raw events — raw_events.py\n// ---------------------------------------------------------------------------\n\n/**\n * Raw event session backlog item (from store.raw_event_backlog() + _with_session_aliases).\n * Fields vary by query — max_seq/pending come from the backlog query,\n * last_received_event_seq/updated_at may be absent.\n */\nexport interface ApiRawEventBacklogItem {\n\tstream_id: string;\n\topencode_session_id: string;\n\tsession_stream_id?: string;\n\tsession_id?: string;\n\tcwd?: string | null;\n\tproject?: string | null;\n\tstarted_at?: string | null;\n\tmax_seq?: number;\n\tpending?: number;\n\tlast_seen_ts_wall_ms?: number | null;\n\tlast_received_event_seq?: number;\n\tlast_flushed_event_seq?: number;\n\tupdated_at?: string;\n}\n\n/** Raw event backlog totals. */\nexport interface ApiRawEventBacklogTotals {\n\tpending: number;\n\tsessions: number;\n}\n\n/** Ingest capability metadata. */\nexport interface ApiRawEventIngestInfo {\n\tavailable: boolean;\n\tmode: string;\n\tmax_body_bytes: number;\n}\n\n/**\n * GET /api/raw-events\n *\n * Returns backlog totals directly (compat endpoint for stats panel).\n */\nexport type ApiRawEventsResponse = ApiRawEventBacklogTotals;\n\n/**\n * GET /api/raw-events/status\n */\nexport interface ApiRawEventsStatusResponse {\n\titems: ApiRawEventBacklogItem[];\n\ttotals: ApiRawEventBacklogTotals;\n\tingest: ApiRawEventIngestInfo;\n}\n\n/**\n * POST /api/raw-events — response.\n */\nexport interface ApiRawEventsPostResponse {\n\tinserted: number;\n\treceived: number;\n}\n\n/**\n * POST /api/claude-hooks — response.\n */\nexport interface ApiClaudeHooksPostResponse {\n\tinserted: number;\n\tskipped: number;\n}\n\n// ---------------------------------------------------------------------------\n// Sync — sync.py\n// ---------------------------------------------------------------------------\n\n/** Project scope filter for a peer. */\nexport interface ApiProjectScope {\n\tinclude: string[];\n\texclude: string[];\n\teffective_include: string[];\n\teffective_exclude: string[];\n\tinherits_global: boolean;\n}\n\n/** Peer status breakdown. */\nexport interface ApiPeerStatus {\n\tsync_status: \"ok\" | \"error\" | \"stale\" | \"unknown\";\n\tping_status: \"ok\" | \"stale\" | \"unknown\";\n\tpeer_state: \"online\" | \"offline\" | \"degraded\" | \"stale\" | \"unknown\";\n\tfresh: boolean;\n\tlast_sync_at: string | null;\n\tlast_ping_at: string | null;\n}\n\n/** Peer item in sync status/peers responses. */\nexport interface ApiSyncPeerItem {\n\tpeer_device_id: string;\n\tname: string | null;\n\tfingerprint: string | null;\n\tpinned: boolean;\n\taddresses: string[];\n\tlast_seen_at: string | null;\n\tlast_sync_at: string | null;\n\tlast_error: string | null;\n\thas_error: boolean;\n\tclaimed_local_actor: boolean;\n\tactor_id: string | null;\n\tactor_display_name: string | null;\n\tproject_scope: ApiProjectScope;\n\tstatus?: ApiPeerStatus;\n}\n\n/** Sync attempt item. */\n/** Raw sync attempt row — returned by /api/sync/attempts. */\nexport interface ApiSyncAttemptItem {\n\tpeer_device_id: string;\n\tok: number;\n\terror: string | null;\n\tstarted_at: string;\n\tfinished_at: string | null;\n\tops_in: number;\n\tops_out: number;\n}\n\n/** Enriched sync attempt — embedded in /api/sync/status with extra fields. */\nexport interface ApiSyncAttemptItemEnriched extends ApiSyncAttemptItem {\n\tstatus: string;\n\taddress: string | null;\n}\n\nexport interface ApiSharingReviewItem {\n\tpeer_device_id: string;\n\tpeer_name: string;\n\tactor_id: string;\n\tactor_display_name: string;\n\tproject: string | null;\n\tscope_label: string;\n\tshareable_count: number;\n\tprivate_count: number;\n\ttotal_count: number;\n}\n\nexport interface ApiLegacyDeviceItem {\n\torigin_device_id: string;\n\tmemory_count: number;\n\tlast_seen_at: string | null;\n}\n\n/**\n * Coordinator status snapshot (from coordinator.status_snapshot()).\n * Shape varies by state — fields are optional to cover enabled/disabled modes.\n */\nexport interface ApiCoordinatorStatus {\n\tenabled: boolean;\n\tconfigured: boolean;\n\tcoordinator_url?: string | null;\n\tgroups?: string[];\n\tgroup_id?: string | null;\n\tlast_sync_at?: string | null;\n\tlast_error?: string | null;\n}\n\n/** Join request item. */\nexport interface ApiJoinRequest {\n\trequest_id: string;\n\tdevice_id: string;\n\tfingerprint: string;\n\tstatus: string;\n}\n\n/** Status block nested in sync status response. */\nexport interface ApiSyncStatusBlock {\n\tenabled: boolean;\n\tinterval_s: number;\n\tpeer_count: number;\n\tlast_sync_at: string | null;\n\tdaemon_state: \"ok\" | \"disabled\" | \"error\" | \"stopped\" | \"degraded\" | \"offline-peers\" | \"stale\";\n\tdaemon_running: boolean;\n\tdaemon_detail: string | null;\n\tproject_filter_active: boolean;\n\tproject_filter: { include: string[]; exclude: string[] };\n\tredacted: boolean;\n\tpeers: Record<string, ApiPeerStatus>;\n\tpending: number;\n\tsync: Record<string, unknown>;\n\tping: Record<string, unknown>;\n}\n\n/**\n * GET /api/sync/status\n */\nexport interface ApiSyncStatusResponse {\n\t/* Top-level status fields */\n\tenabled: boolean;\n\tinterval_s: number;\n\tpeer_count: number;\n\tlast_sync_at: string | null;\n\tdaemon_state: \"ok\" | \"disabled\" | \"error\" | \"stopped\" | \"degraded\" | \"offline-peers\" | \"stale\";\n\tdaemon_running: boolean;\n\tdaemon_detail: string | null;\n\tproject_filter_active: boolean;\n\tproject_filter: { include: string[]; exclude: string[] };\n\tredacted: boolean;\n\t/* Diagnostics (present when includeDiagnostics=1) */\n\tdevice_id?: string | null;\n\tfingerprint?: string | null;\n\tbind?: string;\n\tdaemon_last_error?: string | null;\n\tdaemon_last_error_at?: string | null;\n\tdaemon_last_ok_at?: string | null;\n\t/* Nested collections */\n\tstatus: ApiSyncStatusBlock;\n\tpeers: ApiSyncPeerItem[];\n\tattempts: ApiSyncAttemptItemEnriched[];\n\tlegacy_devices: ApiLegacyDeviceItem[];\n\tsharing_review: ApiSharingReviewItem[];\n\tcoordinator: ApiCoordinatorStatus;\n\tjoin_requests: ApiJoinRequest[];\n}\n\n/**\n * GET /api/sync/peers\n */\nexport interface ApiSyncPeersResponse {\n\titems: ApiSyncPeerItem[];\n\tredacted: boolean;\n}\n\n/**\n * GET /api/sync/actors\n */\nexport interface ApiSyncActorsResponse {\n\titems: Actor[];\n}\n\n/**\n * GET /api/sync/attempts\n */\nexport interface ApiSyncAttemptsResponse {\n\titems: ApiSyncAttemptItem[];\n}\n\n/**\n * GET /api/sync/pairing\n */\nexport interface ApiSyncPairingResponse {\n\tdevice_id?: string;\n\tfingerprint?: string;\n\tpublic_key?: string;\n\tpairing_filter_hint: string;\n\taddresses?: string[];\n\tredacted?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Sync mutations — request/response types\n// ---------------------------------------------------------------------------\n\n/** POST /api/sync/actors — request. */\nexport interface ApiCreateActorRequest {\n\tdisplay_name: string;\n\tactor_id?: string | null;\n}\n\n/**\n * POST /api/sync/actors — response.\n *\n * Returns the full Actor row from store.create_actor().\n */\nexport type ApiCreateActorResponse = Actor;\n\n/** POST /api/sync/actors/rename — request. */\nexport interface ApiRenameActorRequest {\n\tactor_id: string;\n\tdisplay_name: string;\n}\n\n/**\n * POST /api/sync/actors/rename — response.\n *\n * Returns the full Actor row from store.rename_actor().\n */\nexport type ApiRenameActorResponse = Actor;\n\n/** POST /api/sync/actors/merge — request. */\nexport interface ApiMergeActorRequest {\n\tprimary_actor_id: string;\n\tsecondary_actor_id: string;\n}\n\n/** POST /api/sync/actors/merge — response. */\nexport interface ApiMergeActorResponse {\n\tmerged_count: number;\n}\n\n/** POST /api/sync/peers/rename — request. */\nexport interface ApiRenamePeerRequest {\n\tpeer_device_id: string;\n\tname: string;\n}\n\n/** POST /api/sync/peers/rename — response. */\nexport interface ApiOkResponse {\n\tok: true;\n}\n\n/** POST /api/sync/peers/scope — request. */\nexport interface ApiPeerScopeRequest {\n\tpeer_device_id: string;\n\tinclude?: string[] | null;\n\texclude?: string[] | null;\n\tinherit_global?: boolean;\n}\n\n/** POST /api/sync/peers/scope — response. */\nexport interface ApiPeerScopeResponse {\n\tok: true;\n\tproject_scope: ApiProjectScope;\n}\n\n/** POST /api/sync/peers/identity — request. */\nexport interface ApiPeerIdentityRequest {\n\tpeer_device_id: string;\n\tactor_id?: string | null;\n\tclaimed_local_actor?: boolean;\n}\n\n/** POST /api/sync/peers/identity — response. */\nexport interface ApiPeerIdentityResponse {\n\tok: true;\n\tactor_id: string | null;\n\tclaimed_local_actor: boolean;\n}\n\n/** POST /api/sync/legacy-devices/claim — request. */\nexport interface ApiClaimLegacyDeviceRequest {\n\torigin_device_id: string;\n}\n\n/** POST /api/sync/legacy-devices/claim — response. */\nexport interface ApiClaimLegacyDeviceResponse {\n\tok: true;\n\torigin_device_id: string;\n\tupdated: number;\n}\n\n/** POST /api/sync/invites/create — request. */\nexport interface ApiCreateInviteRequest {\n\tgroup_id: string;\n\tcoordinator_url?: string | null;\n\tpolicy?: \"auto_admit\" | \"approval_required\";\n\tttl_hours?: number;\n}\n\n/** POST /api/sync/invites/create — response. */\nexport interface ApiCreateInviteResponse extends ApiOkResponse {\n\tinvite: string;\n}\n\n/** POST /api/sync/invites/import — request. */\nexport interface ApiImportInviteRequest {\n\tinvite: string;\n}\n\n/** POST /api/sync/invites/import — response. */\nexport interface ApiImportInviteResponse extends ApiOkResponse {\n\tgroup_id: string;\n}\n\n/** POST /api/sync/join-requests/review — request. */\nexport interface ApiReviewJoinRequestRequest {\n\trequest_id: string;\n\taction: \"approve\" | \"deny\";\n}\n\n/** POST /api/sync/join-requests/review — response. */\nexport interface ApiReviewJoinRequestResponse {\n\tok: true;\n\trequest: ApiJoinRequest;\n}\n\n/** DELETE /api/sync/peers/:peer_device_id — response. */\nexport type ApiDeletePeerResponse = ApiOkResponse;\n\n/** POST /api/sync/actions/sync-now — request. */\nexport interface ApiSyncNowRequest {\n\tpeer_device_id?: string;\n\taddress?: string;\n}\n\n/** POST /api/sync/actions/sync-now — response. */\nexport interface ApiSyncNowResponse {\n\titems: Record<string, unknown>[];\n}\n","/**\n * Claude hook payload mapping.\n *\n * Ports codemem/claude_hooks.py — normalizes raw Claude Code hook payloads\n * (PreToolUse, PostToolUse, Stop, etc.) into raw event envelopes suitable\n * for the raw event sweeper pipeline.\n *\n * Entry points:\n * mapClaudeHookPayload(payload) → adapter event or null\n * buildRawEventEnvelopeFromHook(payload) → raw event envelope or null\n * buildIngestPayloadFromHook(payload) → ingest payload or null\n */\n\nimport { createHash } from \"node:crypto\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, isAbsolute, resolve } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// Path helpers\n// ---------------------------------------------------------------------------\n\n/** Expand `~/...` paths like Python's `Path(...).expanduser()`. */\nfunction expandUser(value: string): string {\n\treturn value.startsWith(\"~/\") ? resolve(homedir(), value.slice(2)) : value;\n}\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const MAPPABLE_CLAUDE_HOOK_EVENTS = new Set([\n\t\"SessionStart\",\n\t\"UserPromptSubmit\",\n\t\"PreToolUse\",\n\t\"PostToolUse\",\n\t\"PostToolUseFailure\",\n\t\"Stop\",\n\t\"SessionEnd\",\n]);\n\n// ---------------------------------------------------------------------------\n// Timestamp helpers\n// ---------------------------------------------------------------------------\n\nfunction nowIso(): string {\n\treturn new Date()\n\t\t.toISOString()\n\t\t.replace(\"+00:00\", \"\")\n\t\t.replace(/\\.(\\d{3})\\d*Z$/, \".$1Z\");\n}\n\n/**\n * Normalize an ISO timestamp string, returning null if invalid.\n *\n * Matches Python's `datetime.isoformat().replace(\"+00:00\", \"Z\")`:\n * - No fractional seconds if the input has none → \"2026-03-04T01:00:00Z\"\n * - Preserves fractional seconds when present → \"2026-03-04T01:00:00.123000Z\"\n *\n * JS `Date.toISOString()` always outputs \".000Z\" which would produce different\n * sha256 event IDs than Python during the migration crossover period.\n */\nfunction normalizeIsoTs(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst text = value.trim();\n\tif (!text) return null;\n\ttry {\n\t\t// Python: datetime.fromisoformat(text.replace(\"Z\", \"+00:00\"))\n\t\t// then: if parsed.tzinfo is None: parsed = parsed.replace(tzinfo=UTC)\n\t\t//\n\t\t// JS `new Date(\"2026-03-04T01:00:00\")` treats naive timestamps as LOCAL time,\n\t\t// but Python treats them as UTC. Detect naive timestamps and append Z.\n\t\tconst hasTimezone =\n\t\t\t/[Zz]$/.test(text) || /[+-]\\d{2}:\\d{2}$/.test(text) || /[+-]\\d{4}$/.test(text);\n\t\tconst parseText = hasTimezone ? text : `${text}Z`;\n\n\t\tconst d = new Date(parseText);\n\t\tif (Number.isNaN(d.getTime())) return null;\n\n\t\t// Detect whether the input has fractional seconds\n\t\t// Strip timezone suffix first, then check for a dot before \"Z\"/\"+\"/\"-\"\n\t\tconst hasFractional = /\\.\\d+([Zz+-]|$)/.test(text);\n\t\tif (!hasFractional) {\n\t\t\t// No fractional seconds — produce \"YYYY-MM-DDTHH:MM:SSZ\" (matches Python)\n\t\t\treturn d.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n\t\t}\n\t\t// Has fractional seconds — produce microsecond precision like Python (6 digits)\n\t\tconst iso = d.toISOString(); // \"YYYY-MM-DDTHH:MM:SS.mmmZ\"\n\t\t// Pad from 3 digits to 6 to match Python's microsecond output\n\t\treturn iso.replace(/\\.(\\d{3})Z$/, \".$1000Z\");\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Parse an ISO timestamp to wall-clock milliseconds. */\nfunction isoToWallMs(value: string): number {\n\treturn new Date(value).getTime();\n}\n\n// ---------------------------------------------------------------------------\n// Stable event id\n// ---------------------------------------------------------------------------\n\nfunction stableEventId(...parts: string[]): string {\n\tconst joined = parts.join(\"|\");\n\tconst digest = createHash(\"sha256\").update(joined, \"utf-8\").digest(\"hex\").slice(0, 24);\n\treturn `cld_evt_${digest}`;\n}\n\n// ---------------------------------------------------------------------------\n// Project inference (mirrors Python's _infer_project_from_cwd /\n// _resolve_hook_project / _resolve_hook_project_from_payload_paths)\n// ---------------------------------------------------------------------------\n\n/** Normalize a raw label value to a plain project name (basename if path). */\nexport function normalizeProjectLabel(value: unknown): string | null {\n\tif (typeof value !== \"string\") return null;\n\tconst cleaned = value.trim().replace(/[\\\\/]+$/, \"\");\n\tif (!cleaned) return null;\n\tif (cleaned.includes(\"/\") || cleaned.includes(\"\\\\\")) {\n\t\t// Windows-style path (drive letter or backslash)\n\t\tconst isWindows =\n\t\t\tcleaned.includes(\"\\\\\") ||\n\t\t\t(cleaned.length >= 2 && cleaned[1] === \":\" && /[a-zA-Z]/.test(cleaned[0] ?? \"\"));\n\t\tif (isWindows) {\n\t\t\tconst parts = cleaned.replaceAll(\"\\\\\", \"/\").split(\"/\");\n\t\t\treturn parts[parts.length - 1] || null;\n\t\t}\n\t\tconst parts = cleaned.split(\"/\");\n\t\treturn parts[parts.length - 1] || null;\n\t}\n\treturn cleaned;\n}\n\n/**\n * Walk up from `cwd` looking for a .git marker, then return the basename of\n * that directory (or the cwd basename if no git root found).\n * Returns null if cwd is not an absolute, existing directory.\n */\nfunction inferProjectFromCwd(cwd: string | null): string | null {\n\tif (typeof cwd !== \"string\" || !cwd.trim()) return null;\n\tconst text = expandUser(cwd.trim());\n\tif (!isAbsolute(text)) return null;\n\ttry {\n\t\tconst stat = statSync(text, { throwIfNoEntry: false });\n\t\tif (!stat?.isDirectory()) return null;\n\t} catch {\n\t\treturn null;\n\t}\n\n\tlet current = text;\n\twhile (true) {\n\t\tconst gitPath = resolve(current, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\treturn basename(current) || null;\n\t\t}\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) break;\n\t\tcurrent = parent;\n\t}\n\treturn basename(text) || null;\n}\n\n/**\n * Infer project from a file path hint (e.g. a tool input `filePath`).\n * Walks up from the file's directory.\n */\nfunction inferProjectFromPathHint(pathHint: unknown, cwdHint?: string | null): string | null {\n\tif (typeof pathHint !== \"string\" || !pathHint.trim()) return null;\n\tconst text = expandUser(pathHint.trim());\n\n\tlet candidate: string;\n\tif (isAbsolute(text)) {\n\t\tcandidate = text;\n\t} else {\n\t\tif (typeof cwdHint !== \"string\" || !cwdHint.trim()) return null;\n\t\tconst base = expandUser(cwdHint.trim());\n\t\tif (!isAbsolute(base)) return null;\n\t\ttry {\n\t\t\tconst stat = statSync(base, { throwIfNoEntry: false });\n\t\t\tif (!stat?.isDirectory()) return null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t\tcandidate = resolve(base, text);\n\t}\n\n\t// Determine starting dir: if candidate is a dir use it, else use parent\n\tlet start: string;\n\ttry {\n\t\tconst stat = statSync(candidate, { throwIfNoEntry: false });\n\t\tstart = stat?.isDirectory() ? candidate : dirname(candidate);\n\t} catch {\n\t\tstart = dirname(candidate);\n\t}\n\n\t// Walk up to find an existing directory\n\tlet current = start;\n\twhile (!existsSync(current)) {\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return null;\n\t\tcurrent = parent;\n\t}\n\n\treturn inferProjectFromCwd(current);\n}\n\n/**\n * Resolve the project for a hook payload.\n * Priority: CODEMEM_PROJECT env → cwd git root → payload project label.\n */\nexport function resolveHookProject(cwd: string | null, payloadProject: unknown): string | null {\n\tconst envProject = normalizeProjectLabel(process.env.CODEMEM_PROJECT);\n\tif (envProject) return envProject;\n\n\tconst payloadLabel = normalizeProjectLabel(payloadProject);\n\tconst cwdLabel = inferProjectFromCwd(cwd);\n\n\tif (cwdLabel) {\n\t\t// If payload label matches cwd label exactly, prefer payload (avoids ambiguity)\n\t\tif (payloadLabel && payloadLabel === cwdLabel) return payloadLabel;\n\t\treturn cwdLabel;\n\t}\n\treturn payloadLabel ?? null;\n}\n\n/**\n * Try to infer project from tool_input paths or transcript_path in a hook payload.\n */\nfunction resolveHookProjectFromPayloadPaths(hookPayload: Record<string, unknown>): string | null {\n\tconst cwdHint = typeof hookPayload.cwd === \"string\" ? hookPayload.cwd : null;\n\tconst toolInput = hookPayload.tool_input;\n\tif (toolInput != null && typeof toolInput === \"object\" && !Array.isArray(toolInput)) {\n\t\tconst ti = toolInput as Record<string, unknown>;\n\t\tfor (const key of [\"filePath\", \"file_path\", \"path\"]) {\n\t\t\tconst project = inferProjectFromPathHint(ti[key], cwdHint);\n\t\t\tif (project) return project;\n\t\t}\n\t}\n\tconst project = inferProjectFromPathHint(hookPayload.transcript_path, cwdHint);\n\tif (project) return project;\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Usage normalization\n// ---------------------------------------------------------------------------\n\nfunction normalizeUsage(value: unknown): Record<string, number> | null {\n\tif (value == null || typeof value !== \"object\" || Array.isArray(value)) return null;\n\tconst v = value as Record<string, unknown>;\n\n\t// Matches Python: int(value.get(key) or 0) — no clamping to >= 0\n\tconst toInt = (key: string): number => {\n\t\ttry {\n\t\t\tconst n = Number(v[key] ?? 0);\n\t\t\treturn Number.isFinite(n) ? Math.trunc(n) : 0;\n\t\t} catch {\n\t\t\treturn 0;\n\t\t}\n\t};\n\n\tconst normalized = {\n\t\tinput_tokens: toInt(\"input_tokens\"),\n\t\toutput_tokens: toInt(\"output_tokens\"),\n\t\tcache_creation_input_tokens: toInt(\"cache_creation_input_tokens\"),\n\t\tcache_read_input_tokens: toInt(\"cache_read_input_tokens\"),\n\t};\n\tconst total = Object.values(normalized).reduce((a, b) => a + b, 0);\n\treturn total > 0 ? normalized : null;\n}\n\n// ---------------------------------------------------------------------------\n// Text extraction from content blocks\n// ---------------------------------------------------------------------------\n\nfunction textFromContent(value: unknown): string {\n\tif (typeof value === \"string\") return value.trim();\n\tif (Array.isArray(value)) {\n\t\tconst parts = value.map(textFromContent).filter(Boolean);\n\t\treturn parts.join(\"\\n\").trim();\n\t}\n\tif (value != null && typeof value === \"object\") {\n\t\tconst v = value as Record<string, unknown>;\n\t\tif (typeof v.text === \"string\") return v.text.trim();\n\t\treturn textFromContent(v.content);\n\t}\n\treturn \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Transcript extraction (for Stop events without last_assistant_message)\n// ---------------------------------------------------------------------------\n\n/**\n * Read the transcript JSONL and return the last assistant message text + usage.\n * Returns [null, null] on any read or parse failure.\n */\nfunction extractFromTranscript(\n\ttranscriptPath: unknown,\n\tcwdHint?: string | null,\n): [string | null, Record<string, number> | null] {\n\tif (typeof transcriptPath !== \"string\") return [null, null];\n\tconst raw = expandUser(transcriptPath.trim());\n\tif (!raw) return [null, null];\n\n\tlet resolvedPath: string;\n\tif (isAbsolute(raw)) {\n\t\tresolvedPath = raw;\n\t} else {\n\t\tif (typeof cwdHint !== \"string\" || !cwdHint.trim()) return [null, null];\n\t\tconst base = expandUser(cwdHint.trim());\n\t\tif (!isAbsolute(base)) return [null, null];\n\t\ttry {\n\t\t\tconst stat = statSync(base, { throwIfNoEntry: false });\n\t\t\tif (!stat?.isDirectory()) return [null, null];\n\t\t} catch {\n\t\t\treturn [null, null];\n\t\t}\n\t\tresolvedPath = resolve(base, raw);\n\t}\n\n\ttry {\n\t\tconst stat = statSync(resolvedPath, { throwIfNoEntry: false });\n\t\tif (!stat?.isFile()) return [null, null];\n\t} catch {\n\t\treturn [null, null];\n\t}\n\n\tlet assistantText: string | null = null;\n\tlet assistantUsage: Record<string, number> | null = null;\n\n\ttry {\n\t\tconst content = readFileSync(resolvedPath, \"utf-8\");\n\t\tfor (const rawLine of content.split(\"\\n\")) {\n\t\t\tconst line = rawLine.trim();\n\t\t\tif (!line) continue;\n\t\t\tlet record: unknown;\n\t\t\ttry {\n\t\t\t\trecord = JSON.parse(line);\n\t\t\t} catch {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (record == null || typeof record !== \"object\" || Array.isArray(record)) continue;\n\t\t\tconst r = record as Record<string, unknown>;\n\n\t\t\t// A line may be the record itself or have a nested `message` field\n\t\t\tconst candidates: Record<string, unknown>[] = [r];\n\t\t\tif (r.message != null && typeof r.message === \"object\" && !Array.isArray(r.message)) {\n\t\t\t\tcandidates.push(r.message as Record<string, unknown>);\n\t\t\t}\n\n\t\t\tlet role = \"\";\n\t\t\tlet contentValue: unknown = null;\n\t\t\tlet usageValue: unknown = null;\n\n\t\t\tfor (const c of candidates) {\n\t\t\t\tif (!role) {\n\t\t\t\t\tif (typeof c.role === \"string\") role = c.role.trim().toLowerCase();\n\t\t\t\t\telse if (c.type === \"assistant\") role = \"assistant\";\n\t\t\t\t}\n\t\t\t\tif (contentValue == null) {\n\t\t\t\t\tfor (const field of [\"content\", \"text\"]) {\n\t\t\t\t\t\tif (field in c) {\n\t\t\t\t\t\t\tcontentValue = c[field];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (usageValue == null) {\n\t\t\t\t\tfor (const field of [\"usage\", \"token_usage\", \"tokenUsage\"]) {\n\t\t\t\t\t\tif (field in c) {\n\t\t\t\t\t\t\tusageValue = c[field];\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (role !== \"assistant\") continue;\n\t\t\tconst text = textFromContent(contentValue);\n\t\t\tif (!text) continue;\n\t\t\tassistantText = text;\n\t\t\tassistantUsage = normalizeUsage(usageValue);\n\t\t}\n\t} catch {\n\t\treturn [null, null];\n\t}\n\n\treturn [assistantText, assistantUsage];\n}\n\n// ---------------------------------------------------------------------------\n// Session id\n// ---------------------------------------------------------------------------\n\nfunction coerceSessionId(payload: Record<string, unknown>): string | null {\n\tconst raw = payload.session_id;\n\tif (typeof raw !== \"string\") return null;\n\tconst value = raw.trim();\n\treturn value || null;\n}\n\n// ---------------------------------------------------------------------------\n// mapClaudeHookPayload\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeHookAdapterEvent {\n\tschema_version: \"1.0\";\n\tsource: \"claude\";\n\tsession_id: string;\n\tevent_id: string;\n\tevent_type: string;\n\tts: string;\n\tordering_confidence: \"low\";\n\tcwd: string | null;\n\tpayload: Record<string, unknown>;\n\tmeta: Record<string, unknown>;\n}\n\n/**\n * Map a raw Claude Code hook payload to a normalized adapter event.\n * Returns null if the event type is unsupported or required fields are missing.\n */\nexport function mapClaudeHookPayload(\n\tpayload: Record<string, unknown>,\n): ClaudeHookAdapterEvent | null {\n\tconst hookEvent = String(payload.hook_event_name ?? \"\").trim();\n\tif (!MAPPABLE_CLAUDE_HOOK_EVENTS.has(hookEvent)) return null;\n\n\tconst sessionId = coerceSessionId(payload);\n\tif (!sessionId) return null;\n\n\tconst rawTs = payload.ts ?? payload.timestamp;\n\tconst normalizedRawTs = normalizeIsoTs(rawTs);\n\tconst ts = normalizedRawTs ?? nowIso();\n\tconst toolUseId = String(payload.tool_use_id ?? \"\").trim();\n\n\tconst consumed = new Set([\n\t\t\"hook_event_name\",\n\t\t\"session_id\",\n\t\t\"cwd\",\n\t\t\"ts\",\n\t\t\"timestamp\",\n\t\t\"transcript_path\",\n\t\t\"permission_mode\",\n\t\t\"tool_use_id\",\n\t]);\n\n\tlet eventType: string;\n\tlet eventPayload: Record<string, unknown>;\n\tlet eventIdPayload: Record<string, unknown>;\n\n\tif (hookEvent === \"SessionStart\") {\n\t\teventType = \"session_start\";\n\t\teventPayload = { source: payload.source };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"source\");\n\t} else if (hookEvent === \"UserPromptSubmit\") {\n\t\tconst text = String(payload.prompt ?? \"\").trim();\n\t\tif (!text) return null;\n\t\teventType = \"prompt\";\n\t\teventPayload = { text };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"prompt\");\n\t} else if (hookEvent === \"PreToolUse\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\teventType = \"tool_call\";\n\t\teventPayload = { tool_name: toolName, tool_input: toolInput };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t} else if (hookEvent === \"PostToolUse\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tconst toolResponse = payload.tool_response ?? null;\n\t\teventType = \"tool_result\";\n\t\teventPayload = {\n\t\t\ttool_name: toolName,\n\t\t\tstatus: \"ok\",\n\t\t\ttool_input: toolInput,\n\t\t\ttool_output: toolResponse,\n\t\t\ttool_error: null,\n\t\t};\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t\tconsumed.add(\"tool_response\");\n\t} else if (hookEvent === \"PostToolUseFailure\") {\n\t\tconst toolName = String(payload.tool_name ?? \"\").trim();\n\t\tif (!toolName) return null;\n\t\tconst toolInput =\n\t\t\tpayload.tool_input != null &&\n\t\t\ttypeof payload.tool_input === \"object\" &&\n\t\t\t!Array.isArray(payload.tool_input)\n\t\t\t\t? (payload.tool_input as Record<string, unknown>)\n\t\t\t\t: {};\n\t\tconst error = payload.error ?? null;\n\t\teventType = \"tool_result\";\n\t\teventPayload = {\n\t\t\ttool_name: toolName,\n\t\t\tstatus: \"error\",\n\t\t\ttool_input: toolInput,\n\t\t\ttool_output: null,\n\t\t\terror,\n\t\t};\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"tool_name\");\n\t\tconsumed.add(\"tool_input\");\n\t\tconsumed.add(\"error\");\n\t\tconsumed.add(\"is_interrupt\");\n\t} else if (hookEvent === \"Stop\") {\n\t\tconst rawAssistantText = String(payload.last_assistant_message ?? \"\").trim();\n\t\tconst rawUsage = normalizeUsage(payload.usage);\n\n\t\tlet assistantText = rawAssistantText;\n\t\tlet usage = rawUsage;\n\n\t\tif (!assistantText || usage === null) {\n\t\t\tconst cwd = typeof payload.cwd === \"string\" ? payload.cwd : null;\n\t\t\tconst [transcriptText, transcriptUsage] = extractFromTranscript(payload.transcript_path, cwd);\n\t\t\tif (!assistantText && transcriptText) assistantText = transcriptText;\n\t\t\tif (usage === null && transcriptUsage !== null) usage = transcriptUsage;\n\t\t}\n\n\t\tif (!assistantText) return null;\n\n\t\teventType = \"assistant\";\n\t\teventPayload = { text: assistantText };\n\t\tif (usage !== null) eventPayload.usage = usage;\n\n\t\teventIdPayload = { text: rawAssistantText };\n\t\tif (rawUsage !== null) eventIdPayload.usage = rawUsage;\n\t\tif (!rawAssistantText && rawUsage === null) {\n\t\t\tconst transcriptPath = payload.transcript_path;\n\t\t\tif (typeof transcriptPath === \"string\" && transcriptPath.trim()) {\n\t\t\t\teventIdPayload.transcript_path = transcriptPath.trim();\n\t\t\t}\n\t\t}\n\t\tconsumed.add(\"stop_hook_active\");\n\t\tconsumed.add(\"last_assistant_message\");\n\t\tconsumed.add(\"usage\");\n\t} else {\n\t\t// SessionEnd\n\t\teventType = \"session_end\";\n\t\teventPayload = { reason: payload.reason ?? null };\n\t\teventIdPayload = { ...eventPayload };\n\t\tconsumed.add(\"reason\");\n\t}\n\n\t// Build meta — forward unknown fields as hook_fields\n\tconst meta: Record<string, unknown> = {\n\t\thook_event_name: hookEvent,\n\t\tordering_confidence: \"low\",\n\t};\n\tif (toolUseId) meta.tool_use_id = toolUseId;\n\tif (normalizedRawTs === null) meta.ts_normalized = \"generated\";\n\n\tconst unknown: Record<string, unknown> = {};\n\tfor (const [k, v] of Object.entries(payload)) {\n\t\tif (!consumed.has(k)) unknown[k] = v;\n\t}\n\tif (Object.keys(unknown).length > 0) meta.hook_fields = unknown;\n\n\t// Compute stable event id\n\tconst eventIdTsSeed = normalizedRawTs ?? ts;\n\t// Matches Python's json.dumps(sort_keys=True, default=str):\n\t// - sortKeys() recursively sorts object keys\n\t// - the replacer coerces non-JSON-native values to strings (like Python's default=str)\n\tconst payloadHash = createHash(\"sha256\")\n\t\t.update(\n\t\t\tJSON.stringify(sortKeys(eventIdPayload), (_key, value) => {\n\t\t\t\tif (value === undefined) return \"None\"; // Python stringifies None\n\t\t\t\tif (typeof value === \"bigint\") return String(value);\n\t\t\t\treturn value;\n\t\t\t}),\n\t\t\t\"utf-8\",\n\t\t)\n\t\t.digest(\"hex\");\n\n\tconst eventId = stableEventId(sessionId, hookEvent, eventIdTsSeed, toolUseId, payloadHash);\n\n\tconst cwd = typeof payload.cwd === \"string\" ? payload.cwd : null;\n\n\treturn {\n\t\tschema_version: \"1.0\",\n\t\tsource: \"claude\",\n\t\tsession_id: sessionId,\n\t\tevent_id: eventId,\n\t\tevent_type: eventType,\n\t\tts,\n\t\tordering_confidence: \"low\",\n\t\tcwd,\n\t\tpayload: eventPayload,\n\t\tmeta,\n\t};\n}\n\n/** Recursively sort object keys (matches Python's json.dumps(sort_keys=True)). */\nfunction sortKeys(value: unknown): unknown {\n\tif (value == null || typeof value !== \"object\" || Array.isArray(value)) return value;\n\tconst sorted: Record<string, unknown> = {};\n\tfor (const k of Object.keys(value as Record<string, unknown>).sort()) {\n\t\tsorted[k] = sortKeys((value as Record<string, unknown>)[k]);\n\t}\n\treturn sorted;\n}\n\n// ---------------------------------------------------------------------------\n// buildRawEventEnvelopeFromHook\n// ---------------------------------------------------------------------------\n\nexport interface ClaudeHookRawEventEnvelope {\n\tsession_stream_id: string;\n\tsession_id: string;\n\topencode_session_id: string;\n\tsource: string;\n\tevent_id: string;\n\tevent_type: \"claude.hook\";\n\tpayload: Record<string, unknown>;\n\tts_wall_ms: number;\n\tcwd: string | null;\n\tproject: string | null;\n\tstarted_at: string | null;\n}\n\n/**\n * Build a raw event envelope from a Claude Code hook payload.\n * Returns null if the payload is unsupported or missing required fields.\n */\nexport function buildRawEventEnvelopeFromHook(\n\thookPayload: Record<string, unknown>,\n): ClaudeHookRawEventEnvelope | null {\n\tconst adapterEvent = mapClaudeHookPayload(hookPayload);\n\tif (adapterEvent === null) return null;\n\n\tconst sessionId = adapterEvent.session_id.trim();\n\tif (!sessionId) return null;\n\n\tconst ts = adapterEvent.ts.trim();\n\tif (!ts) return null;\n\n\tconst source = adapterEvent.source || \"claude\";\n\tconst hookEventName = String(hookPayload.hook_event_name ?? \"\");\n\tconst cwd = typeof hookPayload.cwd === \"string\" ? hookPayload.cwd : null;\n\n\tlet project = resolveHookProject(cwd, hookPayload.project);\n\tif (project === null) {\n\t\tproject = resolveHookProjectFromPayloadPaths(hookPayload);\n\t}\n\n\treturn {\n\t\tsession_stream_id: sessionId,\n\t\tsession_id: sessionId,\n\t\topencode_session_id: sessionId,\n\t\tsource,\n\t\tevent_id: adapterEvent.event_id,\n\t\tevent_type: \"claude.hook\",\n\t\tpayload: {\n\t\t\ttype: \"claude.hook\",\n\t\t\ttimestamp: ts,\n\t\t\t_adapter: adapterEvent,\n\t\t},\n\t\tts_wall_ms: isoToWallMs(ts),\n\t\tcwd,\n\t\tproject,\n\t\tstarted_at: hookEventName === \"SessionStart\" ? ts : null,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// buildIngestPayloadFromHook\n// ---------------------------------------------------------------------------\n\n/**\n * Build an ingest pipeline payload from a Claude Code hook payload.\n * Used by the direct-ingest path (non-raw-event path).\n * Returns null if the payload is unsupported.\n */\nexport function buildIngestPayloadFromHook(\n\thookPayload: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst adapterEvent = mapClaudeHookPayload(hookPayload);\n\tif (adapterEvent === null) return null;\n\n\tconst sessionId = adapterEvent.session_id;\n\treturn {\n\t\tcwd: hookPayload.cwd ?? null,\n\t\tevents: [\n\t\t\t{\n\t\t\t\ttype: \"claude.hook\",\n\t\t\t\ttimestamp: adapterEvent.ts,\n\t\t\t\t_adapter: adapterEvent,\n\t\t\t},\n\t\t],\n\t\tsession_context: {\n\t\t\tsource: \"claude\",\n\t\t\tstream_id: sessionId,\n\t\t\tsession_stream_id: sessionId,\n\t\t\tsession_id: sessionId,\n\t\t\topencode_session_id: sessionId,\n\t\t},\n\t};\n}\n","/**\n * Invite link encoding/decoding for the coordinator join flow.\n *\n * Ported from codemem/coordinator_invites.py.\n */\n\nexport interface InvitePayload {\n\tv: number;\n\tkind: string;\n\tcoordinator_url: string;\n\tgroup_id: string;\n\tpolicy: string;\n\ttoken: string;\n\texpires_at: string;\n\tteam_name: string | null;\n}\n\n/** Encode an invite payload as a compact base64url string (no padding). */\nexport function encodeInvitePayload(payload: InvitePayload): string {\n\tconst json = JSON.stringify(payload);\n\tconst bytes = new TextEncoder().encode(json);\n\tconst base64 = Buffer.from(bytes).toString(\"base64url\");\n\t// base64url from Buffer already omits padding — strip defensively\n\treturn base64.replace(/=+$/, \"\");\n}\n\n/** Decode a base64url invite payload string back to an InvitePayload. */\nexport function decodeInvitePayload(value: string): InvitePayload {\n\t// Re-add padding that was stripped\n\tconst padded = value + \"=\".repeat((4 - (value.length % 4)) % 4);\n\tconst json = Buffer.from(padded, \"base64url\").toString(\"utf-8\");\n\tconst data: unknown = JSON.parse(json);\n\tif (typeof data !== \"object\" || data === null) {\n\t\tthrow new Error(\"invalid invite payload\");\n\t}\n\treturn data as InvitePayload;\n}\n\n/** Build a `codemem://join?invite=...` link from an encoded payload. */\nexport function inviteLink(encodedPayload: string): string {\n\treturn `codemem://join?invite=${encodeURIComponent(encodedPayload)}`;\n}\n\n/**\n * Extract the raw encoded payload from either a `codemem://` link or a raw value.\n *\n * Throws if a `codemem://` link is provided but has no `invite` query parameter.\n */\nexport function extractInvitePayload(value: string): string {\n\tconst raw = value.trim();\n\tif (raw.startsWith(\"codemem://\")) {\n\t\tconst url = new URL(raw);\n\t\tconst invite = url.searchParams.get(\"invite\") ?? \"\";\n\t\tif (!invite) {\n\t\t\tthrow new Error(\"invite payload missing from link\");\n\t\t}\n\t\treturn invite;\n\t}\n\treturn raw;\n}\n","/**\n * CoordinatorStore — SQLite-backed state for the coordinator relay server.\n *\n * The coordinator is a cloud relay that manages group membership, device\n * enrollment, presence, invites, and join requests for sync between devices.\n *\n * This store uses its OWN database (separate from the main codemem DB) and\n * owns its own schema — the TS side creates tables directly.\n *\n * Ported from codemem/coordinator_store.py.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport { mkdirSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport type { Database as DatabaseType } from \"better-sqlite3\";\nimport Database from \"better-sqlite3\";\n\nexport const DEFAULT_COORDINATOR_DB_PATH = join(homedir(), \".codemem\", \"coordinator.sqlite\");\n\n// ---------------------------------------------------------------------------\n// Address helpers (subset of sync/discovery.py, inlined to avoid circular deps)\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize an address using the URL constructor.\n * Duplicated from sync-discovery.ts to avoid circular deps\n * (coordinator-store loads before sync-discovery in the stack).\n * TODO: extract to shared address-utils.ts module.\n */\nfunction normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\nfunction addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst parsed = new URL(address);\n\t\tconst host = parsed.hostname.toLowerCase();\n\t\tif (\n\t\t\t(parsed.protocol === \"http:\" || parsed.protocol === \"\") &&\n\t\t\thost &&\n\t\t\tparsed.port &&\n\t\t\tparsed.pathname === \"/\"\n\t\t) {\n\t\t\treturn `${host}:${parsed.port}`;\n\t\t}\n\t} catch {\n\t\t// Not parseable\n\t}\n\treturn address;\n}\n\nfunction mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction nowISO(): string {\n\treturn new Date().toISOString();\n}\n\nfunction tokenUrlSafe(bytes: number): string {\n\treturn randomBytes(bytes).toString(\"base64url\").replace(/=+$/, \"\");\n}\n\nfunction rowToRecord(row: unknown): Record<string, unknown> {\n\tif (row == null) return {};\n\t// better-sqlite3 returns plain objects when using .get()/.all()\n\treturn row as Record<string, unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Schema\n// ---------------------------------------------------------------------------\n\nfunction initializeSchema(db: DatabaseType): void {\n\tdb.exec(`\n\t\tCREATE TABLE IF NOT EXISTS groups (\n\t\t\tgroup_id TEXT PRIMARY KEY,\n\t\t\tdisplay_name TEXT,\n\t\t\tcreated_at TEXT NOT NULL\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS enrolled_devices (\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tpublic_key TEXT NOT NULL,\n\t\t\tfingerprint TEXT NOT NULL,\n\t\t\tdisplay_name TEXT,\n\t\t\tenabled INTEGER NOT NULL DEFAULT 1,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (group_id, device_id)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS presence_records (\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\taddresses_json TEXT NOT NULL,\n\t\t\tlast_seen_at TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tcapabilities_json TEXT NOT NULL DEFAULT '{}',\n\t\t\tPRIMARY KEY (group_id, device_id)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS request_nonces (\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tnonce TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tPRIMARY KEY (device_id, nonce)\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS coordinator_invites (\n\t\t\tinvite_id TEXT PRIMARY KEY,\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\ttoken TEXT NOT NULL UNIQUE,\n\t\t\tpolicy TEXT NOT NULL,\n\t\t\texpires_at TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\tcreated_by TEXT,\n\t\t\tteam_name_snapshot TEXT,\n\t\t\trevoked_at TEXT\n\t\t);\n\n\t\tCREATE TABLE IF NOT EXISTS coordinator_join_requests (\n\t\t\trequest_id TEXT PRIMARY KEY,\n\t\t\tgroup_id TEXT NOT NULL,\n\t\t\tdevice_id TEXT NOT NULL,\n\t\t\tpublic_key TEXT NOT NULL,\n\t\t\tfingerprint TEXT NOT NULL,\n\t\t\tdisplay_name TEXT,\n\t\t\ttoken TEXT NOT NULL,\n\t\t\tstatus TEXT NOT NULL,\n\t\t\tcreated_at TEXT NOT NULL,\n\t\t\treviewed_at TEXT,\n\t\t\treviewed_by TEXT\n\t\t);\n\t`);\n}\n\n// ---------------------------------------------------------------------------\n// Connection\n// ---------------------------------------------------------------------------\n\nexport function connectCoordinator(path?: string): DatabaseType {\n\tconst dbPath = path ?? DEFAULT_COORDINATOR_DB_PATH;\n\tmkdirSync(dirname(dbPath), { recursive: true });\n\tconst db = new Database(dbPath);\n\tdb.pragma(\"foreign_keys = ON\");\n\tdb.pragma(\"busy_timeout = 5000\");\n\tdb.pragma(\"journal_mode = WAL\");\n\tdb.pragma(\"synchronous = NORMAL\");\n\tinitializeSchema(db);\n\treturn db;\n}\n\n// ---------------------------------------------------------------------------\n// CoordinatorStore\n// ---------------------------------------------------------------------------\n\nexport class CoordinatorStore {\n\treadonly path: string;\n\treadonly db: DatabaseType;\n\n\tconstructor(path?: string) {\n\t\tthis.path = path ?? DEFAULT_COORDINATOR_DB_PATH;\n\t\tthis.db = connectCoordinator(this.path);\n\t}\n\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n\n\t// -- Groups -------------------------------------------------------------\n\n\tcreateGroup(groupId: string, displayName?: string | null): void {\n\t\tthis.db\n\t\t\t.prepare(\"INSERT OR IGNORE INTO groups(group_id, display_name, created_at) VALUES (?, ?, ?)\")\n\t\t\t.run(groupId, displayName ?? null, nowISO());\n\t}\n\n\tgetGroup(groupId: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups WHERE group_id = ?\")\n\t\t\t.get(groupId);\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\tlistGroups(): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\"SELECT group_id, display_name, created_at FROM groups ORDER BY created_at ASC\")\n\t\t\t.all()\n\t\t\t.map(rowToRecord);\n\t}\n\n\t// -- Devices ------------------------------------------------------------\n\n\tenrollDevice(\n\t\tgroupId: string,\n\t\topts: {\n\t\t\tdeviceId: string;\n\t\t\tfingerprint: string;\n\t\t\tpublicKey: string;\n\t\t\tdisplayName?: string | null;\n\t\t},\n\t): void {\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO enrolled_devices(\n\t\t\t\t\tgroup_id, device_id, public_key, fingerprint, display_name, enabled, created_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, 1, ?)\n\t\t\t\tON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\tpublic_key = excluded.public_key,\n\t\t\t\t\tfingerprint = excluded.fingerprint,\n\t\t\t\t\tdisplay_name = excluded.display_name,\n\t\t\t\t\tenabled = 1`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tgroupId,\n\t\t\t\topts.deviceId,\n\t\t\t\topts.publicKey,\n\t\t\t\topts.fingerprint,\n\t\t\t\topts.displayName ?? null,\n\t\t\t\tnowISO(),\n\t\t\t);\n\t}\n\n\tlistEnrolledDevices(groupId: string, includeDisabled = false): Record<string, unknown>[] {\n\t\tconst where = includeDisabled ? \"\" : \"AND enabled = 1\";\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT group_id, device_id, fingerprint, display_name, enabled, created_at\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t WHERE group_id = ? ${where}\n\t\t\t\t ORDER BY created_at ASC, device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId)\n\t\t\t.map(rowToRecord);\n\t}\n\n\tgetEnrollment(groupId: string, deviceId: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT device_id, public_key, fingerprint, display_name\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t WHERE group_id = ? AND device_id = ? AND enabled = 1`,\n\t\t\t)\n\t\t\t.get(groupId, deviceId);\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\trenameDevice(groupId: string, deviceId: string, displayName: string): boolean {\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`UPDATE enrolled_devices SET display_name = ?\n\t\t\t\t WHERE group_id = ? AND device_id = ?`,\n\t\t\t)\n\t\t\t.run(displayName, groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\tsetDeviceEnabled(groupId: string, deviceId: string, enabled: boolean): boolean {\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`UPDATE enrolled_devices SET enabled = ?\n\t\t\t\t WHERE group_id = ? AND device_id = ?`,\n\t\t\t)\n\t\t\t.run(enabled ? 1 : 0, groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\tremoveDevice(groupId: string, deviceId: string): boolean {\n\t\tthis.db\n\t\t\t.prepare(\"DELETE FROM presence_records WHERE group_id = ? AND device_id = ?\")\n\t\t\t.run(groupId, deviceId);\n\t\tconst result = this.db\n\t\t\t.prepare(\"DELETE FROM enrolled_devices WHERE group_id = ? AND device_id = ?\")\n\t\t\t.run(groupId, deviceId);\n\t\treturn result.changes > 0;\n\t}\n\n\t// -- Invites ------------------------------------------------------------\n\n\tcreateInvite(opts: {\n\t\tgroupId: string;\n\t\tpolicy: string;\n\t\texpiresAt: string;\n\t\tcreatedBy?: string | null;\n\t}): Record<string, unknown> {\n\t\tconst now = nowISO();\n\t\tconst inviteId = tokenUrlSafe(12);\n\t\tconst token = tokenUrlSafe(24);\n\t\tconst group = this.getGroup(opts.groupId);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO coordinator_invites(\n\t\t\t\t\tinvite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NULL)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\tinviteId,\n\t\t\t\topts.groupId,\n\t\t\t\ttoken,\n\t\t\t\topts.policy,\n\t\t\t\topts.expiresAt,\n\t\t\t\tnow,\n\t\t\t\topts.createdBy ?? null,\n\t\t\t\t(group?.display_name as string) ?? null,\n\t\t\t);\n\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites WHERE invite_id = ?`,\n\t\t\t)\n\t\t\t.get(inviteId);\n\t\treturn row ? rowToRecord(row) : {};\n\t}\n\n\tgetInviteByToken(token: string): Record<string, unknown> | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites\n\t\t\t\t WHERE token = ?\n\t\t\t\t AND revoked_at IS NULL\n\t\t\t\t AND expires_at > ?`,\n\t\t\t)\n\t\t\t.get(token, new Date().toISOString());\n\t\treturn row ? rowToRecord(row) : null;\n\t}\n\n\tlistInvites(groupId: string): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT invite_id, group_id, token, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t FROM coordinator_invites WHERE group_id = ?\n\t\t\t\t ORDER BY created_at ASC`,\n\t\t\t)\n\t\t\t.all(groupId)\n\t\t\t.map(rowToRecord);\n\t}\n\n\t// -- Join requests ------------------------------------------------------\n\n\tcreateJoinRequest(opts: {\n\t\tgroupId: string;\n\t\tdeviceId: string;\n\t\tpublicKey: string;\n\t\tfingerprint: string;\n\t\tdisplayName?: string | null;\n\t\ttoken: string;\n\t}): Record<string, unknown> {\n\t\tconst now = nowISO();\n\t\tconst requestId = tokenUrlSafe(12);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO coordinator_join_requests(\n\t\t\t\t\trequest_id, group_id, device_id, public_key, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t) VALUES (?, ?, ?, ?, ?, ?, ?, 'pending', ?, NULL, NULL)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\trequestId,\n\t\t\t\topts.groupId,\n\t\t\t\topts.deviceId,\n\t\t\t\topts.publicKey,\n\t\t\t\topts.fingerprint,\n\t\t\t\topts.displayName ?? null,\n\t\t\t\topts.token,\n\t\t\t\tnow,\n\t\t\t);\n\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t)\n\t\t\t.get(requestId);\n\t\treturn row ? rowToRecord(row) : {};\n\t}\n\n\tlistJoinRequests(groupId: string, status = \"pending\"): Record<string, unknown>[] {\n\t\treturn this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t FROM coordinator_join_requests\n\t\t\t\t WHERE group_id = ? AND status = ?\n\t\t\t\t ORDER BY created_at ASC, device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId, status)\n\t\t\t.map(rowToRecord);\n\t}\n\n\treviewJoinRequest(opts: {\n\t\trequestId: string;\n\t\tapproved: boolean;\n\t\treviewedBy?: string | null;\n\t}): (Record<string, unknown> & { _no_transition?: boolean }) | null {\n\t\tconst row = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT request_id, group_id, device_id, public_key, fingerprint, display_name, token, status\n\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t)\n\t\t\t.get(opts.requestId) as Record<string, unknown> | undefined;\n\n\t\tif (!row) return null;\n\n\t\tif (row.status !== \"pending\") {\n\t\t\treturn { ...rowToRecord(row), _no_transition: true };\n\t\t}\n\n\t\t// Wrap enroll + status update in a transaction for atomicity\n\t\treturn this.db.transaction(() => {\n\t\t\tconst reviewedAt = nowISO();\n\t\t\tconst nextStatus = opts.approved ? \"approved\" : \"denied\";\n\n\t\t\tif (opts.approved) {\n\t\t\t\tthis.enrollDevice(row.group_id as string, {\n\t\t\t\t\tdeviceId: row.device_id as string,\n\t\t\t\t\tfingerprint: row.fingerprint as string,\n\t\t\t\t\tpublicKey: row.public_key as string,\n\t\t\t\t\tdisplayName: ((row.display_name as string) ?? \"\").trim() || null,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tthis.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`UPDATE coordinator_join_requests\n\t\t\t\t\t SET status = ?, reviewed_at = ?, reviewed_by = ?\n\t\t\t\t\t WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.run(nextStatus, reviewedAt, opts.reviewedBy ?? null, opts.requestId);\n\n\t\t\tconst updated = this.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT request_id, group_id, device_id, fingerprint, display_name, token, status, created_at, reviewed_at, reviewed_by\n\t\t\t\t\t FROM coordinator_join_requests WHERE request_id = ?`,\n\t\t\t\t)\n\t\t\t\t.get(opts.requestId);\n\t\t\treturn updated ? rowToRecord(updated) : null;\n\t\t})();\n\t}\n\n\t// -- Presence -----------------------------------------------------------\n\n\tupsertPresence(opts: {\n\t\tgroupId: string;\n\t\tdeviceId: string;\n\t\taddresses: string[];\n\t\tttlS: number;\n\t\tcapabilities?: Record<string, unknown> | null;\n\t}): Record<string, unknown> {\n\t\tconst now = new Date();\n\t\tconst expiresAt = new Date(now.getTime() + opts.ttlS * 1000).toISOString();\n\t\tconst normalized = mergeAddresses([], opts.addresses);\n\n\t\tthis.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO presence_records(group_id, device_id, addresses_json, last_seen_at, expires_at, capabilities_json)\n\t\t\t\t VALUES (?, ?, ?, ?, ?, ?)\n\t\t\t\t ON CONFLICT(group_id, device_id) DO UPDATE SET\n\t\t\t\t\taddresses_json = excluded.addresses_json,\n\t\t\t\t\tlast_seen_at = excluded.last_seen_at,\n\t\t\t\t\texpires_at = excluded.expires_at,\n\t\t\t\t\tcapabilities_json = excluded.capabilities_json`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\topts.groupId,\n\t\t\t\topts.deviceId,\n\t\t\t\tJSON.stringify(normalized),\n\t\t\t\tnow.toISOString(),\n\t\t\t\texpiresAt,\n\t\t\t\tJSON.stringify(opts.capabilities ?? {}),\n\t\t\t);\n\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tdevice_id: opts.deviceId,\n\t\t\taddresses: normalized,\n\t\t\texpires_at: expiresAt,\n\t\t};\n\t}\n\n\tlistGroupPeers(groupId: string, requestingDeviceId: string): Record<string, unknown>[] {\n\t\tconst now = new Date();\n\t\tconst rows = this.db\n\t\t\t.prepare(\n\t\t\t\t`SELECT enrolled_devices.device_id, enrolled_devices.fingerprint, enrolled_devices.display_name,\n\t\t\t\t\t\tpresence_records.addresses_json, presence_records.last_seen_at, presence_records.expires_at,\n\t\t\t\t\t\tpresence_records.capabilities_json\n\t\t\t\t FROM enrolled_devices\n\t\t\t\t LEFT JOIN presence_records\n\t\t\t\t ON presence_records.group_id = enrolled_devices.group_id\n\t\t\t\t AND presence_records.device_id = enrolled_devices.device_id\n\t\t\t\t WHERE enrolled_devices.group_id = ?\n\t\t\t\t AND enrolled_devices.enabled = 1\n\t\t\t\t AND enrolled_devices.device_id != ?\n\t\t\t\t ORDER BY enrolled_devices.device_id ASC`,\n\t\t\t)\n\t\t\t.all(groupId, requestingDeviceId) as Record<string, unknown>[];\n\n\t\treturn rows.map((row) => {\n\t\t\tconst expiresRaw = String(row.expires_at ?? \"\").trim();\n\t\t\tlet stale = true;\n\t\t\tif (expiresRaw) {\n\t\t\t\tconst expiresAt = new Date(expiresRaw);\n\t\t\t\t// Invalid Date has NaN time — treat as stale\n\t\t\t\tstale = Number.isNaN(expiresAt.getTime()) || expiresAt <= now;\n\t\t\t}\n\t\t\tconst addresses = stale\n\t\t\t\t? []\n\t\t\t\t: mergeAddresses([], JSON.parse((row.addresses_json as string) || \"[]\") as string[]);\n\n\t\t\treturn {\n\t\t\t\tdevice_id: row.device_id,\n\t\t\t\tfingerprint: row.fingerprint,\n\t\t\t\tdisplay_name: row.display_name,\n\t\t\t\taddresses,\n\t\t\t\tlast_seen_at: row.last_seen_at,\n\t\t\t\texpires_at: row.expires_at,\n\t\t\t\tstale,\n\t\t\t\tcapabilities: JSON.parse((row.capabilities_json as string) || \"{}\"),\n\t\t\t};\n\t\t});\n\t}\n}\n","/**\n * OpenCode provider configuration loading and resolution.\n *\n * Mirrors codemem/observer_config.py — reads ~/.config/opencode/opencode.json{c},\n * resolves custom provider settings (base URL, headers, API keys), and expands\n * environment variable / file placeholders in config values.\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\n\n// ---------------------------------------------------------------------------\n// JSONC helpers\n// ---------------------------------------------------------------------------\n\n/** Strip JavaScript-style `//` line comments and `/* ... *​/` block comments from JSONC text. */\nexport function stripJsonComments(text: string): string {\n\tconst result: string[] = [];\n\tlet inString = false;\n\tlet escapeNext = false;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst char = text.charAt(i);\n\t\tif (escapeNext) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"\\\\\" && inString) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = !inString;\n\t\t\tresult.push(char);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inString && char === \"/\" && i + 1 < text.length) {\n\t\t\tconst next = text.charAt(i + 1);\n\t\t\tif (next === \"/\") {\n\t\t\t\t// Line comment — skip until newline\n\t\t\t\tlet j = i + 2;\n\t\t\t\twhile (j < text.length && text.charAt(j) !== \"\\n\") j++;\n\t\t\t\ti = j - 1; // outer loop will increment\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (next === \"*\") {\n\t\t\t\t// Block comment — skip until */\n\t\t\t\tlet j = i + 2;\n\t\t\t\twhile (j < text.length - 1) {\n\t\t\t\t\tif (text.charAt(j) === \"*\" && text.charAt(j + 1) === \"/\") {\n\t\t\t\t\t\tj += 2;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tj++;\n\t\t\t\t}\n\t\t\t\ti = j - 1; // outer loop will increment\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t\tresult.push(char);\n\t}\n\treturn result.join(\"\");\n}\n\n/** Remove trailing commas before `]` or `}` (outside strings). */\nexport function stripTrailingCommas(text: string): string {\n\tconst result: string[] = [];\n\tlet inString = false;\n\tlet escapeNext = false;\n\tfor (let i = 0; i < text.length; i++) {\n\t\tconst char = text.charAt(i);\n\t\tif (escapeNext) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"\\\\\" && inString) {\n\t\t\tresult.push(char);\n\t\t\tescapeNext = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = !inString;\n\t\t\tresult.push(char);\n\t\t\tcontinue;\n\t\t}\n\t\tif (!inString && char === \",\") {\n\t\t\t// Look ahead past whitespace for a closing bracket/brace\n\t\t\tlet j = i + 1;\n\t\t\twhile (j < text.length && /\\s/.test(text.charAt(j))) j++;\n\t\t\tif (j < text.length && (text.charAt(j) === \"]\" || text.charAt(j) === \"}\")) {\n\t\t\t\tcontinue; // skip the trailing comma\n\t\t\t}\n\t\t}\n\t\tresult.push(char);\n\t}\n\treturn result.join(\"\");\n}\n\n// ---------------------------------------------------------------------------\n// Config loading\n// ---------------------------------------------------------------------------\n\n/** Load OpenCode config from `~/.config/opencode/opencode.json{c}`. */\nexport function loadOpenCodeConfig(): Record<string, unknown> {\n\tconst configDir = join(homedir(), \".config\", \"opencode\");\n\tconst candidates = [join(configDir, \"opencode.json\"), join(configDir, \"opencode.jsonc\")];\n\n\tconst configPath = candidates.find((p) => existsSync(p));\n\tif (!configPath) return {};\n\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configPath, \"utf-8\");\n\t} catch {\n\t\treturn {};\n\t}\n\n\t// Try plain JSON first\n\ttry {\n\t\treturn JSON.parse(text) as Record<string, unknown>;\n\t} catch {\n\t\t// fall through to JSONC\n\t}\n\n\ttry {\n\t\tconst cleaned = stripTrailingCommas(stripJsonComments(text));\n\t\treturn JSON.parse(cleaned) as Record<string, unknown>;\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Expand `~/...` paths like Python's `Path(...).expanduser()`. */\nexport function expandUserPath(value: string): string {\n\treturn value.startsWith(\"~/\") ? join(homedir(), value.slice(2)) : value;\n}\n\n/** Env var overrides matching Python's CONFIG_ENV_OVERRIDES. */\nexport const CODEMEM_CONFIG_ENV_OVERRIDES: Record<string, string> = {\n\tactor_id: \"CODEMEM_ACTOR_ID\",\n\tactor_display_name: \"CODEMEM_ACTOR_DISPLAY_NAME\",\n\tclaude_command: \"CODEMEM_CLAUDE_COMMAND\",\n\tobserver_provider: \"CODEMEM_OBSERVER_PROVIDER\",\n\tobserver_model: \"CODEMEM_OBSERVER_MODEL\",\n\tobserver_base_url: \"CODEMEM_OBSERVER_BASE_URL\",\n\tobserver_runtime: \"CODEMEM_OBSERVER_RUNTIME\",\n\tobserver_auth_source: \"CODEMEM_OBSERVER_AUTH_SOURCE\",\n\tobserver_auth_file: \"CODEMEM_OBSERVER_AUTH_FILE\",\n\tobserver_auth_command: \"CODEMEM_OBSERVER_AUTH_COMMAND\",\n\tobserver_auth_timeout_ms: \"CODEMEM_OBSERVER_AUTH_TIMEOUT_MS\",\n\tobserver_auth_cache_ttl_s: \"CODEMEM_OBSERVER_AUTH_CACHE_TTL_S\",\n\tobserver_headers: \"CODEMEM_OBSERVER_HEADERS\",\n\tobserver_max_chars: \"CODEMEM_OBSERVER_MAX_CHARS\",\n\tpack_observation_limit: \"CODEMEM_PACK_OBSERVATION_LIMIT\",\n\tpack_session_limit: \"CODEMEM_PACK_SESSION_LIMIT\",\n\tsync_enabled: \"CODEMEM_SYNC_ENABLED\",\n\tsync_host: \"CODEMEM_SYNC_HOST\",\n\tsync_port: \"CODEMEM_SYNC_PORT\",\n\tsync_interval_s: \"CODEMEM_SYNC_INTERVAL_S\",\n\tsync_mdns: \"CODEMEM_SYNC_MDNS\",\n\tsync_advertise: \"CODEMEM_SYNC_ADVERTISE\",\n\tsync_coordinator_url: \"CODEMEM_SYNC_COORDINATOR_URL\",\n\tsync_coordinator_group: \"CODEMEM_SYNC_COORDINATOR_GROUP\",\n\tsync_coordinator_groups: \"CODEMEM_SYNC_COORDINATOR_GROUPS\",\n\tsync_coordinator_timeout_s: \"CODEMEM_SYNC_COORDINATOR_TIMEOUT_S\",\n\tsync_coordinator_presence_ttl_s: \"CODEMEM_SYNC_COORDINATOR_PRESENCE_TTL_S\",\n\tsync_coordinator_admin_secret: \"CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET\",\n\tsync_projects_include: \"CODEMEM_SYNC_PROJECTS_INCLUDE\",\n\tsync_projects_exclude: \"CODEMEM_SYNC_PROJECTS_EXCLUDE\",\n\traw_events_sweeper_interval_s: \"CODEMEM_RAW_EVENTS_SWEEPER_INTERVAL_S\",\n};\n\n/** Resolve codemem config path with CODEMEM_CONFIG override. */\nexport function getCodememConfigPath(): string {\n\tconst envPath = process.env.CODEMEM_CONFIG?.trim();\n\tif (envPath) return expandUserPath(envPath);\n\tconst configDir = join(homedir(), \".config\", \"codemem\");\n\tconst candidates = [join(configDir, \"config.json\"), join(configDir, \"config.jsonc\")];\n\treturn candidates.find((p) => existsSync(p)) ?? join(configDir, \"config.json\");\n}\n\n/** Read codemem config file with the same JSON/JSONC behavior as Python. */\nexport function readCodememConfigFile(): Record<string, unknown> {\n\tconst configPath = getCodememConfigPath();\n\tif (!existsSync(configPath)) return {};\n\n\tlet text: string;\n\ttry {\n\t\ttext = readFileSync(configPath, \"utf-8\");\n\t} catch {\n\t\treturn {};\n\t}\n\n\tif (!text.trim()) return {};\n\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: {};\n\t} catch {\n\t\t// fall through to JSONC\n\t}\n\n\ttry {\n\t\tconst cleaned = stripTrailingCommas(stripJsonComments(text));\n\t\tconst parsed = JSON.parse(cleaned) as unknown;\n\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Persist the codemem config file as normalized JSON. */\nexport function writeCodememConfigFile(data: Record<string, unknown>, configPath?: string): string {\n\tconst targetPath = configPath ? expandUserPath(configPath) : getCodememConfigPath();\n\tmkdirSync(dirname(targetPath), { recursive: true });\n\twriteFileSync(targetPath, `${JSON.stringify(data, null, 2)}\\n`, \"utf8\");\n\treturn targetPath;\n}\n\n/** Return active env overrides for codemem config keys. */\nexport function getCodememEnvOverrides(): Record<string, string> {\n\tconst overrides: Record<string, string> = {};\n\tfor (const [key, envVar] of Object.entries(CODEMEM_CONFIG_ENV_OVERRIDES)) {\n\t\tconst val = process.env[envVar];\n\t\tif (val != null && val !== \"\") overrides[key] = envVar;\n\t}\n\treturn overrides;\n}\n\n// ---------------------------------------------------------------------------\n// Provider helpers\n// ---------------------------------------------------------------------------\n\ntype AnyRecord = Record<string, unknown>;\n\nfunction asRecord(value: unknown): AnyRecord | null {\n\treturn value != null && typeof value === \"object\" && !Array.isArray(value)\n\t\t? (value as AnyRecord)\n\t\t: null;\n}\n\n/** Get provider-specific config block from the opencode config. */\nexport function getOpenCodeProviderConfig(provider: string): AnyRecord {\n\tconst config = loadOpenCodeConfig();\n\tconst providerConfig = asRecord(config.provider);\n\tif (!providerConfig) return {};\n\tconst data = asRecord(providerConfig[provider]);\n\treturn data ?? {};\n}\n\n/** List all custom provider keys from the opencode config. */\nexport function listCustomProviders(): Set<string> {\n\tconst config = loadOpenCodeConfig();\n\tconst providerConfig = asRecord(config.provider);\n\tif (!providerConfig) return new Set();\n\treturn new Set(Object.keys(providerConfig));\n}\n\nconst BUILTIN_MODEL_PREFIX_PROVIDERS = new Set([\"openai\", \"anthropic\", \"opencode\"]);\n\nfunction extractProviderPrefix(value: unknown): string | null {\n\tif (typeof value !== \"string\" || !value.includes(\"/\")) return null;\n\tconst prefix = value.split(\"/\")[0]?.trim().toLowerCase();\n\treturn prefix ? prefix : null;\n}\n\nexport function listConfiguredOpenCodeProviders(): Set<string> {\n\tconst providers = listCustomProviders();\n\tconst config = loadOpenCodeConfig();\n\tfor (const key of [\"model\", \"small_model\"]) {\n\t\tconst prefix = extractProviderPrefix(config[key]);\n\t\tif (prefix) providers.add(prefix);\n\t}\n\treturn providers;\n}\n\nexport function listObserverProviderOptions(): string[] {\n\tconst providers = listConfiguredOpenCodeProviders();\n\tfor (const provider of BUILTIN_MODEL_PREFIX_PROVIDERS) providers.add(provider);\n\treturn Array.from(providers).sort((a, b) => a.localeCompare(b));\n}\n\nexport function resolveBuiltInProviderFromModel(model: string): string | null {\n\tconst prefix = extractProviderPrefix(model);\n\treturn prefix && BUILTIN_MODEL_PREFIX_PROVIDERS.has(prefix) ? prefix : null;\n}\n\nexport function resolveBuiltInProviderDefaultModel(provider: string): string | null {\n\tif (provider === \"openai\") return \"gpt-5.1-codex-mini\";\n\tif (provider === \"anthropic\") return \"claude-sonnet-4-20250514\";\n\tif (provider === \"opencode\") return \"opencode/gpt-5.1-codex-mini\";\n\treturn null;\n}\n\nexport function resolveBuiltInProviderModel(\n\tprovider: string,\n\tmodelName: string,\n): [baseUrl: string | null, modelId: string | null, headers: Record<string, string>] {\n\tif (provider !== \"opencode\") {\n\t\treturn [null, modelName || resolveBuiltInProviderDefaultModel(provider), {}];\n\t}\n\tconst name = modelName || resolveBuiltInProviderDefaultModel(provider) || \"\";\n\tconst prefix = `${provider}/`;\n\tconst shortName = name.startsWith(prefix) ? name.slice(prefix.length) : name;\n\treturn [\"https://opencode.ai/zen/v1\", shortName || null, {}];\n}\n\n/** Extract provider prefix from a model string like `\"myprovider/model-name\"`. */\nexport function resolveCustomProviderFromModel(\n\tmodel: string,\n\tproviders: Set<string>,\n): string | null {\n\tif (!model || !model.includes(\"/\")) return null;\n\tconst prefix = model.split(\"/\")[0] ?? \"\";\n\treturn prefix && providers.has(prefix) ? prefix : null;\n}\n\n// ---------------------------------------------------------------------------\n// Placeholder resolution\n// ---------------------------------------------------------------------------\n\n/**\n * Expand `$ENV_VAR` / `${ENV_VAR}` references and `{file:/path}` placeholders.\n *\n * Environment variable expansion mirrors Python's `os.path.expandvars`.\n * File placeholders read the file content and substitute it inline.\n */\nexport function resolvePlaceholder(value: string): string {\n\tconst expanded = expandEnvVars(value);\n\treturn resolveFilePlaceholder(expanded);\n}\n\n/** Expand `$VAR` and `${VAR}` environment variable references. */\nfunction expandEnvVars(value: string): string {\n\treturn value.replace(/\\$\\{([^}]+)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, braced, bare) => {\n\t\tconst name = braced ?? bare;\n\t\treturn process.env[name] ?? match;\n\t});\n}\n\n/** Expand `{file:path}` placeholders by reading the referenced file. */\nfunction resolveFilePlaceholder(value: string): string {\n\tif (!value.includes(\"{file:\")) return value;\n\treturn value.replace(/\\{file:([^}]+)\\}/g, (match, rawPath: string) => {\n\t\tconst trimmed = rawPath.trim();\n\t\tif (!trimmed) return match;\n\t\tconst resolved = expandEnvVars(trimmed).replace(/^~/, homedir());\n\t\ttry {\n\t\t\treturn readFileSync(resolved, \"utf-8\").trim();\n\t\t} catch {\n\t\t\treturn match;\n\t\t}\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Provider option extraction\n// ---------------------------------------------------------------------------\n\n/** Extract the `options` sub-object from a provider config block. */\nexport function getProviderOptions(providerConfig: AnyRecord): AnyRecord {\n\tconst options = asRecord(providerConfig.options);\n\treturn options ?? {};\n}\n\n/** Extract `baseURL` / `baseUrl` / `base_url` from provider config. */\nexport function getProviderBaseUrl(providerConfig: AnyRecord): string | null {\n\tconst options = getProviderOptions(providerConfig);\n\t// Use || (not ??) so empty strings fall through to the next candidate (matches Python's `or`)\n\tconst baseUrl = options.baseURL || options.baseUrl || options.base_url || providerConfig.base_url;\n\treturn typeof baseUrl === \"string\" && baseUrl ? baseUrl : null;\n}\n\n/** Extract and resolve headers (with placeholder expansion) from provider config. */\nexport function getProviderHeaders(providerConfig: AnyRecord): Record<string, string> {\n\tconst options = getProviderOptions(providerConfig);\n\tconst headers = asRecord(options.headers);\n\tif (!headers) return {};\n\tconst parsed: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (typeof key !== \"string\" || typeof value !== \"string\") continue;\n\t\tparsed[key] = resolvePlaceholder(value);\n\t}\n\treturn parsed;\n}\n\n/** Extract API key from provider config (direct value or env-var reference). */\nexport function getProviderApiKey(providerConfig: AnyRecord): string | null {\n\tconst options = getProviderOptions(providerConfig);\n\t// Use || (not ??) so empty strings fall through (matches Python's `or`)\n\tconst apiKey = options.apiKey || providerConfig.apiKey;\n\tif (typeof apiKey === \"string\" && apiKey) {\n\t\treturn resolvePlaceholder(apiKey);\n\t}\n\tconst apiKeyEnv = (options.apiKeyEnv ?? options.api_key_env) as string | undefined;\n\tif (typeof apiKeyEnv === \"string\" && apiKeyEnv) {\n\t\tconst value = process.env[apiKeyEnv];\n\t\tif (value) return value;\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Custom provider model resolution\n// ---------------------------------------------------------------------------\n\n/** Find the default model for a custom provider. */\nexport function resolveCustomProviderDefaultModel(provider: string): string | null {\n\tconst providerConfig = getOpenCodeProviderConfig(provider);\n\tconst options = getProviderOptions(providerConfig);\n\tconst defaultModel =\n\t\toptions.defaultModel ??\n\t\toptions.default_model ??\n\t\tproviderConfig.defaultModel ??\n\t\tproviderConfig.default_model;\n\tif (typeof defaultModel === \"string\" && defaultModel) {\n\t\treturn defaultModel.startsWith(`${provider}/`) ? defaultModel : `${provider}/${defaultModel}`;\n\t}\n\tconst models = asRecord(providerConfig.models);\n\tif (models) {\n\t\tconst firstKey = Object.keys(models)[0];\n\t\tif (typeof firstKey === \"string\" && firstKey) {\n\t\t\treturn `${provider}/${firstKey}`;\n\t\t}\n\t}\n\treturn null;\n}\n\n/**\n * Resolve base_url, model_id, and headers for a custom provider model.\n *\n * Returns `[baseUrl, modelId, headers]`.\n */\nexport function resolveCustomProviderModel(\n\tprovider: string,\n\tmodelName: string,\n): [baseUrl: string | null, modelId: string | null, headers: Record<string, string>] {\n\tconst providerConfig = getOpenCodeProviderConfig(provider);\n\tconst baseUrl = getProviderBaseUrl(providerConfig);\n\tconst headers = getProviderHeaders(providerConfig);\n\n\tlet name = modelName;\n\tif (!name) {\n\t\tname = resolveCustomProviderDefaultModel(provider) ?? \"\";\n\t}\n\n\tconst prefix = `${provider}/`;\n\tconst shortName = name.startsWith(prefix) ? name.slice(prefix.length) : name;\n\n\tconst models = asRecord(providerConfig.models);\n\tlet modelId: string | null = shortName;\n\tif (models) {\n\t\tconst modelConfig = asRecord(models[shortName]);\n\t\tif (modelConfig && typeof modelConfig.id === \"string\") {\n\t\t\tmodelId = modelConfig.id;\n\t\t}\n\t}\n\n\tif (typeof modelId !== \"string\" || !modelId) {\n\t\tmodelId = null;\n\t}\n\n\treturn [baseUrl, modelId, headers];\n}\n","/**\n * Database connection and schema initialization for the codemem TS backend.\n *\n * Mirrors codemem/db.py — same pragmas, same WAL mode, same sqlite-vec loading.\n * During Phase 1 of the migration, Python owns DDL (schema migrations).\n * The TS runtime validates the schema version but does NOT run migrations.\n */\n\nimport {\n\tcloseSync,\n\tcopyFileSync,\n\texistsSync,\n\tmkdirSync,\n\topenSync,\n\treaddirSync,\n\trenameSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { basename, dirname, join } from \"node:path\";\nimport type { Database as DatabaseType } from \"better-sqlite3\";\nimport Database from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport { expandUserPath } from \"./observer-config.js\";\n\n// Re-export the Database type for consumers\nexport type { DatabaseType as Database };\n\n/** Current schema version this TS runtime was built against. */\nexport const SCHEMA_VERSION = 6;\n\n/**\n * Minimum schema version the TS runtime can operate with.\n * Per the coexistence contract (docs/plans/2026-03-15-db-coexistence-contract.md),\n * TS must tolerate additive newer schemas. It only hard-fails if the schema is\n * too old to have the tables/columns it needs.\n */\nexport const MIN_COMPATIBLE_SCHEMA = 6;\n\n/** Required tables the TS runtime needs to function. */\nconst REQUIRED_TABLES = [\n\t\"memory_items\",\n\t\"sessions\",\n\t\"artifacts\",\n\t\"raw_events\",\n\t\"raw_event_sessions\",\n\t\"usage_events\",\n] as const;\n\n/** Marker file written after the first successful TS access to a DB. */\nconst TS_MARKER = \".codemem-ts-accessed\";\n\n/** Lock file to avoid concurrent first-access backups. */\nconst TS_BACKUP_LOCK = \".codemem-ts-backup.lock\";\n\n/** Consider a backup lock stale after 15 minutes. */\nconst TS_BACKUP_LOCK_STALE_MS = 15 * 60 * 1000;\n\ninterface BackupLockAcquireResult {\n\tfd: number | null;\n\tcontention: boolean;\n}\n\nfunction acquireBackupLock(lockPath: string): BackupLockAcquireResult {\n\tmkdirSync(dirname(lockPath), { recursive: true });\n\ttry {\n\t\treturn { fd: openSync(lockPath, \"wx\"), contention: false };\n\t} catch (err) {\n\t\tconst code = err && typeof err === \"object\" ? (err as NodeJS.ErrnoException).code : undefined;\n\t\tif (code === \"EEXIST\") {\n\t\t\tlet stale = false;\n\t\t\ttry {\n\t\t\t\tconst ageMs = Date.now() - statSync(lockPath).mtimeMs;\n\t\t\t\tstale = Number.isFinite(ageMs) && ageMs > TS_BACKUP_LOCK_STALE_MS;\n\t\t\t} catch {\n\t\t\t\t// If we can't read lock metadata, treat as live contention.\n\t\t\t}\n\n\t\t\tif (!stale) {\n\t\t\t\treturn { fd: null, contention: true };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tunlinkSync(lockPath);\n\t\t\t} catch (unlinkErr) {\n\t\t\t\tconsole.error(\n\t\t\t\t\t`[codemem] Warning: failed to clear stale backup lock at ${lockPath}:`,\n\t\t\t\t\tunlinkErr,\n\t\t\t\t);\n\t\t\t\treturn { fd: null, contention: false };\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\treturn { fd: openSync(lockPath, \"wx\"), contention: false };\n\t\t\t} catch (retryErr) {\n\t\t\t\tconst retryCode =\n\t\t\t\t\tretryErr && typeof retryErr === \"object\"\n\t\t\t\t\t\t? (retryErr as NodeJS.ErrnoException).code\n\t\t\t\t\t\t: undefined;\n\t\t\t\tif (retryCode === \"EEXIST\") {\n\t\t\t\t\treturn { fd: null, contention: true };\n\t\t\t\t}\n\t\t\t\tconsole.error(`[codemem] Warning: failed to acquire backup lock at ${lockPath}:`, retryErr);\n\t\t\t\treturn { fd: null, contention: false };\n\t\t\t}\n\t\t}\n\n\t\tconsole.error(`[codemem] Warning: failed to acquire backup lock at ${lockPath}:`, err);\n\t\treturn { fd: null, contention: false };\n\t}\n}\n\n/** Default database path — matches Python's DEFAULT_DB_PATH. */\nexport const DEFAULT_DB_PATH = join(homedir(), \".codemem\", \"mem.sqlite\");\n\n/**\n * Resolve the database path from explicit arg → CODEMEM_DB env → default.\n * All TS entry points should use this instead of hardcoding fallback logic.\n */\nexport function resolveDbPath(explicit?: string): string {\n\tif (explicit) return expandUserPath(explicit);\n\tconst envPath = process.env.CODEMEM_DB;\n\tif (envPath) return expandUserPath(envPath);\n\treturn DEFAULT_DB_PATH;\n}\n\n/** Legacy database paths that may exist from older installs. */\nconst LEGACY_DB_PATHS = [\n\tjoin(homedir(), \".codemem.sqlite\"),\n\tjoin(homedir(), \".opencode-mem.sqlite\"),\n];\n\n/** WAL sidecar extensions for a SQLite database file. */\nconst SIDECAR_EXTENSIONS = [\"-wal\", \"-shm\"];\n\n/**\n * Move a file and its WAL sidecars to a new location.\n * Falls back to copy+delete if rename fails (cross-device).\n */\nfunction moveWithSidecars(src: string, dst: string): void {\n\tmkdirSync(dirname(dst), { recursive: true });\n\tconst pairs: [string, string][] = [\n\t\t[src, dst],\n\t\t...SIDECAR_EXTENSIONS.map((ext): [string, string] => [src + ext, dst + ext]),\n\t];\n\tfor (const [srcPath, dstPath] of pairs) {\n\t\tif (!existsSync(srcPath)) continue;\n\t\ttry {\n\t\t\trenameSync(srcPath, dstPath);\n\t\t} catch {\n\t\t\ttry {\n\t\t\t\tcopyFileSync(srcPath, dstPath);\n\t\t\t\ttry {\n\t\t\t\t\tunlinkSync(srcPath);\n\t\t\t\t} catch {\n\t\t\t\t\t// Best effort — original left in place\n\t\t\t\t}\n\t\t\t} catch (copyErr) {\n\t\t\t\t// TOCTOU: source vanished between existsSync and copy (concurrent migration).\n\t\t\t\t// If target now exists, another process already migrated — that's fine.\n\t\t\t\tif (existsSync(dstPath)) continue;\n\t\t\t\tthrow copyErr;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Migrate legacy database paths to the current default location.\n *\n * Only runs when dbPath is the DEFAULT_DB_PATH and doesn't already exist.\n * Matches Python's migrate_legacy_default_db in db.py.\n */\nexport function migrateLegacyDbPath(dbPath: string): void {\n\tif (dbPath !== DEFAULT_DB_PATH) return;\n\tif (existsSync(dbPath)) return;\n\n\tfor (const legacyPath of LEGACY_DB_PATHS) {\n\t\tif (!existsSync(legacyPath)) continue;\n\t\tmoveWithSidecars(legacyPath, dbPath);\n\t\treturn;\n\t}\n}\n\n/**\n * Open a better-sqlite3 connection with the standard codemem pragmas.\n *\n * Migrates legacy DB paths if needed, creates parent directories,\n * sets WAL mode, busy timeout, foreign keys.\n * Does NOT initialize or migrate the schema — during Phase 1, Python owns DDL.\n */\nexport function connect(dbPath: string = DEFAULT_DB_PATH): DatabaseType {\n\tmigrateLegacyDbPath(dbPath);\n\tmkdirSync(dirname(dbPath), { recursive: true });\n\tconst db = new Database(dbPath);\n\n\t// Match Python's connect() pragmas exactly\n\tdb.pragma(\"foreign_keys = ON\");\n\tdb.pragma(\"busy_timeout = 5000\");\n\n\tconst journalMode = db.pragma(\"journal_mode = WAL\", { simple: true }) as string;\n\tif (journalMode.toLowerCase() !== \"wal\") {\n\t\tconsole.warn(\n\t\t\t`Failed to enable WAL mode (got ${journalMode}). Concurrent access may not work correctly.`,\n\t\t);\n\t}\n\n\tdb.pragma(\"synchronous = NORMAL\");\n\n\treturn db;\n}\n\n/**\n * Load the sqlite-vec extension into an open database connection.\n *\n * Call this after connect() when vector operations are needed.\n * Skipped when CODEMEM_EMBEDDING_DISABLED=1.\n */\nexport function loadSqliteVec(db: DatabaseType): void {\n\tif (isEmbeddingDisabled()) {\n\t\treturn;\n\t}\n\n\tsqliteVec.load(db);\n\n\tconst row = db.prepare(\"SELECT vec_version() AS v\").get() as { v: string } | undefined;\n\tif (!row?.v) {\n\t\tthrow new Error(\"sqlite-vec loaded but version check failed\");\n\t}\n}\n\n/** Check if embeddings are disabled via environment variable. */\nexport function isEmbeddingDisabled(): boolean {\n\tconst val = process.env.CODEMEM_EMBEDDING_DISABLED?.toLowerCase();\n\treturn val === \"1\" || val === \"true\" || val === \"yes\";\n}\n\n/**\n * Read the schema user_version from the database.\n *\n * During Phase 1, the TS runtime uses this to verify the schema is initialized\n * (by Python) before operating. It does NOT set or bump the version.\n */\nexport function getSchemaVersion(db: DatabaseType): number {\n\tconst row = db.pragma(\"user_version\", { simple: true });\n\treturn typeof row === \"number\" ? row : 0;\n}\n\n/**\n * Create a timestamped backup of the database on first TS access.\n *\n * The backup file is named `mem.sqlite.pre-ts-YYYYMMDDTHHMMSS.bak` and placed\n * next to the original. A marker file prevents repeated backups. This ensures\n * users can recover if the TS runtime introduces bugs that corrupt the DB\n * during the migration period.\n */\nexport function backupOnFirstAccess(dbPath: string): void {\n\tconst markerPath = join(dirname(dbPath), TS_MARKER);\n\tif (existsSync(markerPath)) return;\n\n\t// Find the actual DB file to back up. It may be at the target path already,\n\t// or at a legacy location that migrateLegacyDbPath() will move later.\n\t// Back up whichever exists BEFORE migration runs.\n\tlet sourceDbPath = dbPath;\n\tif (!existsSync(dbPath)) {\n\t\t// Check legacy locations — same order as migrateLegacyDbPath()\n\t\tconst legacyPaths = [\n\t\t\tjoin(dirname(dbPath), \"..\", \".codemem.sqlite\"),\n\t\t\tjoin(dirname(dbPath), \"..\", \".opencode-mem.sqlite\"),\n\t\t];\n\t\tconst legacyPath = legacyPaths.find((p) => existsSync(p));\n\t\tif (legacyPath) {\n\t\t\tsourceDbPath = legacyPath;\n\t\t} else {\n\t\t\treturn; // Fresh DB, nothing to back up\n\t\t}\n\t}\n\n\tconst lockPath = join(dirname(dbPath), TS_BACKUP_LOCK);\n\tconst { fd: lockFd, contention } = acquireBackupLock(lockPath);\n\tif (contention) {\n\t\t// Another process is handling first-access backup.\n\t\treturn;\n\t}\n\n\tconst writeMarker = (): void => {\n\t\ttry {\n\t\t\tmkdirSync(dirname(markerPath), { recursive: true });\n\t\t\twriteFileSync(markerPath, new Date().toISOString(), \"utf-8\");\n\t\t} catch {\n\t\t\t// Non-fatal — worst case we attempt backup again later.\n\t\t}\n\t};\n\n\tconst hasViableExistingBackup = (): boolean => {\n\t\tlet sourceSize = 0;\n\t\ttry {\n\t\t\tsourceSize = statSync(sourceDbPath).size;\n\t\t} catch {\n\t\t\tsourceSize = 0;\n\t\t}\n\t\tconst minViableSize = sourceSize > 0 ? Math.floor(sourceSize * 0.9) : 1;\n\t\tconst prefix = `${basename(sourceDbPath)}.pre-ts-`;\n\t\ttry {\n\t\t\tconst entries = readdirSync(dirname(sourceDbPath));\n\t\t\tfor (const entry of entries) {\n\t\t\t\tif (!entry.startsWith(prefix) || !entry.endsWith(\".bak\")) continue;\n\t\t\t\tconst fullPath = join(dirname(sourceDbPath), entry);\n\t\t\t\ttry {\n\t\t\t\t\tif (statSync(fullPath).size >= minViableSize) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Ignore races while inspecting backup candidates.\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore directory read errors — proceed with normal backup path.\n\t\t}\n\t\treturn false;\n\t};\n\n\tconst ts = new Date().toISOString().replace(/[:.]/g, \"\").slice(0, 15);\n\tconst backupPath = `${sourceDbPath}.pre-ts-${ts}.bak`;\n\tlet backupSucceeded = false;\n\ttry {\n\t\tif (existsSync(markerPath)) return;\n\t\tif (hasViableExistingBackup()) {\n\t\t\twriteMarker();\n\t\t\treturn;\n\t\t}\n\n\t\tcopyFileSync(sourceDbPath, backupPath);\n\t\t// Also back up WAL/SHM if present (they contain uncommitted data)\n\t\tconst walPath = `${sourceDbPath}-wal`;\n\t\tconst shmPath = `${sourceDbPath}-shm`;\n\t\tif (existsSync(walPath)) copyFileSync(walPath, `${backupPath}-wal`);\n\t\tif (existsSync(shmPath)) copyFileSync(shmPath, `${backupPath}-shm`);\n\t\tconsole.error(`[codemem] First TS access — backed up database to ${backupPath}`);\n\t\tbackupSucceeded = true;\n\t} catch (err) {\n\t\tconsole.error(`[codemem] Warning: failed to create backup at ${backupPath}:`, err);\n\t\t// Continue — backup failure shouldn't prevent operation, but don't write marker\n\t} finally {\n\t\tif (lockFd != null) {\n\t\t\ttry {\n\t\t\t\tcloseSync(lockFd);\n\t\t\t} catch {\n\t\t\t\t// Ignore close errors.\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tunlinkSync(lockPath);\n\t\t\t} catch {\n\t\t\t\t// Ignore lock cleanup errors.\n\t\t\t}\n\t\t}\n\t}\n\n\t// Only write marker after backup succeeds — a transient failure should\n\t// retry on next access, not permanently suppress the safety net.\n\tif (backupSucceeded) {\n\t\twriteMarker();\n\t}\n}\n\n/**\n * Verify the database schema is initialized and compatible.\n *\n * Per the coexistence contract: TS tolerates additive newer schemas (Python may\n * have run migrations that add tables/columns the TS runtime doesn't know about).\n * TS only hard-fails if:\n * - Schema is uninitialized (version 0)\n * - Schema is too old (below MIN_COMPATIBLE_SCHEMA)\n * - Required tables are missing\n * - FTS5 index is missing (needed for search)\n *\n * Warns (but continues) if schema is newer than SCHEMA_VERSION — the additive\n * changes are assumed safe per the coexistence contract.\n */\nexport function assertSchemaReady(db: DatabaseType): void {\n\tconst version = getSchemaVersion(db);\n\tif (version === 0) {\n\t\tthrow new Error(\n\t\t\t\"Database schema is not initialized. \" +\n\t\t\t\t\"Run the Python runtime to initialize: uv run codemem stats\",\n\t\t);\n\t}\n\tif (version < MIN_COMPATIBLE_SCHEMA) {\n\t\tthrow new Error(\n\t\t\t`Database schema version ${version} is older than minimum compatible (${MIN_COMPATIBLE_SCHEMA}). ` +\n\t\t\t\t\"Run the Python runtime to complete migrations: uv run codemem stats\",\n\t\t);\n\t}\n\tif (version > SCHEMA_VERSION) {\n\t\tconsole.warn(\n\t\t\t`Database schema version ${version} is newer than this TS runtime (${SCHEMA_VERSION}). ` +\n\t\t\t\t\"Running in compatibility mode — additive schema changes are tolerated.\",\n\t\t);\n\t}\n\n\t// Validate required tables exist (catches corrupt or partially migrated DBs)\n\tconst missing = REQUIRED_TABLES.filter((t) => !tableExists(db, t));\n\tif (missing.length > 0) {\n\t\tthrow new Error(\n\t\t\t`Required tables missing: ${missing.join(\", \")}. ` +\n\t\t\t\t\"The database may be corrupt or from an incompatible version.\",\n\t\t);\n\t}\n\n\t// FTS5 index is required for search\n\tif (!tableExists(db, \"memory_fts\")) {\n\t\tthrow new Error(\n\t\t\t\"FTS5 index (memory_fts) is missing. \" +\n\t\t\t\t\"Run the Python runtime to rebuild: uv run codemem stats\",\n\t\t);\n\t}\n}\n\n/** Check if a table exists in the database. */\nexport function tableExists(db: DatabaseType, table: string): boolean {\n\tconst row = db\n\t\t.prepare(\"SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = ?\")\n\t\t.get(table);\n\treturn row !== undefined;\n}\n\n/** Check if a column exists in a table. */\nexport function columnExists(db: DatabaseType, table: string, column: string): boolean {\n\tif (!tableExists(db, table)) return false;\n\tconst row = db\n\t\t.prepare(\"SELECT 1 FROM pragma_table_info(?) WHERE name = ? LIMIT 1\")\n\t\t.get(table, column);\n\treturn row !== undefined;\n}\n\n/**\n * Apply additive compatibility fixes for legacy TS-era schemas.\n *\n * These are safe, one-way `ALTER TABLE ... ADD COLUMN` updates used to\n * prevent runtime failures when older local databases are missing columns\n * introduced in later releases.\n */\nexport function ensureAdditiveSchemaCompatibility(db: DatabaseType): void {\n\tif (!tableExists(db, \"raw_event_flush_batches\")) return;\n\n\tconst additiveColumns: Array<{ name: string; ddl: string }> = [\n\t\t{ name: \"error_message\", ddl: \"TEXT\" },\n\t\t{ name: \"error_type\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_provider\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_model\", ddl: \"TEXT\" },\n\t\t{ name: \"observer_runtime\", ddl: \"TEXT\" },\n\t\t{ name: \"attempt_count\", ddl: \"INTEGER NOT NULL DEFAULT 0\" },\n\t];\n\n\tfor (const { name, ddl } of additiveColumns) {\n\t\tif (columnExists(db, \"raw_event_flush_batches\", name)) continue;\n\t\ttry {\n\t\t\tdb.exec(`ALTER TABLE raw_event_flush_batches ADD COLUMN ${name} ${ddl}`);\n\t\t} catch (err) {\n\t\t\tconst message = err instanceof Error ? err.message.toLowerCase() : \"\";\n\t\t\tconst duplicateColumn = message.includes(\"duplicate column name\");\n\t\t\tif (duplicateColumn && columnExists(db, \"raw_event_flush_batches\", name)) {\n\t\t\t\t// Another process may have raced and added the column first.\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n\n/** Safely parse a JSON string, returning {} on failure. */\nexport function fromJson(text: string | null | undefined): Record<string, unknown> {\n\tif (!text) return {};\n\ttry {\n\t\tconst parsed = JSON.parse(text);\n\t\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\t\treturn {};\n\t\t}\n\t\treturn parsed as Record<string, unknown>;\n\t} catch {\n\t\tconsole.warn(`[codemem] fromJson: invalid JSON (${text.slice(0, 80)}...)`);\n\t\treturn {};\n\t}\n}\n\n/**\n * Parse a JSON string strictly — throws on invalid input.\n *\n * Use in mutation paths (replication apply, import) where silently\n * returning {} would mask data corruption. Read paths should use\n * fromJson() which is forgiving.\n */\nexport function fromJsonStrict(text: string | null | undefined): Record<string, unknown> {\n\tif (!text) return {};\n\tconst parsed = JSON.parse(text);\n\tif (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n\t\tthrow new Error(\n\t\t\t`fromJsonStrict: expected object, got ${Array.isArray(parsed) ? \"array\" : typeof parsed}`,\n\t\t);\n\t}\n\treturn parsed as Record<string, unknown>;\n}\n\n/** Serialize a value to JSON, defaulting null/undefined to \"{}\". */\nexport function toJson(data: unknown): string {\n\tif (data == null) return \"{}\";\n\treturn JSON.stringify(data);\n}\n\n/**\n * Serialize a value to JSON, preserving null as SQL NULL.\n *\n * Use this for columns that store JSON arrays (facts, concepts, files_read,\n * files_modified) where NULL means \"no data\" and \"{}\" would be corruption.\n * Also normalizes empty objects ({}) to null — Python's from_json returns {}\n * for empty DB values, so imported exports carry {} instead of null.\n * Use `toJson` for metadata_json where \"{}\" is the correct empty default.\n */\nexport function toJsonNullable(data: unknown): string | null {\n\tif (data == null) return null;\n\t// Normalize empty objects to null — these are never valid array column values\n\tif (\n\t\ttypeof data === \"object\" &&\n\t\t!Array.isArray(data) &&\n\t\tObject.keys(data as object).length === 0\n\t) {\n\t\treturn null;\n\t}\n\treturn JSON.stringify(data);\n}\n","/**\n * Low-signal observation filtering for the ingest pipeline.\n *\n * Ports the relevant parts of codemem/summarizer.py — detects observations\n * that are too generic or noisy to store as memories.\n */\n\n// ---------------------------------------------------------------------------\n// Patterns that indicate low-signal content\n// ---------------------------------------------------------------------------\n\n/**\n * Observation-level patterns that indicate content too generic to store.\n * Empty by default — trust the observer LLM. Only patterns that consistently\n * get through observer guidance are added here.\n */\nconst LOW_SIGNAL_OBSERVATION_PATTERNS: RegExp[] = [\n\t/\\bno\\s+code\\s+changes?\\s+(?:were|was)\\s+(?:recorded|made)\\b/i,\n\t/\\bno\\s+code\\s+was\\s+modified\\b/i,\n\t/\\bno\\s+new\\s+(?:code|configuration|config|documentation)(?:\\s+or\\s+(?:code|configuration|config|documentation))?\\s+(?:was|were)\\s+(?:shipped|delivered)\\b/i,\n\t/\\bno\\s+new\\s+deliverables?\\b/i,\n\t/\\bno\\s+definitive\\s+(?:code\\s+rewrite|feature\\s+delivery)(?:\\s+or\\s+(?:code\\s+rewrite|feature\\s+delivery))?\\s+(?:occurred|happened)\\b/i,\n\t/\\bonly\\s+file\\s+inspection\\s+occurred\\b/i,\n\t/\\bonly\\s+produced\\s+(?:an?\\s+)?understanding\\b/i,\n\t/\\bconsisted\\s+entirely\\s+of\\s+capturing\\b/i,\n\t/\\bno\\s+fully\\s+resolved\\s+deliverable\\b/i,\n\t/\\beffort\\s+focused\\s+on\\s+clarifying\\b/i,\n\t/\\bno\\s+code\\s*,?\\s+configuration\\s*,?\\s+or\\s+documentation\\s+changes?\\s+(?:were|was)\\s+made\\b/i,\n\t/\\bwork\\s+consisted\\s+entirely\\s+of\\s+capturing\\s+the\\s+current\\s+state\\b/i,\n\t/\\bprimary\\s+user\\s+request\\s+details\\s+were\\s+absent\\b/i,\n];\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** Normalize observation text: strip leading bullets/markers, collapse whitespace. */\nexport function normalizeObservation(text: string): string {\n\tlet cleaned = text.trim().replace(/^[\\s\\-\\u2022\\u2514\\u203a>$]+/, \"\");\n\tcleaned = cleaned.replace(/\\s+/g, \" \").trim();\n\treturn cleaned;\n}\n\n/**\n * Return true if the observation text is too generic / low-signal to store.\n *\n * Checks against known patterns of empty or self-referential content that\n * the observer LLM sometimes generates.\n */\nexport function isLowSignalObservation(text: string): boolean {\n\tconst normalized = normalizeObservation(text);\n\tif (!normalized) return true;\n\treturn LOW_SIGNAL_OBSERVATION_PATTERNS.some((p) => p.test(normalized));\n}\n","import { existsSync, lstatSync, readFileSync } from \"node:fs\";\nimport { basename, dirname, resolve } from \"node:path\";\n\nexport function projectBasename(value: string): string {\n\tconst normalized = value.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\tif (!normalized) return \"\";\n\tconst parts = normalized.split(\"/\");\n\treturn parts[parts.length - 1] ?? \"\";\n}\n\nexport function projectColumnClause(\n\tcolumnExpr: string,\n\tproject: string,\n): { clause: string; params: string[] } {\n\tconst trimmed = project.trim();\n\tif (!trimmed) return { clause: \"\", params: [] };\n\tconst value = /[\\\\/]/.test(trimmed) ? projectBasename(trimmed) : trimmed;\n\tif (!value) return { clause: \"\", params: [] };\n\treturn {\n\t\tclause: `(${columnExpr} = ? OR ${columnExpr} LIKE ? OR ${columnExpr} LIKE ?)`,\n\t\tparams: [value, `%/${value}`, `%\\\\${value}`],\n\t};\n}\n\nexport function projectClause(project: string): { clause: string; params: string[] } {\n\treturn projectColumnClause(\"sessions.project\", project);\n}\n\nexport function projectMatchesFilter(\n\tprojectFilter: string | null | undefined,\n\titemProject: string | null | undefined,\n): boolean {\n\tif (!projectFilter) return true;\n\tif (!itemProject) return false;\n\tconst normalizedFilter = projectFilter.trim().replaceAll(\"\\\\\", \"/\");\n\tif (!normalizedFilter) return true;\n\tconst filterValue = normalizedFilter.includes(\"/\")\n\t\t? projectBasename(normalizedFilter)\n\t\t: normalizedFilter;\n\tconst normalizedProject = itemProject.replaceAll(\"\\\\\", \"/\");\n\treturn normalizedProject === filterValue || normalizedProject.endsWith(`/${filterValue}`);\n}\n\nfunction findGitAnchor(startCwd: string): string | null {\n\tlet current = resolve(startCwd);\n\twhile (true) {\n\t\tconst gitPath = resolve(current, \".git\");\n\t\tif (existsSync(gitPath)) {\n\t\t\ttry {\n\t\t\t\tif (lstatSync(gitPath).isDirectory()) {\n\t\t\t\t\treturn current;\n\t\t\t\t}\n\t\t\t\tconst text = readFileSync(gitPath, \"utf8\").trim();\n\t\t\t\tif (text.startsWith(\"gitdir:\")) {\n\t\t\t\t\tconst gitdir = resolve(current, text.slice(\"gitdir:\".length).trim()).replaceAll(\n\t\t\t\t\t\t\"\\\\\",\n\t\t\t\t\t\t\"/\",\n\t\t\t\t\t);\n\t\t\t\t\tconst worktreeMarker = \"/.git/worktrees/\";\n\t\t\t\t\tconst worktreeIndex = gitdir.indexOf(worktreeMarker);\n\t\t\t\t\tif (worktreeIndex >= 0) {\n\t\t\t\t\t\treturn gitdir.slice(0, worktreeIndex);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn current;\n\t\t\t} catch {\n\t\t\t\treturn current;\n\t\t\t}\n\t\t}\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return null;\n\t\tcurrent = parent;\n\t}\n}\n\nexport function resolveProject(cwd: string, override?: string | null): string | null {\n\tif (override != null) {\n\t\tconst trimmed = override.trim();\n\t\treturn trimmed || null;\n\t}\n\tconst gitAnchor = findGitAnchor(cwd);\n\tif (gitAnchor) {\n\t\treturn basename(gitAnchor);\n\t}\n\treturn basename(resolve(cwd));\n}\n","/**\n * Drizzle ORM schema for the codemem SQLite database.\n */\n\nimport {\n\tindex,\n\tinteger,\n\tprimaryKey,\n\treal,\n\tsqliteTable,\n\ttext,\n\tuniqueIndex,\n} from \"drizzle-orm/sqlite-core\";\n\nexport const sessions = sqliteTable(\"sessions\", {\n\tid: integer(\"id\").primaryKey(),\n\tstarted_at: text(\"started_at\").notNull(),\n\tended_at: text(\"ended_at\"),\n\tcwd: text(\"cwd\"),\n\tproject: text(\"project\"),\n\tgit_remote: text(\"git_remote\"),\n\tgit_branch: text(\"git_branch\"),\n\tuser: text(\"user\"),\n\ttool_version: text(\"tool_version\"),\n\tmetadata_json: text(\"metadata_json\"),\n\timport_key: text(\"import_key\"),\n});\n\nexport type Session = typeof sessions.$inferSelect;\nexport type NewSession = typeof sessions.$inferInsert;\n\nexport const artifacts = sqliteTable(\n\t\"artifacts\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\")\n\t\t\t.notNull()\n\t\t\t.references(() => sessions.id, { onDelete: \"cascade\" }),\n\t\tkind: text(\"kind\").notNull(),\n\t\tpath: text(\"path\"),\n\t\tcontent_text: text(\"content_text\"),\n\t\tcontent_hash: text(\"content_hash\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t},\n\t(table) => [index(\"idx_artifacts_session_kind\").on(table.session_id, table.kind)],\n);\n\nexport type Artifact = typeof artifacts.$inferSelect;\nexport type NewArtifact = typeof artifacts.$inferInsert;\n\nexport const memoryItems = sqliteTable(\n\t\"memory_items\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\")\n\t\t\t.notNull()\n\t\t\t.references(() => sessions.id, { onDelete: \"cascade\" }),\n\t\tkind: text(\"kind\").notNull(),\n\t\ttitle: text(\"title\").notNull(),\n\t\tsubtitle: text(\"subtitle\"),\n\t\tbody_text: text(\"body_text\").notNull(),\n\t\tconfidence: real(\"confidence\").default(0.5),\n\t\ttags_text: text(\"tags_text\").default(\"\"),\n\t\tactive: integer(\"active\").default(1),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\tactor_id: text(\"actor_id\"),\n\t\tactor_display_name: text(\"actor_display_name\"),\n\t\tvisibility: text(\"visibility\"),\n\t\tworkspace_id: text(\"workspace_id\"),\n\t\tworkspace_kind: text(\"workspace_kind\"),\n\t\torigin_device_id: text(\"origin_device_id\"),\n\t\torigin_source: text(\"origin_source\"),\n\t\ttrust_state: text(\"trust_state\"),\n\t\tfacts: text(\"facts\"),\n\t\tnarrative: text(\"narrative\"),\n\t\tconcepts: text(\"concepts\"),\n\t\tfiles_read: text(\"files_read\"),\n\t\tfiles_modified: text(\"files_modified\"),\n\t\tuser_prompt_id: integer(\"user_prompt_id\"),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tdeleted_at: text(\"deleted_at\"),\n\t\trev: integer(\"rev\").default(0),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_memory_items_active_created\").on(table.active, table.created_at),\n\t\tindex(\"idx_memory_items_session\").on(table.session_id),\n\t],\n);\n\nexport type MemoryItem = typeof memoryItems.$inferSelect;\nexport type NewMemoryItem = typeof memoryItems.$inferInsert;\n\nexport const usageEvents = sqliteTable(\n\t\"usage_events\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"set null\",\n\t\t}),\n\t\tevent: text(\"event\").notNull(),\n\t\ttokens_read: integer(\"tokens_read\").default(0),\n\t\ttokens_written: integer(\"tokens_written\").default(0),\n\t\ttokens_saved: integer(\"tokens_saved\").default(0),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_usage_events_event_created\").on(table.event, table.created_at),\n\t\tindex(\"idx_usage_events_session\").on(table.session_id),\n\t],\n);\n\nexport type UsageEvent = typeof usageEvents.$inferSelect;\nexport type NewUsageEvent = typeof usageEvents.$inferInsert;\n\nexport const rawEvents = sqliteTable(\n\t\"raw_events\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tevent_id: text(\"event_id\"),\n\t\tevent_seq: integer(\"event_seq\").notNull(),\n\t\tevent_type: text(\"event_type\").notNull(),\n\t\tts_wall_ms: integer(\"ts_wall_ms\"),\n\t\tts_mono_ms: real(\"ts_mono_ms\"),\n\t\tpayload_json: text(\"payload_json\").notNull(),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tuniqueIndex(\"idx_raw_events_source_stream_seq\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.event_seq,\n\t\t),\n\t\tuniqueIndex(\"idx_raw_events_source_stream_event_id\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.event_id,\n\t\t),\n\t\tindex(\"idx_raw_events_session_seq\").on(table.opencode_session_id, table.event_seq),\n\t\tindex(\"idx_raw_events_created\").on(table.created_at),\n\t],\n);\n\nexport type RawEvent = typeof rawEvents.$inferSelect;\nexport type NewRawEvent = typeof rawEvents.$inferInsert;\n\nexport const rawEventSessions = sqliteTable(\n\t\"raw_event_sessions\",\n\t{\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tcwd: text(\"cwd\"),\n\t\tproject: text(\"project\"),\n\t\tstarted_at: text(\"started_at\"),\n\t\tlast_seen_ts_wall_ms: integer(\"last_seen_ts_wall_ms\"),\n\t\tlast_received_event_seq: integer(\"last_received_event_seq\").notNull().default(-1),\n\t\tlast_flushed_event_seq: integer(\"last_flushed_event_seq\").notNull().default(-1),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => [primaryKey({ columns: [table.source, table.stream_id] })],\n);\n\nexport type RawEventSession = typeof rawEventSessions.$inferSelect;\nexport type NewRawEventSession = typeof rawEventSessions.$inferInsert;\n\nexport const opencodeSessions = sqliteTable(\n\t\"opencode_sessions\",\n\t{\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tprimaryKey({ columns: [table.source, table.stream_id] }),\n\t\tindex(\"idx_opencode_sessions_session\").on(table.session_id),\n\t],\n);\n\nexport type OpencodeSession = typeof opencodeSessions.$inferSelect;\nexport type NewOpencodeSession = typeof opencodeSessions.$inferInsert;\n\nexport const rawEventFlushBatches = sqliteTable(\n\t\"raw_event_flush_batches\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsource: text(\"source\").notNull().default(\"opencode\"),\n\t\tstream_id: text(\"stream_id\").notNull().default(\"\"),\n\t\topencode_session_id: text(\"opencode_session_id\").notNull(),\n\t\tstart_event_seq: integer(\"start_event_seq\").notNull(),\n\t\tend_event_seq: integer(\"end_event_seq\").notNull(),\n\t\textractor_version: text(\"extractor_version\").notNull(),\n\t\tstatus: text(\"status\").notNull(),\n\t\terror_message: text(\"error_message\"),\n\t\terror_type: text(\"error_type\"),\n\t\tobserver_provider: text(\"observer_provider\"),\n\t\tobserver_model: text(\"observer_model\"),\n\t\tobserver_runtime: text(\"observer_runtime\"),\n\t\tattempt_count: integer(\"attempt_count\").notNull().default(0),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => [\n\t\tuniqueIndex(\"idx_flush_batches_source_stream_seq_ver\").on(\n\t\t\ttable.source,\n\t\t\ttable.stream_id,\n\t\t\ttable.start_event_seq,\n\t\t\ttable.end_event_seq,\n\t\t\ttable.extractor_version,\n\t\t),\n\t\tindex(\"idx_flush_batches_session_created\").on(table.opencode_session_id, table.created_at),\n\t\tindex(\"idx_flush_batches_status_updated\").on(table.status, table.updated_at),\n\t],\n);\n\nexport type RawEventFlushBatch = typeof rawEventFlushBatches.$inferSelect;\nexport type NewRawEventFlushBatch = typeof rawEventFlushBatches.$inferInsert;\n\nexport const userPrompts = sqliteTable(\n\t\"user_prompts\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tproject: text(\"project\"),\n\t\tprompt_text: text(\"prompt_text\").notNull(),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tcreated_at_epoch: integer(\"created_at_epoch\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_user_prompts_session\").on(table.session_id),\n\t\tindex(\"idx_user_prompts_project\").on(table.project),\n\t\tindex(\"idx_user_prompts_epoch\").on(table.created_at_epoch),\n\t],\n);\n\nexport type UserPrompt = typeof userPrompts.$inferSelect;\nexport type NewUserPrompt = typeof userPrompts.$inferInsert;\n\nexport const sessionSummaries = sqliteTable(\n\t\"session_summaries\",\n\t{\n\t\tid: integer(\"id\").primaryKey(),\n\t\tsession_id: integer(\"session_id\").references(() => sessions.id, {\n\t\t\tonDelete: \"cascade\",\n\t\t}),\n\t\tproject: text(\"project\"),\n\t\trequest: text(\"request\"),\n\t\tinvestigated: text(\"investigated\"),\n\t\tlearned: text(\"learned\"),\n\t\tcompleted: text(\"completed\"),\n\t\tnext_steps: text(\"next_steps\"),\n\t\tnotes: text(\"notes\"),\n\t\tfiles_read: text(\"files_read\"),\n\t\tfiles_edited: text(\"files_edited\"),\n\t\tprompt_number: integer(\"prompt_number\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tcreated_at_epoch: integer(\"created_at_epoch\").notNull(),\n\t\tmetadata_json: text(\"metadata_json\"),\n\t\timport_key: text(\"import_key\"),\n\t},\n\t(table) => [\n\t\tindex(\"idx_session_summaries_session\").on(table.session_id),\n\t\tindex(\"idx_session_summaries_project\").on(table.project),\n\t\tindex(\"idx_session_summaries_epoch\").on(table.created_at_epoch),\n\t],\n);\n\nexport type SessionSummary = typeof sessionSummaries.$inferSelect;\nexport type NewSessionSummary = typeof sessionSummaries.$inferInsert;\n\nexport const replicationOps = sqliteTable(\n\t\"replication_ops\",\n\t{\n\t\top_id: text(\"op_id\").primaryKey(),\n\t\tentity_type: text(\"entity_type\").notNull(),\n\t\tentity_id: text(\"entity_id\").notNull(),\n\t\top_type: text(\"op_type\").notNull(),\n\t\tpayload_json: text(\"payload_json\"),\n\t\tclock_rev: integer(\"clock_rev\").notNull(),\n\t\tclock_updated_at: text(\"clock_updated_at\").notNull(),\n\t\tclock_device_id: text(\"clock_device_id\").notNull(),\n\t\tdevice_id: text(\"device_id\").notNull(),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t},\n\t(table) => [\n\t\tindex(\"idx_replication_ops_created\").on(table.created_at, table.op_id),\n\t\tindex(\"idx_replication_ops_entity\").on(table.entity_type, table.entity_id),\n\t],\n);\n\nexport type ReplicationOp = typeof replicationOps.$inferSelect;\nexport type NewReplicationOp = typeof replicationOps.$inferInsert;\n\nexport const replicationCursors = sqliteTable(\"replication_cursors\", {\n\tpeer_device_id: text(\"peer_device_id\").primaryKey(),\n\tlast_applied_cursor: text(\"last_applied_cursor\"),\n\tlast_acked_cursor: text(\"last_acked_cursor\"),\n\tupdated_at: text(\"updated_at\").notNull(),\n});\n\nexport type ReplicationCursor = typeof replicationCursors.$inferSelect;\nexport type NewReplicationCursor = typeof replicationCursors.$inferInsert;\n\nexport const syncPeers = sqliteTable(\"sync_peers\", {\n\tpeer_device_id: text(\"peer_device_id\").primaryKey(),\n\tname: text(\"name\"),\n\tpinned_fingerprint: text(\"pinned_fingerprint\"),\n\tpublic_key: text(\"public_key\"),\n\taddresses_json: text(\"addresses_json\"),\n\tclaimed_local_actor: integer(\"claimed_local_actor\").notNull().default(0),\n\tactor_id: text(\"actor_id\"),\n\tprojects_include_json: text(\"projects_include_json\"),\n\tprojects_exclude_json: text(\"projects_exclude_json\"),\n\tcreated_at: text(\"created_at\").notNull(),\n\tlast_seen_at: text(\"last_seen_at\"),\n\tlast_sync_at: text(\"last_sync_at\"),\n\tlast_error: text(\"last_error\"),\n});\n\nexport type SyncPeer = typeof syncPeers.$inferSelect;\nexport type NewSyncPeer = typeof syncPeers.$inferInsert;\n\nexport const syncNonces = sqliteTable(\"sync_nonces\", {\n\tnonce: text(\"nonce\").primaryKey(),\n\tdevice_id: text(\"device_id\").notNull(),\n\tcreated_at: text(\"created_at\").notNull(),\n});\n\nexport type SyncNonce = typeof syncNonces.$inferSelect;\nexport type NewSyncNonce = typeof syncNonces.$inferInsert;\n\nexport const syncDevice = sqliteTable(\"sync_device\", {\n\tdevice_id: text(\"device_id\").primaryKey(),\n\tpublic_key: text(\"public_key\").notNull(),\n\tfingerprint: text(\"fingerprint\").notNull(),\n\tcreated_at: text(\"created_at\").notNull(),\n});\n\nexport type SyncDevice = typeof syncDevice.$inferSelect;\nexport type NewSyncDevice = typeof syncDevice.$inferInsert;\n\nexport const syncAttempts = sqliteTable(\n\t\"sync_attempts\",\n\t{\n\t\tid: integer(\"id\").primaryKey({ autoIncrement: true }),\n\t\tpeer_device_id: text(\"peer_device_id\").notNull(),\n\t\tstarted_at: text(\"started_at\").notNull(),\n\t\tfinished_at: text(\"finished_at\"),\n\t\tok: integer(\"ok\").notNull(),\n\t\tops_in: integer(\"ops_in\").notNull(),\n\t\tops_out: integer(\"ops_out\").notNull(),\n\t\terror: text(\"error\"),\n\t},\n\t(table) => [index(\"idx_sync_attempts_peer_started\").on(table.peer_device_id, table.started_at)],\n);\n\nexport type SyncAttempt = typeof syncAttempts.$inferSelect;\nexport type NewSyncAttempt = typeof syncAttempts.$inferInsert;\n\nexport const syncDaemonState = sqliteTable(\"sync_daemon_state\", {\n\tid: integer(\"id\").primaryKey(),\n\tlast_error: text(\"last_error\"),\n\tlast_traceback: text(\"last_traceback\"),\n\tlast_error_at: text(\"last_error_at\"),\n\tlast_ok_at: text(\"last_ok_at\"),\n});\n\nexport type SyncDaemonState = typeof syncDaemonState.$inferSelect;\nexport type NewSyncDaemonState = typeof syncDaemonState.$inferInsert;\n\nexport const rawEventIngestSamples = sqliteTable(\"raw_event_ingest_samples\", {\n\tid: integer(\"id\").primaryKey({ autoIncrement: true }),\n\tcreated_at: text(\"created_at\").notNull(),\n\tinserted_events: integer(\"inserted_events\").notNull().default(0),\n\tskipped_invalid: integer(\"skipped_invalid\").notNull().default(0),\n\tskipped_duplicate: integer(\"skipped_duplicate\").notNull().default(0),\n\tskipped_conflict: integer(\"skipped_conflict\").notNull().default(0),\n});\n\nexport type RawEventIngestSample = typeof rawEventIngestSamples.$inferSelect;\nexport type NewRawEventIngestSample = typeof rawEventIngestSamples.$inferInsert;\n\nexport const rawEventIngestStats = sqliteTable(\"raw_event_ingest_stats\", {\n\tid: integer(\"id\").primaryKey(),\n\tinserted_events: integer(\"inserted_events\").notNull().default(0),\n\tskipped_events: integer(\"skipped_events\").notNull().default(0),\n\tskipped_invalid: integer(\"skipped_invalid\").notNull().default(0),\n\tskipped_duplicate: integer(\"skipped_duplicate\").notNull().default(0),\n\tskipped_conflict: integer(\"skipped_conflict\").notNull().default(0),\n\tupdated_at: text(\"updated_at\").notNull(),\n});\n\nexport type RawEventIngestStat = typeof rawEventIngestStats.$inferSelect;\nexport type NewRawEventIngestStat = typeof rawEventIngestStats.$inferInsert;\n\nexport const actors = sqliteTable(\n\t\"actors\",\n\t{\n\t\tactor_id: text(\"actor_id\").primaryKey(),\n\t\tdisplay_name: text(\"display_name\").notNull(),\n\t\tis_local: integer(\"is_local\").notNull().default(0),\n\t\tstatus: text(\"status\").notNull().default(\"active\"),\n\t\tmerged_into_actor_id: text(\"merged_into_actor_id\"),\n\t\tcreated_at: text(\"created_at\").notNull(),\n\t\tupdated_at: text(\"updated_at\").notNull(),\n\t},\n\t(table) => ({\n\t\tisLocalIdx: index(\"idx_actors_is_local\").on(table.is_local),\n\t\tstatusIdx: index(\"idx_actors_status\").on(table.status),\n\t}),\n);\n\nexport type Actor = typeof actors.$inferSelect;\nexport type NewActor = typeof actors.$inferInsert;\n\nexport const schema = {\n\tsessions,\n\tartifacts,\n\tmemoryItems,\n\tusageEvents,\n\trawEvents,\n\trawEventSessions,\n\topencodeSessions,\n\trawEventFlushBatches,\n\tuserPrompts,\n\tsessionSummaries,\n\treplicationOps,\n\treplicationCursors,\n\tsyncPeers,\n\tsyncNonces,\n\tsyncDevice,\n\tsyncAttempts,\n\tsyncDaemonState,\n\trawEventIngestSamples,\n\trawEventIngestStats,\n\tactors,\n};\n","import { statSync } from \"node:fs\";\nimport { and, eq, gt, gte, inArray, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { assertSchemaReady, connect, type Database, resolveDbPath } from \"./db.js\";\nimport { isLowSignalObservation } from \"./ingest-filters.js\";\nimport { projectClause } from \"./project.js\";\nimport * as schema from \"./schema.js\";\n\nexport interface RawEventStatusItem {\n\tsource: string;\n\tstream_id: string;\n\topencode_session_id: string | null;\n\tcwd: string | null;\n\tproject: string | null;\n\tstarted_at: string | null;\n\tlast_seen_ts_wall_ms: number | null;\n\tlast_received_event_seq: number;\n\tlast_flushed_event_seq: number;\n\tupdated_at: string;\n\tsession_stream_id: string;\n\tsession_id: string;\n}\n\nexport interface RawEventStatusResult {\n\titems: RawEventStatusItem[];\n\ttotals: { pending: number; sessions: number };\n\tingest: { available: true; mode: \"stream_queue\"; max_body_bytes: number };\n}\n\nfunction withDb<T>(dbPath: string | undefined, fn: (db: Database, resolvedPath: string) => T): T {\n\tconst resolvedPath = resolveDbPath(dbPath);\n\tconst db = connect(resolvedPath);\n\ttry {\n\t\tassertSchemaReady(db);\n\t\treturn fn(db, resolvedPath);\n\t} finally {\n\t\tdb.close();\n\t}\n}\n\nexport function initDatabase(dbPath?: string): { path: string; sizeBytes: number } {\n\treturn withDb(dbPath, (_db, resolvedPath) => {\n\t\tconst stats = statSync(resolvedPath);\n\t\treturn { path: resolvedPath, sizeBytes: stats.size };\n\t});\n}\n\nexport function vacuumDatabase(dbPath?: string): { path: string; sizeBytes: number } {\n\treturn withDb(dbPath, (db, resolvedPath) => {\n\t\tdb.exec(\"VACUUM\");\n\t\tconst stats = statSync(resolvedPath);\n\t\treturn { path: resolvedPath, sizeBytes: stats.size };\n\t});\n}\n\nexport function getRawEventStatus(dbPath?: string, limit = 25): RawEventStatusResult {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst maxEvents = d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventSessions.source,\n\t\t\t\tstream_id: schema.rawEventSessions.stream_id,\n\t\t\t\topencode_session_id: schema.rawEventSessions.opencode_session_id,\n\t\t\t\tcwd: schema.rawEventSessions.cwd,\n\t\t\t\tproject: schema.rawEventSessions.project,\n\t\t\t\tstarted_at: schema.rawEventSessions.started_at,\n\t\t\t\tlast_seen_ts_wall_ms: schema.rawEventSessions.last_seen_ts_wall_ms,\n\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\tlast_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq,\n\t\t\t\tupdated_at: schema.rawEventSessions.updated_at,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(gt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq))\n\t\t\t.orderBy(sql`${schema.rawEventSessions.updated_at} DESC`)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\tconst items = rows.map((row) => {\n\t\t\tconst streamId = String(row.stream_id ?? row.opencode_session_id ?? \"\");\n\t\t\treturn {\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstream_id: streamId,\n\t\t\t\topencode_session_id:\n\t\t\t\t\trow.opencode_session_id == null ? null : String(row.opencode_session_id),\n\t\t\t\tcwd: row.cwd == null ? null : String(row.cwd),\n\t\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\t\tstarted_at: row.started_at == null ? null : String(row.started_at),\n\t\t\t\tlast_seen_ts_wall_ms:\n\t\t\t\t\trow.last_seen_ts_wall_ms == null ? null : Number(row.last_seen_ts_wall_ms),\n\t\t\t\tlast_received_event_seq: Number(row.last_received_event_seq ?? -1),\n\t\t\t\tlast_flushed_event_seq: Number(row.last_flushed_event_seq ?? -1),\n\t\t\t\tupdated_at: String(row.updated_at ?? \"\"),\n\t\t\t\tsession_stream_id: streamId,\n\t\t\t\tsession_id: streamId,\n\t\t\t};\n\t\t});\n\n\t\tconst totalsRow = d\n\t\t\t.select({\n\t\t\t\tsessions: sql<number>`COUNT(1)`,\n\t\t\t\tpending: sql<\n\t\t\t\t\tnumber | null\n\t\t\t\t>`SUM(${maxEvents.max_seq} - ${schema.rawEventSessions.last_flushed_event_seq})`,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(gt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq))\n\t\t\t.get();\n\n\t\treturn {\n\t\t\titems,\n\t\t\ttotals: {\n\t\t\t\tpending: Number(totalsRow?.pending ?? 0),\n\t\t\t\tsessions: Number(totalsRow?.sessions ?? 0),\n\t\t\t},\n\t\t\tingest: {\n\t\t\t\tavailable: true,\n\t\t\t\tmode: \"stream_queue\",\n\t\t\t\tmax_body_bytes: 2_000_000,\n\t\t\t},\n\t\t};\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Reliability metrics\n// ---------------------------------------------------------------------------\n\nexport interface ReliabilityMetrics {\n\tcounts: {\n\t\tinserted_events: number;\n\t\tdropped_events: number;\n\t\tstarted_batches: number;\n\t\trunning_batches: number;\n\t\tcompleted_batches: number;\n\t\terrored_batches: number;\n\t\tterminal_batches: number;\n\t\tsessions_with_events: number;\n\t\tsessions_with_started_at: number;\n\t\tretry_depth_max: number;\n\t};\n\trates: {\n\t\tflush_success_rate: number;\n\t\tdropped_event_rate: number;\n\t\tsession_boundary_accuracy: number;\n\t};\n\twindow_hours: number | null;\n}\n\nexport function getReliabilityMetrics(\n\tdbPath?: string,\n\twindowHours?: number | null,\n): ReliabilityMetrics {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst cutoffIso =\n\t\t\twindowHours != null ? new Date(Date.now() - windowHours * 3600 * 1000).toISOString() : null;\n\n\t\t// Batch counts\n\t\tconst batchRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tstarted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('started', 'pending') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\trunning: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('running', 'claimed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\tcompleted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} = 'completed' THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\terrored: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('error', 'failed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(gte(schema.rawEventFlushBatches.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tstarted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('started', 'pending') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\trunning: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('running', 'claimed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\tcompleted: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} = 'completed' THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t\terrored: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventFlushBatches.status} IN ('error', 'failed') THEN 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\n\t\tconst startedBatches = Number(batchRow?.started ?? 0);\n\t\tconst runningBatches = Number(batchRow?.running ?? 0);\n\t\tconst completedBatches = Number(batchRow?.completed ?? 0);\n\t\tconst erroredBatches = Number(batchRow?.errored ?? 0);\n\t\tconst terminalBatches = completedBatches + erroredBatches;\n\t\tconst flushSuccessRate = terminalBatches > 0 ? completedBatches / terminalBatches : 1.0;\n\n\t\t// Event counts from raw_event_sessions\n\t\t// Sequences are 0-based indexes, so +1 converts to counts.\n\t\tconst eventRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\ttotal_received: sql<number>`COALESCE(SUM(${schema.rawEventSessions.last_received_event_seq} + 1), 0)`,\n\t\t\t\t\t\t\ttotal_flushed: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventSessions.last_flushed_event_seq} >= 0 THEN ${schema.rawEventSessions.last_flushed_event_seq} + 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t\t\t.where(gte(schema.rawEventSessions.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\ttotal_received: sql<number>`COALESCE(SUM(${schema.rawEventSessions.last_received_event_seq} + 1), 0)`,\n\t\t\t\t\t\t\ttotal_flushed: sql<number>`COALESCE(SUM(CASE WHEN ${schema.rawEventSessions.last_flushed_event_seq} >= 0 THEN ${schema.rawEventSessions.last_flushed_event_seq} + 1 ELSE 0 END), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\n\t\t// In-flight events: sum of (end_event_seq - start_event_seq + 1) for active batches\n\t\tconst inFlightRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tin_flight: sql<number>`COALESCE(SUM(${schema.rawEventFlushBatches.end_event_seq} - ${schema.rawEventFlushBatches.start_event_seq} + 1), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tand(\n\t\t\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\n\t\t\t\t\t\t\t\t\t\"started\",\n\t\t\t\t\t\t\t\t\t\"pending\",\n\t\t\t\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\t\t\t\"claimed\",\n\t\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t\t\tgte(schema.rawEventFlushBatches.updated_at, cutoffIso),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tin_flight: sql<number>`COALESCE(SUM(${schema.rawEventFlushBatches.end_event_seq} - ${schema.rawEventFlushBatches.start_event_seq} + 1), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(\n\t\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\n\t\t\t\t\t\t\t\t\"started\",\n\t\t\t\t\t\t\t\t\"pending\",\n\t\t\t\t\t\t\t\t\"running\",\n\t\t\t\t\t\t\t\t\"claimed\",\n\t\t\t\t\t\t\t]),\n\t\t\t\t\t\t)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\t\tconst inFlightEvents = Number(inFlightRow?.in_flight ?? 0);\n\n\t\tconst insertedEvents = Number(eventRow?.total_flushed ?? 0);\n\t\tconst droppedEvents = Math.max(\n\t\t\t0,\n\t\t\tNumber(eventRow?.total_received ?? 0) - Number(eventRow?.total_flushed ?? 0) - inFlightEvents,\n\t\t);\n\t\tconst droppedDenom = insertedEvents + droppedEvents;\n\t\tconst droppedEventRate = droppedDenom > 0 ? droppedEvents / droppedDenom : 0.0;\n\n\t\t// Session boundary accuracy\n\t\tconst hasEvents = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.selectDistinct({\n\t\t\t\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t\t\t.where(gte(schema.rawEvents.created_at, cutoffIso))\n\t\t\t\t: d\n\t\t\t\t\t\t.selectDistinct({\n\t\t\t\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEvents)\n\t\t).as(\"has_events\");\n\n\t\tconst boundaryRow = d\n\t\t\t.select({\n\t\t\t\tsessions_with_events: sql<number>`COUNT(1)`,\n\t\t\t\tsessions_with_started_at: sql<number>`COALESCE(SUM(CASE WHEN COALESCE(${schema.rawEventSessions.started_at}, '') != '' THEN 1 ELSE 0 END), 0)`,\n\t\t\t})\n\t\t\t.from(hasEvents)\n\t\t\t.leftJoin(\n\t\t\t\tschema.rawEventSessions,\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventSessions.source, hasEvents.source),\n\t\t\t\t\teq(schema.rawEventSessions.stream_id, hasEvents.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.get() as Record<string, number> | undefined;\n\n\t\tconst sessionsWithEvents = Number(boundaryRow?.sessions_with_events ?? 0);\n\t\tconst sessionsWithStartedAt = Number(boundaryRow?.sessions_with_started_at ?? 0);\n\t\tconst sessionBoundaryAccuracy =\n\t\t\tsessionsWithEvents > 0 ? sessionsWithStartedAt / sessionsWithEvents : 1.0;\n\n\t\tconst retryDepthRow = (\n\t\t\tcutoffIso\n\t\t\t\t? d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tretry_depth_max: sql<number>`COALESCE(MAX(${schema.rawEventFlushBatches.attempt_count}), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.where(gte(schema.rawEventFlushBatches.updated_at, cutoffIso))\n\t\t\t\t\t\t.get()\n\t\t\t\t: d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tretry_depth_max: sql<number>`COALESCE(MAX(${schema.rawEventFlushBatches.attempt_count}), 0)`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t\t\t.get()\n\t\t) as Record<string, number> | undefined;\n\t\tconst retryDepthMax = Math.max(0, Number(retryDepthRow?.retry_depth_max ?? 0) - 1);\n\n\t\treturn {\n\t\t\tcounts: {\n\t\t\t\tinserted_events: insertedEvents,\n\t\t\t\tdropped_events: droppedEvents,\n\t\t\t\tstarted_batches: startedBatches,\n\t\t\t\trunning_batches: runningBatches,\n\t\t\t\tcompleted_batches: completedBatches,\n\t\t\t\terrored_batches: erroredBatches,\n\t\t\t\tterminal_batches: terminalBatches,\n\t\t\t\tsessions_with_events: sessionsWithEvents,\n\t\t\t\tsessions_with_started_at: sessionsWithStartedAt,\n\t\t\t\tretry_depth_max: retryDepthMax,\n\t\t\t},\n\t\t\trates: {\n\t\t\t\tflush_success_rate: flushSuccessRate,\n\t\t\t\tdropped_event_rate: droppedEventRate,\n\t\t\t\tsession_boundary_accuracy: sessionBoundaryAccuracy,\n\t\t\t},\n\t\t\twindow_hours: windowHours ?? null,\n\t\t};\n\t});\n}\n\nexport interface GateResult {\n\tpassed: boolean;\n\tfailures: string[];\n\tmetrics: ReliabilityMetrics;\n}\n\nexport function rawEventsGate(\n\tdbPath?: string,\n\topts?: {\n\t\tminFlushSuccessRate?: number;\n\t\tmaxDroppedEventRate?: number;\n\t\tminSessionBoundaryAccuracy?: number;\n\t\twindowHours?: number;\n\t},\n): GateResult {\n\tconst minFlushSuccessRate = opts?.minFlushSuccessRate ?? 0.95;\n\tconst maxDroppedEventRate = opts?.maxDroppedEventRate ?? 0.05;\n\tconst minSessionBoundaryAccuracy = opts?.minSessionBoundaryAccuracy ?? 0.9;\n\tconst windowHours = opts?.windowHours ?? 24;\n\n\tconst metrics = getReliabilityMetrics(dbPath, windowHours);\n\tconst failures: string[] = [];\n\n\tif (metrics.rates.flush_success_rate < minFlushSuccessRate) {\n\t\tfailures.push(\n\t\t\t`flush_success_rate=${metrics.rates.flush_success_rate.toFixed(4)} < min ${minFlushSuccessRate.toFixed(4)}`,\n\t\t);\n\t}\n\tif (metrics.rates.dropped_event_rate > maxDroppedEventRate) {\n\t\tfailures.push(\n\t\t\t`dropped_event_rate=${metrics.rates.dropped_event_rate.toFixed(4)} > max ${maxDroppedEventRate.toFixed(4)}`,\n\t\t);\n\t}\n\tif (metrics.rates.session_boundary_accuracy < minSessionBoundaryAccuracy) {\n\t\tfailures.push(\n\t\t\t`session_boundary_accuracy=${metrics.rates.session_boundary_accuracy.toFixed(4)} < min ${minSessionBoundaryAccuracy.toFixed(4)}`,\n\t\t);\n\t}\n\n\treturn { passed: failures.length === 0, failures, metrics };\n}\n\n// ---------------------------------------------------------------------------\n// Retry\n// ---------------------------------------------------------------------------\n\nexport function retryRawEventFailures(dbPath?: string, limit = 25): { retried: number } {\n\treturn withDb(dbPath, (db) => {\n\t\tconst d = drizzle(db, { schema });\n\t\tconst now = new Date().toISOString();\n\t\treturn db.transaction(() => {\n\t\t\tconst candidateIds = d\n\t\t\t\t.select({ id: schema.rawEventFlushBatches.id })\n\t\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t\t.where(inArray(schema.rawEventFlushBatches.status, [\"failed\", \"error\"]))\n\t\t\t\t.orderBy(schema.rawEventFlushBatches.updated_at)\n\t\t\t\t.limit(limit)\n\t\t\t\t.all()\n\t\t\t\t.map((row) => Number(row.id));\n\n\t\t\tif (candidateIds.length === 0) return { retried: 0 };\n\n\t\t\tconst result = d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({\n\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\terror_message: null,\n\t\t\t\t\terror_type: null,\n\t\t\t\t\tobserver_provider: null,\n\t\t\t\t\tobserver_model: null,\n\t\t\t\t\tobserver_runtime: null,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.id, candidateIds),\n\t\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\"failed\", \"error\"]),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.run();\n\n\t\t\treturn { retried: Number(result.changes ?? 0) };\n\t\t})();\n\t});\n}\n\nexport interface BackfillTagsTextOptions {\n\tlimit?: number | null;\n\tsince?: string | null;\n\tproject?: string | null;\n\tactiveOnly?: boolean;\n\tdryRun?: boolean;\n\tmemoryIds?: number[] | null;\n}\n\nexport interface BackfillTagsTextResult {\n\tchecked: number;\n\tupdated: number;\n\tskipped: number;\n}\n\nfunction normalizeTag(value: string): string {\n\tlet normalized = value.trim().toLowerCase();\n\tif (!normalized) return \"\";\n\tnormalized = normalized.replace(/[^a-z0-9_]+/g, \"-\");\n\tnormalized = normalized.replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n\tif (!normalized) return \"\";\n\tif (normalized.length > 40) normalized = normalized.slice(0, 40).replace(/-+$/g, \"\");\n\treturn normalized;\n}\n\nfunction fileTags(pathValue: string): string[] {\n\tconst raw = pathValue.trim();\n\tif (!raw) return [];\n\tconst parts = raw.split(/[\\\\/]+/).filter((part) => part && part !== \".\" && part !== \"..\");\n\tif (parts.length === 0) return [];\n\tconst tags: string[] = [];\n\tconst basename = normalizeTag(parts[parts.length - 1] ?? \"\");\n\tif (basename) tags.push(basename);\n\tif (parts.length >= 2) {\n\t\tconst parent = normalizeTag(parts[parts.length - 2] ?? \"\");\n\t\tif (parent) tags.push(parent);\n\t}\n\tif (parts.length >= 3) {\n\t\tconst top = normalizeTag(parts[0] ?? \"\");\n\t\tif (top) tags.push(top);\n\t}\n\treturn tags;\n}\n\nfunction parseJsonStringList(value: string | null): string[] {\n\tif (!value) return [];\n\ttry {\n\t\tconst parsed = JSON.parse(value) as unknown;\n\t\tif (!Array.isArray(parsed)) return [];\n\t\treturn parsed\n\t\t\t.map((item) => (typeof item === \"string\" ? item.trim() : \"\"))\n\t\t\t.filter((item) => item.length > 0);\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction deriveTags(input: {\n\tkind: string;\n\ttitle: string;\n\tconcepts: string[];\n\tfilesRead: string[];\n\tfilesModified: string[];\n}): string[] {\n\tconst tags: string[] = [];\n\tconst kindTag = normalizeTag(input.kind);\n\tif (kindTag) tags.push(kindTag);\n\n\tfor (const concept of input.concepts) {\n\t\tconst tag = normalizeTag(concept);\n\t\tif (tag) tags.push(tag);\n\t}\n\n\tfor (const filePath of [...input.filesRead, ...input.filesModified]) {\n\t\ttags.push(...fileTags(filePath));\n\t}\n\n\tif (tags.length === 0 && input.title.trim()) {\n\t\tconst tokens = input.title.toLowerCase().match(/[a-z0-9_]+/g) ?? [];\n\t\tfor (const token of tokens) {\n\t\t\tconst tag = normalizeTag(token);\n\t\t\tif (tag) tags.push(tag);\n\t\t}\n\t}\n\n\tconst deduped: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const tag of tags) {\n\t\tif (seen.has(tag)) continue;\n\t\tseen.add(tag);\n\t\tdeduped.push(tag);\n\t\tif (deduped.length >= 20) break;\n\t}\n\treturn deduped;\n}\n\n/**\n * Populate memory_items.tags_text for rows where it is empty.\n * Port of Python's backfill_tags_text() maintenance helper.\n */\nexport function backfillTagsText(\n\tdb: Database,\n\topts: BackfillTagsTextOptions = {},\n): BackfillTagsTextResult {\n\tconst { limit, since, project, activeOnly = true, dryRun = false, memoryIds } = opts;\n\n\tconst params: unknown[] = [];\n\tconst whereClauses = [\"(memory_items.tags_text IS NULL OR TRIM(memory_items.tags_text) = '')\"];\n\n\tif (activeOnly) whereClauses.push(\"memory_items.active = 1\");\n\tif (since) {\n\t\twhereClauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(since);\n\t}\n\n\tlet joinSessions = false;\n\tif (project) {\n\t\tconst pc = projectClause(project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t\tjoinSessions = true;\n\t\t}\n\t}\n\n\tif (memoryIds && memoryIds.length > 0) {\n\t\tconst placeholders = memoryIds.map(() => \"?\").join(\",\");\n\t\twhereClauses.push(`memory_items.id IN (${placeholders})`);\n\t\tparams.push(...memoryIds.map((id) => Number(id)));\n\t}\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\tconst limitClause = limit != null && limit > 0 ? \"LIMIT ?\" : \"\";\n\tif (limit != null && limit > 0) params.push(limit);\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.id, memory_items.kind, memory_items.title,\n\t\t\t memory_items.concepts, memory_items.files_read, memory_items.files_modified\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${where}\n\t\t\t ORDER BY memory_items.created_at ASC\n\t\t\t ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{\n\t\tid: number;\n\t\tkind: string | null;\n\t\ttitle: string | null;\n\t\tconcepts: string | null;\n\t\tfiles_read: string | null;\n\t\tfiles_modified: string | null;\n\t}>;\n\n\tlet checked = 0;\n\tlet updated = 0;\n\tlet skipped = 0;\n\tconst now = new Date().toISOString();\n\tconst updateStmt = db.prepare(\n\t\t\"UPDATE memory_items SET tags_text = ?, updated_at = ? WHERE id = ?\",\n\t);\n\tconst updates: Array<{ id: number; tagsText: string }> = [];\n\n\tfor (const row of rows) {\n\t\tchecked += 1;\n\t\tconst tags = deriveTags({\n\t\t\tkind: String(row.kind ?? \"\"),\n\t\t\ttitle: String(row.title ?? \"\"),\n\t\t\tconcepts: parseJsonStringList(row.concepts),\n\t\t\tfilesRead: parseJsonStringList(row.files_read),\n\t\t\tfilesModified: parseJsonStringList(row.files_modified),\n\t\t});\n\t\tconst tagsText = tags.join(\" \");\n\t\tif (!tagsText) {\n\t\t\tskipped += 1;\n\t\t\tcontinue;\n\t\t}\n\t\tupdates.push({ id: row.id, tagsText });\n\t\tupdated += 1;\n\t}\n\n\tif (!dryRun && updates.length > 0) {\n\t\tdb.transaction(() => {\n\t\t\tfor (const update of updates) {\n\t\t\t\tupdateStmt.run(update.tagsText, now, update.id);\n\t\t\t}\n\t\t})();\n\t}\n\n\treturn { checked, updated, skipped };\n}\n\nexport interface DeactivateLowSignalResult {\n\tchecked: number;\n\tdeactivated: number;\n}\n\nexport interface DeactivateLowSignalMemoriesOptions {\n\tkinds?: string[] | null;\n\tlimit?: number | null;\n\tdryRun?: boolean;\n}\n\nconst DEFAULT_LOW_SIGNAL_KINDS = [\n\t\"observation\",\n\t\"discovery\",\n\t\"change\",\n\t\"feature\",\n\t\"bugfix\",\n\t\"refactor\",\n\t\"decision\",\n\t\"note\",\n\t\"entities\",\n\t\"session_summary\",\n];\n\nconst OBSERVATION_EQUIVALENT_KINDS = [\n\t\"observation\",\n\t\"bugfix\",\n\t\"feature\",\n\t\"refactor\",\n\t\"change\",\n\t\"discovery\",\n\t\"decision\",\n\t\"exploration\",\n];\n\n/**\n * Deactivate low-signal observations only.\n */\nexport function deactivateLowSignalObservations(\n\tdb: Database,\n\tlimit?: number | null,\n\tdryRun = false,\n): DeactivateLowSignalResult {\n\treturn deactivateLowSignalMemories(db, {\n\t\tkinds: OBSERVATION_EQUIVALENT_KINDS,\n\t\tlimit,\n\t\tdryRun,\n\t});\n}\n\n/**\n * Deactivate low-signal memories across selected kinds (does not delete rows).\n */\nexport function deactivateLowSignalMemories(\n\tdb: Database,\n\topts: DeactivateLowSignalMemoriesOptions = {},\n): DeactivateLowSignalResult {\n\tconst selectedKinds =\n\t\topts.kinds?.map((kind) => kind.trim()).filter((kind) => kind.length > 0) ?? [];\n\tconst kinds = selectedKinds.length > 0 ? selectedKinds : DEFAULT_LOW_SIGNAL_KINDS;\n\tconst placeholders = kinds.map(() => \"?\").join(\",\");\n\tconst params: unknown[] = [...kinds];\n\tlet limitClause = \"\";\n\tif (opts.limit != null && opts.limit > 0) {\n\t\tlimitClause = \"LIMIT ?\";\n\t\tparams.push(opts.limit);\n\t}\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT id, title, body_text\n\t\t\t FROM memory_items\n\t\t\t WHERE kind IN (${placeholders}) AND active = 1\n\t\t\t ORDER BY id DESC\n\t\t\t ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{ id: number; title: string | null; body_text: string | null }>;\n\n\tconst checked = rows.length;\n\tconst ids = rows\n\t\t.filter((row) => isLowSignalObservation(row.body_text || row.title || \"\"))\n\t\t.map((row) => Number(row.id));\n\n\tif (ids.length === 0 || opts.dryRun === true) {\n\t\treturn { checked, deactivated: ids.length };\n\t}\n\n\tconst now = new Date().toISOString();\n\tconst chunkSize = 200;\n\tfor (let start = 0; start < ids.length; start += chunkSize) {\n\t\tconst chunk = ids.slice(start, start + chunkSize);\n\t\tconst chunkPlaceholders = chunk.map(() => \"?\").join(\",\");\n\t\tdb.prepare(\n\t\t\t`UPDATE memory_items SET active = 0, updated_at = ? WHERE id IN (${chunkPlaceholders})`,\n\t\t).run(now, ...chunk);\n\t}\n\n\treturn { checked, deactivated: ids.length };\n}\n","/**\n * Sync HTTP client: JSON request helper using Node.js built-in fetch.\n *\n * Async counterpart to the synchronous Python http.client implementation.\n * Ported from codemem/sync/http_client.py.\n */\n\n// ---------------------------------------------------------------------------\n// URL helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Normalize a peer address into a base URL.\n *\n * Adds `http://` when no scheme is present, trims whitespace and trailing slashes.\n */\nexport function buildBaseUrl(address: string): string {\n\tconst trimmed = address.trim().replace(/\\/+$/, \"\");\n\tif (!trimmed) return \"\";\n\t// Check for an existing scheme (e.g. http://, https://)\n\tif (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(trimmed)) return trimmed;\n\treturn `http://${trimmed}`;\n}\n\n// ---------------------------------------------------------------------------\n// JSON request\n// ---------------------------------------------------------------------------\n\nexport interface RequestJsonOptions {\n\theaders?: Record<string, string>;\n\tbody?: Record<string, unknown>;\n\tbodyBytes?: Uint8Array;\n\ttimeoutS?: number;\n}\n\n/**\n * Send an HTTP request and parse the JSON response.\n *\n * Returns `[statusCode, parsedBody]`. The body is null when the response\n * has no content. Non-JSON responses are returned as `{ error: \"non_json_response: ...\" }`.\n */\nexport async function requestJson(\n\tmethod: string,\n\turl: string,\n\toptions: RequestJsonOptions = {},\n): Promise<[status: number, body: Record<string, unknown> | null]> {\n\tconst { headers, body, timeoutS = 3 } = options;\n\tlet { bodyBytes } = options;\n\n\tif (bodyBytes == null && body != null) {\n\t\tbodyBytes = new TextEncoder().encode(JSON.stringify(body));\n\t}\n\n\tconst requestHeaders: Record<string, string> = {\n\t\tAccept: \"application/json\",\n\t};\n\tif (bodyBytes != null) {\n\t\trequestHeaders[\"Content-Type\"] = \"application/json\";\n\t\trequestHeaders[\"Content-Length\"] = String(bodyBytes.byteLength);\n\t}\n\tif (headers) {\n\t\tObject.assign(requestHeaders, headers);\n\t}\n\n\tconst response = await fetch(url, {\n\t\tmethod,\n\t\theaders: requestHeaders,\n\t\tbody: bodyBytes ?? null,\n\t\tsignal: AbortSignal.timeout(timeoutS * 1000),\n\t});\n\n\tconst raw = await response.text();\n\tif (!raw) return [response.status, null];\n\n\tlet payload: unknown;\n\ttry {\n\t\tpayload = JSON.parse(raw);\n\t} catch {\n\t\tconst snippet = raw.slice(0, 240).trim();\n\t\treturn [\n\t\t\tresponse.status,\n\t\t\t{ error: snippet ? `non_json_response: ${snippet}` : \"non_json_response\" },\n\t\t];\n\t}\n\n\tif (typeof payload === \"object\" && payload !== null && !Array.isArray(payload)) {\n\t\treturn [response.status, payload as Record<string, unknown>];\n\t}\n\treturn [\n\t\tresponse.status,\n\t\t{ error: `unexpected_json_type: ${Array.isArray(payload) ? \"array\" : typeof payload}` },\n\t];\n}\n","/**\n * Device identity management for the codemem sync system.\n *\n * Handles Ed25519 keypair generation, fingerprinting, and optional\n * keychain storage. Ported from codemem/sync_identity.py.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport {\n\tcreateHash,\n\tcreatePrivateKey,\n\tcreatePublicKey,\n\tgenerateKeyPairSync,\n\trandomUUID,\n} from \"node:crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { eq } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { connect as connectDb, resolveDbPath } from \"./db.js\";\nimport * as schema from \"./schema.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_KEYS_DIR = join(homedir(), \".config\", \"codemem\", \"keys\");\nconst PRIVATE_KEY_NAME = \"device.key\";\nconst PUBLIC_KEY_NAME = \"device.key.pub\";\nconst KEYCHAIN_SERVICE = \"codemem-sync\";\n\n// ---------------------------------------------------------------------------\n// Fingerprint\n// ---------------------------------------------------------------------------\n\n/** SHA-256 hex digest of a public key string. */\nexport function fingerprintPublicKey(publicKey: string): string {\n\treturn createHash(\"sha256\").update(publicKey, \"utf-8\").digest(\"hex\");\n}\n\n// ---------------------------------------------------------------------------\n// Key store mode\n// ---------------------------------------------------------------------------\n\nfunction keyStoreMode(): \"file\" | \"keychain\" {\n\tconst env = process.env.CODEMEM_SYNC_KEY_STORE?.toLowerCase();\n\tif (env === \"keychain\") return \"keychain\";\n\treturn \"file\";\n}\n\n// ---------------------------------------------------------------------------\n// CLI availability helpers\n// ---------------------------------------------------------------------------\n\nfunction cliAvailable(cmd: string): boolean {\n\ttry {\n\t\texecFileSync(\"which\", [cmd], { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Keychain storage (platform-specific)\n// ---------------------------------------------------------------------------\n\nfunction warnKeychainLimitations(): void {\n\tif (process.platform !== \"darwin\") return;\n\tif (keyStoreMode() !== \"keychain\") return;\n\tif (process.env.CODEMEM_SYNC_KEYCHAIN_WARN === \"0\") return;\n\tconsole.warn(\n\t\t\"[codemem] keychain storage on macOS uses the `security` CLI and may expose the key in process arguments.\",\n\t);\n}\n\n/** Store a private key in the OS keychain. Returns true on success. */\nexport function storePrivateKeyKeychain(privateKey: Buffer, deviceId: string): boolean {\n\tif (process.platform === \"linux\") {\n\t\tif (!cliAvailable(\"secret-tool\")) return false;\n\t\ttry {\n\t\t\texecFileSync(\n\t\t\t\t\"secret-tool\",\n\t\t\t\t[\"store\", \"--label\", \"codemem sync key\", \"service\", KEYCHAIN_SERVICE, \"account\", deviceId],\n\t\t\t\t{ input: privateKey, stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\tif (process.platform === \"darwin\") {\n\t\tif (!cliAvailable(\"security\")) return false;\n\t\ttry {\n\t\t\texecFileSync(\n\t\t\t\t\"security\",\n\t\t\t\t[\n\t\t\t\t\t\"add-generic-password\",\n\t\t\t\t\t\"-a\",\n\t\t\t\t\tdeviceId,\n\t\t\t\t\t\"-s\",\n\t\t\t\t\tKEYCHAIN_SERVICE,\n\t\t\t\t\t\"-w\",\n\t\t\t\t\tprivateKey.toString(\"utf-8\"),\n\t\t\t\t\t\"-U\",\n\t\t\t\t],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\treturn false;\n}\n\n/** Load a private key from the OS keychain. Returns null if unavailable. */\nexport function loadPrivateKeyKeychain(deviceId: string): Buffer | null {\n\tif (process.platform === \"linux\") {\n\t\tif (!cliAvailable(\"secret-tool\")) return null;\n\t\ttry {\n\t\t\tconst out = execFileSync(\n\t\t\t\t\"secret-tool\",\n\t\t\t\t[\"lookup\", \"service\", KEYCHAIN_SERVICE, \"account\", deviceId],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn Buffer.from(out);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tif (process.platform === \"darwin\") {\n\t\tif (!cliAvailable(\"security\")) return null;\n\t\ttry {\n\t\t\tconst out = execFileSync(\n\t\t\t\t\"security\",\n\t\t\t\t[\"find-generic-password\", \"-a\", deviceId, \"-s\", KEYCHAIN_SERVICE, \"-w\"],\n\t\t\t\t{ stdio: [\"pipe\", \"pipe\", \"pipe\"] },\n\t\t\t);\n\t\t\treturn Buffer.from(out);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// Key path resolution\n// ---------------------------------------------------------------------------\n\n/** Resolve private and public key file paths. */\nexport function resolveKeyPaths(keysDir?: string): [string, string] {\n\tconst dir = keysDir ?? DEFAULT_KEYS_DIR;\n\treturn [join(dir, PRIVATE_KEY_NAME), join(dir, PUBLIC_KEY_NAME)];\n}\n\n// ---------------------------------------------------------------------------\n// Key file I/O\n// ---------------------------------------------------------------------------\n\n/** Read the public key from disk. Returns null if missing or empty. */\nexport function loadPublicKey(keysDir?: string): string | null {\n\tconst [, publicPath] = resolveKeyPaths(keysDir);\n\tif (!existsSync(publicPath)) return null;\n\tconst content = readFileSync(publicPath, \"utf-8\").trim();\n\treturn content || null;\n}\n\n/** Read the private key from disk (with keychain fallback). Returns null if unavailable. */\nexport function loadPrivateKey(keysDir?: string, dbPath?: string): Buffer | null {\n\tif (keyStoreMode() === \"keychain\") {\n\t\tconst deviceId = loadDeviceId(dbPath);\n\t\tif (deviceId) {\n\t\t\tconst keychainValue = loadPrivateKeyKeychain(deviceId);\n\t\t\tif (keychainValue) return keychainValue;\n\t\t}\n\t}\n\tconst [privatePath] = resolveKeyPaths(keysDir);\n\tif (!existsSync(privatePath)) return null;\n\treturn readFileSync(privatePath);\n}\n\n/** Load device_id from the database (for keychain lookups). */\nfunction loadDeviceId(dbPath?: string): string | null {\n\tconst path = resolveDbPath(dbPath);\n\tif (!existsSync(path)) return null;\n\tconst conn = connectDb(path);\n\ttry {\n\t\tconst d = drizzle(conn, { schema });\n\t\tconst row = d\n\t\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t\t.from(schema.syncDevice)\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\treturn row?.device_id ?? null;\n\t} finally {\n\t\tconn.close();\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Key generation & validation\n// ---------------------------------------------------------------------------\n\nfunction publicKeyLooksValid(publicKey: string): boolean {\n\tconst value = publicKey.trim();\n\treturn (\n\t\tvalue.startsWith(\"ssh-ed25519 \") || value.startsWith(\"ssh-rsa \") || value.startsWith(\"ecdsa-\")\n\t);\n}\n\nfunction backupInvalidKeyFile(path: string, stamp: string): void {\n\tif (!existsSync(path)) return;\n\tconst backupPath = path.replace(/([^/]+)$/, `$1.invalid-${stamp}`);\n\trenameSync(path, backupPath);\n}\n\n/**\n * Generate an Ed25519 keypair using Node's native crypto.\n * Stores private key as PEM (PKCS8), public key as SSH format for compatibility.\n */\nexport function generateKeypair(privatePath: string, publicPath: string): void {\n\tmkdirSync(dirname(privatePath), { recursive: true });\n\tif (existsSync(privatePath) && existsSync(publicPath)) return;\n\n\tif (existsSync(privatePath) && !existsSync(publicPath)) {\n\t\t// Private key exists but public is missing — derive public from private\n\t\ttry {\n\t\t\tconst privKeyObj = loadPrivateKeyObject(privatePath);\n\t\t\tconst pubKeyObj = createPublicKey(privKeyObj);\n\t\t\tconst sshPub = pubKeyObj.export({ type: \"spki\", format: \"der\" });\n\t\t\tconst sshPubStr = derToSshEd25519(sshPub);\n\t\t\tif (sshPubStr && publicKeyLooksValid(sshPubStr)) {\n\t\t\t\twriteFileSync(publicPath, `${sshPubStr}\\n`, { mode: 0o644 });\n\t\t\t\treturn;\n\t\t\t}\n\t\t} catch {\n\t\t\t// Derivation failed — fall through to full regeneration with backup\n\t\t}\n\t\t// Back up the orphaned private key before regenerating\n\t\tconst stamp = new Date().toISOString().replace(/[:.]/g, \"\");\n\t\trenameSync(privatePath, `${privatePath}.orphan-${stamp}`);\n\t}\n\n\tconst { publicKey, privateKey } = generateKeyPairSync(\"ed25519\");\n\n\t// Export private key as PEM (PKCS8)\n\tconst privatePem = privateKey.export({ type: \"pkcs8\", format: \"pem\" }) as string;\n\twriteFileSync(privatePath, privatePem, { mode: 0o600 });\n\n\t// Export public key as SSH wire format: \"ssh-ed25519 <base64>\"\n\tconst pubDer = publicKey.export({ type: \"spki\", format: \"der\" });\n\tconst sshPub = derToSshEd25519(pubDer);\n\tif (!sshPub) {\n\t\tthrow new Error(\"failed to convert public key to SSH format\");\n\t}\n\twriteFileSync(publicPath, `${sshPub}\\n`, { mode: 0o644 });\n}\n\n/**\n * Convert a DER-encoded SPKI Ed25519 public key to SSH wire format.\n * The last 32 bytes of the DER are the raw Ed25519 public key.\n */\nfunction derToSshEd25519(spkiDer: Buffer): string | null {\n\t// Ed25519 SPKI DER is 44 bytes: 12-byte header + 32-byte key\n\tif (spkiDer.length < 32) return null;\n\tconst rawKey = spkiDer.subarray(spkiDer.length - 32);\n\n\t// SSH wire format: string \"ssh-ed25519\" + string <32-byte key>\n\tconst keyType = Buffer.from(\"ssh-ed25519\");\n\tconst buf = Buffer.alloc(4 + keyType.length + 4 + rawKey.length);\n\tlet offset = 0;\n\tbuf.writeUInt32BE(keyType.length, offset);\n\toffset += 4;\n\tkeyType.copy(buf, offset);\n\toffset += keyType.length;\n\tbuf.writeUInt32BE(rawKey.length, offset);\n\toffset += 4;\n\trawKey.copy(buf, offset);\n\n\treturn `ssh-ed25519 ${buf.toString(\"base64\")}`;\n}\n\n/**\n * Load a private key that may be in OpenSSH format (existing keys) or\n * PKCS8 PEM format (newly generated keys). Node's createPrivateKey\n * handles both transparently.\n */\nfunction loadPrivateKeyObject(privatePath: string): ReturnType<typeof createPrivateKey> {\n\tconst raw = readFileSync(privatePath);\n\t// Try PKCS8 PEM first (our generated format), then OpenSSH\n\ttry {\n\t\treturn createPrivateKey(raw);\n\t} catch {\n\t\treturn createPrivateKey({ key: raw, format: \"pem\", type: \"pkcs8\" });\n\t}\n}\n\n/** Validate that an existing keypair is consistent. */\nexport function validateExistingKeypair(privatePath: string, publicPath: string): boolean {\n\tif (!existsSync(privatePath) || !existsSync(publicPath)) return false;\n\tconst publicKey = readFileSync(publicPath, \"utf-8\").trim();\n\tif (!publicKey || !publicKeyLooksValid(publicKey)) return false;\n\ttry {\n\t\tconst privKeyObj = loadPrivateKeyObject(privatePath);\n\t\tconst pubKeyObj = createPublicKey(privKeyObj);\n\t\tconst pubDer = pubKeyObj.export({ type: \"spki\", format: \"der\" });\n\t\tconst derived = derToSshEd25519(pubDer);\n\t\tif (!derived || !publicKeyLooksValid(derived)) return false;\n\t\t// Auto-fix mismatched public key file\n\t\tif (derived !== publicKey) {\n\t\t\twriteFileSync(publicPath, `${derived}\\n`, \"utf-8\");\n\t\t}\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Main entry: ensure device identity\n// ---------------------------------------------------------------------------\n\nexport interface EnsureDeviceIdentityOptions {\n\tkeysDir?: string;\n\tdeviceId?: string;\n}\n\n/**\n * Ensure this device has a keypair and a row in the sync_device table.\n *\n * Returns [deviceId, fingerprint]. Creates keys and DB row if missing,\n * validates and repairs if existing.\n */\nexport function ensureDeviceIdentity(\n\tdb: Database,\n\toptions?: EnsureDeviceIdentityOptions,\n): [string, string] {\n\tconst keysDir = options?.keysDir ?? DEFAULT_KEYS_DIR;\n\tconst [privatePath, publicPath] = resolveKeyPaths(keysDir);\n\twarnKeychainLimitations();\n\n\t// Check for existing device row\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({\n\t\t\tdevice_id: schema.syncDevice.device_id,\n\t\t\tpublic_key: schema.syncDevice.public_key,\n\t\t\tfingerprint: schema.syncDevice.fingerprint,\n\t\t})\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst existingDeviceId = row?.device_id ?? \"\";\n\tconst existingPublicKey = row?.public_key ?? \"\";\n\tconst existingFingerprint = row?.fingerprint ?? \"\";\n\n\t// Validate or regenerate keys\n\tlet keysReady = existsSync(privatePath) && existsSync(publicPath);\n\tif (keysReady && !validateExistingKeypair(privatePath, publicPath)) {\n\t\tconst stamp = new Date()\n\t\t\t.toISOString()\n\t\t\t.replace(/[-:T.Z]/g, \"\")\n\t\t\t.slice(0, 20);\n\t\tbackupInvalidKeyFile(privatePath, stamp);\n\t\tbackupInvalidKeyFile(publicPath, stamp);\n\t\tkeysReady = false;\n\t}\n\tif (!keysReady) {\n\t\tgenerateKeypair(privatePath, publicPath);\n\t}\n\n\tconst publicKey = readFileSync(publicPath, \"utf-8\").trim();\n\tif (!publicKey) {\n\t\tthrow new Error(\"public key missing\");\n\t}\n\tconst fingerprint = fingerprintPublicKey(publicKey);\n\tconst now = new Date().toISOString();\n\n\t// Update existing device row if keys changed\n\tif (existingDeviceId) {\n\t\tif (existingPublicKey !== publicKey || existingFingerprint !== fingerprint) {\n\t\t\td.update(schema.syncDevice)\n\t\t\t\t.set({ public_key: publicKey, fingerprint })\n\t\t\t\t.where(eq(schema.syncDevice.device_id, existingDeviceId))\n\t\t\t\t.run();\n\t\t}\n\t\tif (keyStoreMode() === \"keychain\") {\n\t\t\t// Read key from file directly — don't go through loadPrivateKey\n\t\t\t// which would resolve device ID from the default DB\n\t\t\tconst privateKey = existsSync(privatePath)\n\t\t\t\t? readFileSync(privatePath)\n\t\t\t\t: loadPrivateKeyKeychain(existingDeviceId);\n\t\t\tif (privateKey) {\n\t\t\t\tstorePrivateKeyKeychain(privateKey, existingDeviceId);\n\t\t\t}\n\t\t}\n\t\treturn [existingDeviceId, fingerprint];\n\t}\n\n\t// Insert new device row\n\tconst resolvedDeviceId = options?.deviceId ?? randomUUID();\n\td.insert(schema.syncDevice)\n\t\t.values({ device_id: resolvedDeviceId, public_key: publicKey, fingerprint, created_at: now })\n\t\t.run();\n\tif (keyStoreMode() === \"keychain\") {\n\t\tconst privateKey = existsSync(privatePath) ? readFileSync(privatePath) : null;\n\t\tif (privateKey) {\n\t\t\tstorePrivateKeyKeychain(privateKey, resolvedDeviceId);\n\t\t}\n\t}\n\treturn [resolvedDeviceId, fingerprint];\n}\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\nimport {\n\tdecodeInvitePayload,\n\tencodeInvitePayload,\n\textractInvitePayload,\n\ttype InvitePayload,\n\tinviteLink,\n} from \"./coordinator-invites.js\";\nimport { CoordinatorStore, DEFAULT_COORDINATOR_DB_PATH } from \"./coordinator-store.js\";\nimport { connect } from \"./db.js\";\nimport { initDatabase } from \"./maintenance.js\";\nimport { readCodememConfigFile, writeCodememConfigFile } from \"./observer-config.js\";\nimport { requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity, loadPublicKey } from \"./sync-identity.js\";\n\nconst VALID_INVITE_POLICIES = new Set([\"auto_admit\", \"approval_required\"]);\n\nfunction coordinatorRemoteTarget(config = readCodememConfigFile()): {\n\tremoteUrl: string | null;\n\tadminSecret: string | null;\n} {\n\tconst remoteUrl = String(config.sync_coordinator_url ?? \"\").trim() || null;\n\tconst adminSecret = remoteUrl\n\t\t? String(\n\t\t\t\tprocess.env.CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET ??\n\t\t\t\t\tconfig.sync_coordinator_admin_secret ??\n\t\t\t\t\t\"\",\n\t\t\t).trim() || null\n\t\t: null;\n\treturn { remoteUrl, adminSecret };\n}\n\nasync function remoteRequest(\n\tmethod: string,\n\turl: string,\n\tadminSecret: string,\n\tbody?: Record<string, unknown>,\n): Promise<Record<string, unknown> | null> {\n\tconst [status, payload] = await requestJson(method, url, {\n\t\theaders: { \"X-Codemem-Coordinator-Admin\": adminSecret },\n\t\tbody,\n\t\ttimeoutS: 3,\n\t});\n\tif (status < 200 || status >= 300) {\n\t\tconst detail = typeof payload?.error === \"string\" ? payload.error : \"unknown\";\n\t\tthrow new Error(`Remote coordinator request failed (${status}): ${detail}`);\n\t}\n\treturn payload;\n}\n\nexport async function coordinatorCreateInviteAction(opts: {\n\tgroupId: string;\n\tcoordinatorUrl?: string | null;\n\tpolicy: string;\n\tttlHours: number;\n\tcreatedBy?: string | null;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown>> {\n\tif (!VALID_INVITE_POLICIES.has(opts.policy)) throw new Error(`Invalid policy: ${opts.policy}`);\n\tconst expiresAt = new Date(Date.now() + opts.ttlHours * 3600 * 1000).toISOString();\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret)\n\t\t\tthrow new Error(\"Admin secret required to create invites via the coordinator API.\");\n\t\tconst payload = await remoteRequest(\n\t\t\t\"POST\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}/v1/admin/invites`,\n\t\t\tadminSecret,\n\t\t\t{\n\t\t\t\tgroup_id: opts.groupId,\n\t\t\t\tpolicy: opts.policy,\n\t\t\t\texpires_at: expiresAt,\n\t\t\t\tcreated_by: opts.createdBy ?? null,\n\t\t\t\tcoordinator_url: opts.coordinatorUrl || remote,\n\t\t\t},\n\t\t);\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tencoded: payload?.encoded,\n\t\t\tlink: payload?.link,\n\t\t\tpayload: payload?.payload,\n\t\t\tmode: \"remote\",\n\t\t};\n\t}\n\tconst resolvedCoordinatorUrl = String(\n\t\topts.coordinatorUrl ?? readCodememConfigFile().sync_coordinator_url ?? \"\",\n\t).trim();\n\tif (!resolvedCoordinatorUrl) throw new Error(\"Coordinator URL required.\");\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\tconst group = store.getGroup(opts.groupId);\n\t\tif (!group) throw new Error(`Group not found: ${opts.groupId}`);\n\t\tconst invite = store.createInvite({\n\t\t\tgroupId: opts.groupId,\n\t\t\tpolicy: opts.policy,\n\t\t\texpiresAt,\n\t\t\tcreatedBy: opts.createdBy ?? null,\n\t\t});\n\t\tconst payload: InvitePayload = {\n\t\t\tv: 1,\n\t\t\tkind: \"coordinator_team_invite\",\n\t\t\tcoordinator_url: resolvedCoordinatorUrl,\n\t\t\tgroup_id: opts.groupId,\n\t\t\tpolicy: opts.policy,\n\t\t\ttoken: String(invite.token ?? \"\"),\n\t\t\texpires_at: expiresAt,\n\t\t\tteam_name: (invite.team_name_snapshot as string) ?? null,\n\t\t};\n\t\tconst encoded = encodeInvitePayload(payload);\n\t\treturn {\n\t\t\tgroup_id: opts.groupId,\n\t\t\tencoded,\n\t\t\tlink: inviteLink(encoded),\n\t\t\tpayload,\n\t\t\tmode: \"local\",\n\t\t};\n\t} finally {\n\t\tstore.close();\n\t}\n}\n\nexport async function coordinatorImportInviteAction(opts: {\n\tinviteValue: string;\n\tdbPath?: string | null;\n\tkeysDir?: string | null;\n\tconfigPath?: string | null;\n}): Promise<Record<string, unknown>> {\n\tconst payload = decodeInvitePayload(extractInvitePayload(opts.inviteValue));\n\tconst resolvedDbPath = opts.dbPath ?? join(homedir(), \".codemem\", \"mem.sqlite\");\n\tinitDatabase(resolvedDbPath);\n\tconst conn = connect(resolvedDbPath);\n\tlet deviceId = \"\";\n\tlet fingerprint = \"\";\n\ttry {\n\t\t[deviceId, fingerprint] = ensureDeviceIdentity(conn, { keysDir: opts.keysDir ?? undefined });\n\t} finally {\n\t\tconn.close();\n\t}\n\tconst publicKey = loadPublicKey(opts.keysDir ?? undefined);\n\tif (!publicKey) throw new Error(\"public key missing\");\n\tconst coordinatorUrl = String(payload.coordinator_url ?? \"\").trim();\n\tif (!coordinatorUrl) throw new Error(\"Invite is missing a coordinator URL.\");\n\tconst config = readCodememConfigFile();\n\tconst displayName = String(config.actor_display_name ?? deviceId).trim() || deviceId;\n\tconst [status, response] = await requestJson(\n\t\t\"POST\",\n\t\t`${coordinatorUrl.replace(/\\/+$/, \"\")}/v1/join`,\n\t\t{\n\t\t\tbody: {\n\t\t\t\ttoken: String(payload.token),\n\t\t\t\tdevice_id: deviceId,\n\t\t\t\tpublic_key: publicKey,\n\t\t\t\tfingerprint,\n\t\t\t\tdisplay_name: displayName,\n\t\t\t},\n\t\t\ttimeoutS: 3,\n\t\t},\n\t);\n\tif (status < 200 || status >= 300) {\n\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\tthrow new Error(`Invite import failed (${status}): ${detail}`);\n\t}\n\tconst nextConfig = readCodememConfigFile();\n\tnextConfig.sync_coordinator_url = coordinatorUrl;\n\tnextConfig.sync_coordinator_group = String(payload.group_id);\n\tconst configPath = writeCodememConfigFile(nextConfig, opts.configPath ?? undefined);\n\treturn {\n\t\tgroup_id: payload.group_id,\n\t\tcoordinator_url: payload.coordinator_url,\n\t\tstatus: response?.status ?? null,\n\t\tconfig_path: configPath,\n\t};\n}\n\nexport async function coordinatorListJoinRequestsAction(opts: {\n\tgroupId: string;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown>[]> {\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret) throw new Error(\"Admin secret required.\");\n\t\tconst payload = await remoteRequest(\n\t\t\t\"GET\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}/v1/admin/join-requests?group_id=${encodeURIComponent(opts.groupId)}`,\n\t\t\tadminSecret,\n\t\t);\n\t\treturn Array.isArray(payload?.items)\n\t\t\t? payload.items.filter(\n\t\t\t\t\t(row): row is Record<string, unknown> => Boolean(row) && typeof row === \"object\",\n\t\t\t\t)\n\t\t\t: [];\n\t}\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\treturn store.listJoinRequests(opts.groupId);\n\t} finally {\n\t\tstore.close();\n\t}\n}\n\nexport async function coordinatorReviewJoinRequestAction(opts: {\n\trequestId: string;\n\tapprove: boolean;\n\treviewedBy?: string | null;\n\tdbPath?: string | null;\n\tremoteUrl?: string | null;\n\tadminSecret?: string | null;\n}): Promise<Record<string, unknown> | null> {\n\tconst remote = opts.remoteUrl ?? coordinatorRemoteTarget().remoteUrl;\n\tconst adminSecret = opts.adminSecret ?? coordinatorRemoteTarget().adminSecret;\n\tif (remote) {\n\t\tif (!adminSecret) throw new Error(\"Admin secret required.\");\n\t\tconst endpoint = opts.approve\n\t\t\t? \"/v1/admin/join-requests/approve\"\n\t\t\t: \"/v1/admin/join-requests/deny\";\n\t\tconst payload = await remoteRequest(\n\t\t\t\"POST\",\n\t\t\t`${remote.replace(/\\/+$/, \"\")}${endpoint}`,\n\t\t\tadminSecret,\n\t\t\t{\n\t\t\t\trequest_id: opts.requestId,\n\t\t\t\treviewed_by: opts.reviewedBy ?? null,\n\t\t\t},\n\t\t);\n\t\tconst request = payload?.request;\n\t\treturn request && typeof request === \"object\" ? (request as Record<string, unknown>) : null;\n\t}\n\tconst store = new CoordinatorStore(opts.dbPath ?? DEFAULT_COORDINATOR_DB_PATH);\n\ttry {\n\t\treturn store.reviewJoinRequest({\n\t\t\trequestId: opts.requestId,\n\t\t\tapproved: opts.approve,\n\t\t\treviewedBy: opts.reviewedBy ?? null,\n\t\t});\n\t} finally {\n\t\tstore.close();\n\t}\n}\n","// src/compose.ts\nvar compose = (middleware, onError, onNotFound) => {\n return (context, next) => {\n let index = -1;\n return dispatch(0);\n async function dispatch(i) {\n if (i <= index) {\n throw new Error(\"next() called multiple times\");\n }\n index = i;\n let res;\n let isError = false;\n let handler;\n if (middleware[i]) {\n handler = middleware[i][0][0];\n context.req.routeIndex = i;\n } else {\n handler = i === middleware.length && next || void 0;\n }\n if (handler) {\n try {\n res = await handler(context, () => dispatch(i + 1));\n } catch (err) {\n if (err instanceof Error && onError) {\n context.error = err;\n res = await onError(err, context);\n isError = true;\n } else {\n throw err;\n }\n }\n } else {\n if (context.finalized === false && onNotFound) {\n res = await onNotFound(context);\n }\n }\n if (res && (context.finalized === false || isError)) {\n context.res = res;\n }\n return context;\n }\n };\n};\nexport {\n compose\n};\n","// src/request/constants.ts\nvar GET_MATCH_RESULT = /* @__PURE__ */ Symbol();\nexport {\n GET_MATCH_RESULT\n};\n","// src/utils/body.ts\nimport { HonoRequest } from \"../request.js\";\nvar parseBody = async (request, options = /* @__PURE__ */ Object.create(null)) => {\n const { all = false, dot = false } = options;\n const headers = request instanceof HonoRequest ? request.raw.headers : request.headers;\n const contentType = headers.get(\"Content-Type\");\n if (contentType?.startsWith(\"multipart/form-data\") || contentType?.startsWith(\"application/x-www-form-urlencoded\")) {\n return parseFormData(request, { all, dot });\n }\n return {};\n};\nasync function parseFormData(request, options) {\n const formData = await request.formData();\n if (formData) {\n return convertFormDataToBodyData(formData, options);\n }\n return {};\n}\nfunction convertFormDataToBodyData(formData, options) {\n const form = /* @__PURE__ */ Object.create(null);\n formData.forEach((value, key) => {\n const shouldParseAllValues = options.all || key.endsWith(\"[]\");\n if (!shouldParseAllValues) {\n form[key] = value;\n } else {\n handleParsingAllValues(form, key, value);\n }\n });\n if (options.dot) {\n Object.entries(form).forEach(([key, value]) => {\n const shouldParseDotValues = key.includes(\".\");\n if (shouldParseDotValues) {\n handleParsingNestedValues(form, key, value);\n delete form[key];\n }\n });\n }\n return form;\n}\nvar handleParsingAllValues = (form, key, value) => {\n if (form[key] !== void 0) {\n if (Array.isArray(form[key])) {\n ;\n form[key].push(value);\n } else {\n form[key] = [form[key], value];\n }\n } else {\n if (!key.endsWith(\"[]\")) {\n form[key] = value;\n } else {\n form[key] = [value];\n }\n }\n};\nvar handleParsingNestedValues = (form, key, value) => {\n if (/(?:^|\\.)__proto__\\./.test(key)) {\n return;\n }\n let nestedForm = form;\n const keys = key.split(\".\");\n keys.forEach((key2, index) => {\n if (index === keys.length - 1) {\n nestedForm[key2] = value;\n } else {\n if (!nestedForm[key2] || typeof nestedForm[key2] !== \"object\" || Array.isArray(nestedForm[key2]) || nestedForm[key2] instanceof File) {\n nestedForm[key2] = /* @__PURE__ */ Object.create(null);\n }\n nestedForm = nestedForm[key2];\n }\n });\n};\nexport {\n parseBody\n};\n","// src/utils/url.ts\nvar splitPath = (path) => {\n const paths = path.split(\"/\");\n if (paths[0] === \"\") {\n paths.shift();\n }\n return paths;\n};\nvar splitRoutingPath = (routePath) => {\n const { groups, path } = extractGroupsFromPath(routePath);\n const paths = splitPath(path);\n return replaceGroupMarks(paths, groups);\n};\nvar extractGroupsFromPath = (path) => {\n const groups = [];\n path = path.replace(/\\{[^}]+\\}/g, (match, index) => {\n const mark = `@${index}`;\n groups.push([mark, match]);\n return mark;\n });\n return { groups, path };\n};\nvar replaceGroupMarks = (paths, groups) => {\n for (let i = groups.length - 1; i >= 0; i--) {\n const [mark] = groups[i];\n for (let j = paths.length - 1; j >= 0; j--) {\n if (paths[j].includes(mark)) {\n paths[j] = paths[j].replace(mark, groups[i][1]);\n break;\n }\n }\n }\n return paths;\n};\nvar patternCache = {};\nvar getPattern = (label, next) => {\n if (label === \"*\") {\n return \"*\";\n }\n const match = label.match(/^\\:([^\\{\\}]+)(?:\\{(.+)\\})?$/);\n if (match) {\n const cacheKey = `${label}#${next}`;\n if (!patternCache[cacheKey]) {\n if (match[2]) {\n patternCache[cacheKey] = next && next[0] !== \":\" && next[0] !== \"*\" ? [cacheKey, match[1], new RegExp(`^${match[2]}(?=/${next})`)] : [label, match[1], new RegExp(`^${match[2]}$`)];\n } else {\n patternCache[cacheKey] = [label, match[1], true];\n }\n }\n return patternCache[cacheKey];\n }\n return null;\n};\nvar tryDecode = (str, decoder) => {\n try {\n return decoder(str);\n } catch {\n return str.replace(/(?:%[0-9A-Fa-f]{2})+/g, (match) => {\n try {\n return decoder(match);\n } catch {\n return match;\n }\n });\n }\n};\nvar tryDecodeURI = (str) => tryDecode(str, decodeURI);\nvar getPath = (request) => {\n const url = request.url;\n const start = url.indexOf(\"/\", url.indexOf(\":\") + 4);\n let i = start;\n for (; i < url.length; i++) {\n const charCode = url.charCodeAt(i);\n if (charCode === 37) {\n const queryIndex = url.indexOf(\"?\", i);\n const hashIndex = url.indexOf(\"#\", i);\n const end = queryIndex === -1 ? hashIndex === -1 ? void 0 : hashIndex : hashIndex === -1 ? queryIndex : Math.min(queryIndex, hashIndex);\n const path = url.slice(start, end);\n return tryDecodeURI(path.includes(\"%25\") ? path.replace(/%25/g, \"%2525\") : path);\n } else if (charCode === 63 || charCode === 35) {\n break;\n }\n }\n return url.slice(start, i);\n};\nvar getQueryStrings = (url) => {\n const queryIndex = url.indexOf(\"?\", 8);\n return queryIndex === -1 ? \"\" : \"?\" + url.slice(queryIndex + 1);\n};\nvar getPathNoStrict = (request) => {\n const result = getPath(request);\n return result.length > 1 && result.at(-1) === \"/\" ? result.slice(0, -1) : result;\n};\nvar mergePath = (base, sub, ...rest) => {\n if (rest.length) {\n sub = mergePath(sub, ...rest);\n }\n return `${base?.[0] === \"/\" ? \"\" : \"/\"}${base}${sub === \"/\" ? \"\" : `${base?.at(-1) === \"/\" ? \"\" : \"/\"}${sub?.[0] === \"/\" ? sub.slice(1) : sub}`}`;\n};\nvar checkOptionalParameter = (path) => {\n if (path.charCodeAt(path.length - 1) !== 63 || !path.includes(\":\")) {\n return null;\n }\n const segments = path.split(\"/\");\n const results = [];\n let basePath = \"\";\n segments.forEach((segment) => {\n if (segment !== \"\" && !/\\:/.test(segment)) {\n basePath += \"/\" + segment;\n } else if (/\\:/.test(segment)) {\n if (/\\?/.test(segment)) {\n if (results.length === 0 && basePath === \"\") {\n results.push(\"/\");\n } else {\n results.push(basePath);\n }\n const optionalSegment = segment.replace(\"?\", \"\");\n basePath += \"/\" + optionalSegment;\n results.push(basePath);\n } else {\n basePath += \"/\" + segment;\n }\n }\n });\n return results.filter((v, i, a) => a.indexOf(v) === i);\n};\nvar _decodeURI = (value) => {\n if (!/[%+]/.test(value)) {\n return value;\n }\n if (value.indexOf(\"+\") !== -1) {\n value = value.replace(/\\+/g, \" \");\n }\n return value.indexOf(\"%\") !== -1 ? tryDecode(value, decodeURIComponent_) : value;\n};\nvar _getQueryParam = (url, key, multiple) => {\n let encoded;\n if (!multiple && key && !/[%+]/.test(key)) {\n let keyIndex2 = url.indexOf(\"?\", 8);\n if (keyIndex2 === -1) {\n return void 0;\n }\n if (!url.startsWith(key, keyIndex2 + 1)) {\n keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);\n }\n while (keyIndex2 !== -1) {\n const trailingKeyCode = url.charCodeAt(keyIndex2 + key.length + 1);\n if (trailingKeyCode === 61) {\n const valueIndex = keyIndex2 + key.length + 2;\n const endIndex = url.indexOf(\"&\", valueIndex);\n return _decodeURI(url.slice(valueIndex, endIndex === -1 ? void 0 : endIndex));\n } else if (trailingKeyCode == 38 || isNaN(trailingKeyCode)) {\n return \"\";\n }\n keyIndex2 = url.indexOf(`&${key}`, keyIndex2 + 1);\n }\n encoded = /[%+]/.test(url);\n if (!encoded) {\n return void 0;\n }\n }\n const results = {};\n encoded ??= /[%+]/.test(url);\n let keyIndex = url.indexOf(\"?\", 8);\n while (keyIndex !== -1) {\n const nextKeyIndex = url.indexOf(\"&\", keyIndex + 1);\n let valueIndex = url.indexOf(\"=\", keyIndex);\n if (valueIndex > nextKeyIndex && nextKeyIndex !== -1) {\n valueIndex = -1;\n }\n let name = url.slice(\n keyIndex + 1,\n valueIndex === -1 ? nextKeyIndex === -1 ? void 0 : nextKeyIndex : valueIndex\n );\n if (encoded) {\n name = _decodeURI(name);\n }\n keyIndex = nextKeyIndex;\n if (name === \"\") {\n continue;\n }\n let value;\n if (valueIndex === -1) {\n value = \"\";\n } else {\n value = url.slice(valueIndex + 1, nextKeyIndex === -1 ? void 0 : nextKeyIndex);\n if (encoded) {\n value = _decodeURI(value);\n }\n }\n if (multiple) {\n if (!(results[name] && Array.isArray(results[name]))) {\n results[name] = [];\n }\n ;\n results[name].push(value);\n } else {\n results[name] ??= value;\n }\n }\n return key ? results[key] : results;\n};\nvar getQueryParam = _getQueryParam;\nvar getQueryParams = (url, key) => {\n return _getQueryParam(url, key, true);\n};\nvar decodeURIComponent_ = decodeURIComponent;\nexport {\n checkOptionalParameter,\n decodeURIComponent_,\n getPath,\n getPathNoStrict,\n getPattern,\n getQueryParam,\n getQueryParams,\n getQueryStrings,\n mergePath,\n splitPath,\n splitRoutingPath,\n tryDecode,\n tryDecodeURI\n};\n","// src/request.ts\nimport { HTTPException } from \"./http-exception.js\";\nimport { GET_MATCH_RESULT } from \"./request/constants.js\";\nimport { parseBody } from \"./utils/body.js\";\nimport { decodeURIComponent_, getQueryParam, getQueryParams, tryDecode } from \"./utils/url.js\";\nvar tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_);\nvar HonoRequest = class {\n /**\n * `.raw` can get the raw Request object.\n *\n * @see {@link https://hono.dev/docs/api/request#raw}\n *\n * @example\n * ```ts\n * // For Cloudflare Workers\n * app.post('/', async (c) => {\n * const metadata = c.req.raw.cf?.hostMetadata?\n * ...\n * })\n * ```\n */\n raw;\n #validatedData;\n // Short name of validatedData\n #matchResult;\n routeIndex = 0;\n /**\n * `.path` can get the pathname of the request.\n *\n * @see {@link https://hono.dev/docs/api/request#path}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const pathname = c.req.path // `/about/me`\n * })\n * ```\n */\n path;\n bodyCache = {};\n constructor(request, path = \"/\", matchResult = [[]]) {\n this.raw = request;\n this.path = path;\n this.#matchResult = matchResult;\n this.#validatedData = {};\n }\n param(key) {\n return key ? this.#getDecodedParam(key) : this.#getAllDecodedParams();\n }\n #getDecodedParam(key) {\n const paramKey = this.#matchResult[0][this.routeIndex][1][key];\n const param = this.#getParamValue(paramKey);\n return param && /\\%/.test(param) ? tryDecodeURIComponent(param) : param;\n }\n #getAllDecodedParams() {\n const decoded = {};\n const keys = Object.keys(this.#matchResult[0][this.routeIndex][1]);\n for (const key of keys) {\n const value = this.#getParamValue(this.#matchResult[0][this.routeIndex][1][key]);\n if (value !== void 0) {\n decoded[key] = /\\%/.test(value) ? tryDecodeURIComponent(value) : value;\n }\n }\n return decoded;\n }\n #getParamValue(paramKey) {\n return this.#matchResult[1] ? this.#matchResult[1][paramKey] : paramKey;\n }\n query(key) {\n return getQueryParam(this.url, key);\n }\n queries(key) {\n return getQueryParams(this.url, key);\n }\n header(name) {\n if (name) {\n return this.raw.headers.get(name) ?? void 0;\n }\n const headerData = {};\n this.raw.headers.forEach((value, key) => {\n headerData[key] = value;\n });\n return headerData;\n }\n async parseBody(options) {\n return this.bodyCache.parsedBody ??= await parseBody(this, options);\n }\n #cachedBody = (key) => {\n const { bodyCache, raw } = this;\n const cachedBody = bodyCache[key];\n if (cachedBody) {\n return cachedBody;\n }\n const anyCachedKey = Object.keys(bodyCache)[0];\n if (anyCachedKey) {\n return bodyCache[anyCachedKey].then((body) => {\n if (anyCachedKey === \"json\") {\n body = JSON.stringify(body);\n }\n return new Response(body)[key]();\n });\n }\n return bodyCache[key] = raw[key]();\n };\n /**\n * `.json()` can parse Request body of type `application/json`\n *\n * @see {@link https://hono.dev/docs/api/request#json}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.json()\n * })\n * ```\n */\n json() {\n return this.#cachedBody(\"text\").then((text) => JSON.parse(text));\n }\n /**\n * `.text()` can parse Request body of type `text/plain`\n *\n * @see {@link https://hono.dev/docs/api/request#text}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.text()\n * })\n * ```\n */\n text() {\n return this.#cachedBody(\"text\");\n }\n /**\n * `.arrayBuffer()` parse Request body as an `ArrayBuffer`\n *\n * @see {@link https://hono.dev/docs/api/request#arraybuffer}\n *\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.arrayBuffer()\n * })\n * ```\n */\n arrayBuffer() {\n return this.#cachedBody(\"arrayBuffer\");\n }\n /**\n * Parses the request body as a `Blob`.\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.blob();\n * });\n * ```\n * @see https://hono.dev/docs/api/request#blob\n */\n blob() {\n return this.#cachedBody(\"blob\");\n }\n /**\n * Parses the request body as `FormData`.\n * @example\n * ```ts\n * app.post('/entry', async (c) => {\n * const body = await c.req.formData();\n * });\n * ```\n * @see https://hono.dev/docs/api/request#formdata\n */\n formData() {\n return this.#cachedBody(\"formData\");\n }\n /**\n * Adds validated data to the request.\n *\n * @param target - The target of the validation.\n * @param data - The validated data to add.\n */\n addValidatedData(target, data) {\n this.#validatedData[target] = data;\n }\n valid(target) {\n return this.#validatedData[target];\n }\n /**\n * `.url()` can get the request url strings.\n *\n * @see {@link https://hono.dev/docs/api/request#url}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const url = c.req.url // `http://localhost:8787/about/me`\n * ...\n * })\n * ```\n */\n get url() {\n return this.raw.url;\n }\n /**\n * `.method()` can get the method name of the request.\n *\n * @see {@link https://hono.dev/docs/api/request#method}\n *\n * @example\n * ```ts\n * app.get('/about/me', (c) => {\n * const method = c.req.method // `GET`\n * })\n * ```\n */\n get method() {\n return this.raw.method;\n }\n get [GET_MATCH_RESULT]() {\n return this.#matchResult;\n }\n /**\n * `.matchedRoutes()` can return a matched route in the handler\n *\n * @deprecated\n *\n * Use matchedRoutes helper defined in \"hono/route\" instead.\n *\n * @see {@link https://hono.dev/docs/api/request#matchedroutes}\n *\n * @example\n * ```ts\n * app.use('*', async function logger(c, next) {\n * await next()\n * c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {\n * const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')\n * console.log(\n * method,\n * ' ',\n * path,\n * ' '.repeat(Math.max(10 - path.length, 0)),\n * name,\n * i === c.req.routeIndex ? '<- respond from here' : ''\n * )\n * })\n * })\n * ```\n */\n get matchedRoutes() {\n return this.#matchResult[0].map(([[, route]]) => route);\n }\n /**\n * `routePath()` can retrieve the path registered within the handler\n *\n * @deprecated\n *\n * Use routePath helper defined in \"hono/route\" instead.\n *\n * @see {@link https://hono.dev/docs/api/request#routepath}\n *\n * @example\n * ```ts\n * app.get('/posts/:id', (c) => {\n * return c.json({ path: c.req.routePath })\n * })\n * ```\n */\n get routePath() {\n return this.#matchResult[0].map(([[, route]]) => route)[this.routeIndex].path;\n }\n};\nvar cloneRawRequest = async (req) => {\n if (!req.raw.bodyUsed) {\n return req.raw.clone();\n }\n const cacheKey = Object.keys(req.bodyCache)[0];\n if (!cacheKey) {\n throw new HTTPException(500, {\n message: \"Cannot clone request: body was already consumed and not cached. Please use HonoRequest methods (e.g., req.json(), req.text()) instead of consuming req.raw directly.\"\n });\n }\n const requestInit = {\n body: await req[cacheKey](),\n cache: req.raw.cache,\n credentials: req.raw.credentials,\n headers: req.header(),\n integrity: req.raw.integrity,\n keepalive: req.raw.keepalive,\n method: req.method,\n mode: req.raw.mode,\n redirect: req.raw.redirect,\n referrer: req.raw.referrer,\n referrerPolicy: req.raw.referrerPolicy,\n signal: req.raw.signal\n };\n return new Request(req.url, requestInit);\n};\nexport {\n HonoRequest,\n cloneRawRequest\n};\n","// src/utils/html.ts\nvar HtmlEscapedCallbackPhase = {\n Stringify: 1,\n BeforeStream: 2,\n Stream: 3\n};\nvar raw = (value, callbacks) => {\n const escapedString = new String(value);\n escapedString.isEscaped = true;\n escapedString.callbacks = callbacks;\n return escapedString;\n};\nvar escapeRe = /[&<>'\"]/;\nvar stringBufferToString = async (buffer, callbacks) => {\n let str = \"\";\n callbacks ||= [];\n const resolvedBuffer = await Promise.all(buffer);\n for (let i = resolvedBuffer.length - 1; ; i--) {\n str += resolvedBuffer[i];\n i--;\n if (i < 0) {\n break;\n }\n let r = resolvedBuffer[i];\n if (typeof r === \"object\") {\n callbacks.push(...r.callbacks || []);\n }\n const isEscaped = r.isEscaped;\n r = await (typeof r === \"object\" ? r.toString() : r);\n if (typeof r === \"object\") {\n callbacks.push(...r.callbacks || []);\n }\n if (r.isEscaped ?? isEscaped) {\n str += r;\n } else {\n const buf = [str];\n escapeToBuffer(r, buf);\n str = buf[0];\n }\n }\n return raw(str, callbacks);\n};\nvar escapeToBuffer = (str, buffer) => {\n const match = str.search(escapeRe);\n if (match === -1) {\n buffer[0] += str;\n return;\n }\n let escape;\n let index;\n let lastIndex = 0;\n for (index = match; index < str.length; index++) {\n switch (str.charCodeAt(index)) {\n case 34:\n escape = \"&quot;\";\n break;\n case 39:\n escape = \"&#39;\";\n break;\n case 38:\n escape = \"&amp;\";\n break;\n case 60:\n escape = \"&lt;\";\n break;\n case 62:\n escape = \"&gt;\";\n break;\n default:\n continue;\n }\n buffer[0] += str.substring(lastIndex, index) + escape;\n lastIndex = index + 1;\n }\n buffer[0] += str.substring(lastIndex, index);\n};\nvar resolveCallbackSync = (str) => {\n const callbacks = str.callbacks;\n if (!callbacks?.length) {\n return str;\n }\n const buffer = [str];\n const context = {};\n callbacks.forEach((c) => c({ phase: HtmlEscapedCallbackPhase.Stringify, buffer, context }));\n return buffer[0];\n};\nvar resolveCallback = async (str, phase, preserveCallbacks, context, buffer) => {\n if (typeof str === \"object\" && !(str instanceof String)) {\n if (!(str instanceof Promise)) {\n str = str.toString();\n }\n if (str instanceof Promise) {\n str = await str;\n }\n }\n const callbacks = str.callbacks;\n if (!callbacks?.length) {\n return Promise.resolve(str);\n }\n if (buffer) {\n buffer[0] += str;\n } else {\n buffer = [str];\n }\n const resStr = Promise.all(callbacks.map((c) => c({ phase, buffer, context }))).then(\n (res) => Promise.all(\n res.filter(Boolean).map((str2) => resolveCallback(str2, phase, false, context, buffer))\n ).then(() => buffer[0])\n );\n if (preserveCallbacks) {\n return raw(await resStr, callbacks);\n } else {\n return resStr;\n }\n};\nexport {\n HtmlEscapedCallbackPhase,\n escapeToBuffer,\n raw,\n resolveCallback,\n resolveCallbackSync,\n stringBufferToString\n};\n","// src/context.ts\nimport { HonoRequest } from \"./request.js\";\nimport { HtmlEscapedCallbackPhase, resolveCallback } from \"./utils/html.js\";\nvar TEXT_PLAIN = \"text/plain; charset=UTF-8\";\nvar setDefaultContentType = (contentType, headers) => {\n return {\n \"Content-Type\": contentType,\n ...headers\n };\n};\nvar createResponseInstance = (body, init) => new Response(body, init);\nvar Context = class {\n #rawRequest;\n #req;\n /**\n * `.env` can get bindings (environment variables, secrets, KV namespaces, D1 database, R2 bucket etc.) in Cloudflare Workers.\n *\n * @see {@link https://hono.dev/docs/api/context#env}\n *\n * @example\n * ```ts\n * // Environment object for Cloudflare Workers\n * app.get('*', async c => {\n * const counter = c.env.COUNTER\n * })\n * ```\n */\n env = {};\n #var;\n finalized = false;\n /**\n * `.error` can get the error object from the middleware if the Handler throws an error.\n *\n * @see {@link https://hono.dev/docs/api/context#error}\n *\n * @example\n * ```ts\n * app.use('*', async (c, next) => {\n * await next()\n * if (c.error) {\n * // do something...\n * }\n * })\n * ```\n */\n error;\n #status;\n #executionCtx;\n #res;\n #layout;\n #renderer;\n #notFoundHandler;\n #preparedHeaders;\n #matchResult;\n #path;\n /**\n * Creates an instance of the Context class.\n *\n * @param req - The Request object.\n * @param options - Optional configuration options for the context.\n */\n constructor(req, options) {\n this.#rawRequest = req;\n if (options) {\n this.#executionCtx = options.executionCtx;\n this.env = options.env;\n this.#notFoundHandler = options.notFoundHandler;\n this.#path = options.path;\n this.#matchResult = options.matchResult;\n }\n }\n /**\n * `.req` is the instance of {@link HonoRequest}.\n */\n get req() {\n this.#req ??= new HonoRequest(this.#rawRequest, this.#path, this.#matchResult);\n return this.#req;\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#event}\n * The FetchEvent associated with the current request.\n *\n * @throws Will throw an error if the context does not have a FetchEvent.\n */\n get event() {\n if (this.#executionCtx && \"respondWith\" in this.#executionCtx) {\n return this.#executionCtx;\n } else {\n throw Error(\"This context has no FetchEvent\");\n }\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#executionctx}\n * The ExecutionContext associated with the current request.\n *\n * @throws Will throw an error if the context does not have an ExecutionContext.\n */\n get executionCtx() {\n if (this.#executionCtx) {\n return this.#executionCtx;\n } else {\n throw Error(\"This context has no ExecutionContext\");\n }\n }\n /**\n * @see {@link https://hono.dev/docs/api/context#res}\n * The Response object for the current request.\n */\n get res() {\n return this.#res ||= createResponseInstance(null, {\n headers: this.#preparedHeaders ??= new Headers()\n });\n }\n /**\n * Sets the Response object for the current request.\n *\n * @param _res - The Response object to set.\n */\n set res(_res) {\n if (this.#res && _res) {\n _res = createResponseInstance(_res.body, _res);\n for (const [k, v] of this.#res.headers.entries()) {\n if (k === \"content-type\") {\n continue;\n }\n if (k === \"set-cookie\") {\n const cookies = this.#res.headers.getSetCookie();\n _res.headers.delete(\"set-cookie\");\n for (const cookie of cookies) {\n _res.headers.append(\"set-cookie\", cookie);\n }\n } else {\n _res.headers.set(k, v);\n }\n }\n }\n this.#res = _res;\n this.finalized = true;\n }\n /**\n * `.render()` can create a response within a layout.\n *\n * @see {@link https://hono.dev/docs/api/context#render-setrenderer}\n *\n * @example\n * ```ts\n * app.get('/', (c) => {\n * return c.render('Hello!')\n * })\n * ```\n */\n render = (...args) => {\n this.#renderer ??= (content) => this.html(content);\n return this.#renderer(...args);\n };\n /**\n * Sets the layout for the response.\n *\n * @param layout - The layout to set.\n * @returns The layout function.\n */\n setLayout = (layout) => this.#layout = layout;\n /**\n * Gets the current layout for the response.\n *\n * @returns The current layout function.\n */\n getLayout = () => this.#layout;\n /**\n * `.setRenderer()` can set the layout in the custom middleware.\n *\n * @see {@link https://hono.dev/docs/api/context#render-setrenderer}\n *\n * @example\n * ```tsx\n * app.use('*', async (c, next) => {\n * c.setRenderer((content) => {\n * return c.html(\n * <html>\n * <body>\n * <p>{content}</p>\n * </body>\n * </html>\n * )\n * })\n * await next()\n * })\n * ```\n */\n setRenderer = (renderer) => {\n this.#renderer = renderer;\n };\n /**\n * `.header()` can set headers.\n *\n * @see {@link https://hono.dev/docs/api/context#header}\n *\n * @example\n * ```ts\n * app.get('/welcome', (c) => {\n * // Set headers\n * c.header('X-Message', 'Hello!')\n * c.header('Content-Type', 'text/plain')\n *\n * return c.body('Thank you for coming')\n * })\n * ```\n */\n header = (name, value, options) => {\n if (this.finalized) {\n this.#res = createResponseInstance(this.#res.body, this.#res);\n }\n const headers = this.#res ? this.#res.headers : this.#preparedHeaders ??= new Headers();\n if (value === void 0) {\n headers.delete(name);\n } else if (options?.append) {\n headers.append(name, value);\n } else {\n headers.set(name, value);\n }\n };\n status = (status) => {\n this.#status = status;\n };\n /**\n * `.set()` can set the value specified by the key.\n *\n * @see {@link https://hono.dev/docs/api/context#set-get}\n *\n * @example\n * ```ts\n * app.use('*', async (c, next) => {\n * c.set('message', 'Hono is hot!!')\n * await next()\n * })\n * ```\n */\n set = (key, value) => {\n this.#var ??= /* @__PURE__ */ new Map();\n this.#var.set(key, value);\n };\n /**\n * `.get()` can use the value specified by the key.\n *\n * @see {@link https://hono.dev/docs/api/context#set-get}\n *\n * @example\n * ```ts\n * app.get('/', (c) => {\n * const message = c.get('message')\n * return c.text(`The message is \"${message}\"`)\n * })\n * ```\n */\n get = (key) => {\n return this.#var ? this.#var.get(key) : void 0;\n };\n /**\n * `.var` can access the value of a variable.\n *\n * @see {@link https://hono.dev/docs/api/context#var}\n *\n * @example\n * ```ts\n * const result = c.var.client.oneMethod()\n * ```\n */\n // c.var.propName is a read-only\n get var() {\n if (!this.#var) {\n return {};\n }\n return Object.fromEntries(this.#var);\n }\n #newResponse(data, arg, headers) {\n const responseHeaders = this.#res ? new Headers(this.#res.headers) : this.#preparedHeaders ?? new Headers();\n if (typeof arg === \"object\" && \"headers\" in arg) {\n const argHeaders = arg.headers instanceof Headers ? arg.headers : new Headers(arg.headers);\n for (const [key, value] of argHeaders) {\n if (key.toLowerCase() === \"set-cookie\") {\n responseHeaders.append(key, value);\n } else {\n responseHeaders.set(key, value);\n }\n }\n }\n if (headers) {\n for (const [k, v] of Object.entries(headers)) {\n if (typeof v === \"string\") {\n responseHeaders.set(k, v);\n } else {\n responseHeaders.delete(k);\n for (const v2 of v) {\n responseHeaders.append(k, v2);\n }\n }\n }\n }\n const status = typeof arg === \"number\" ? arg : arg?.status ?? this.#status;\n return createResponseInstance(data, { status, headers: responseHeaders });\n }\n newResponse = (...args) => this.#newResponse(...args);\n /**\n * `.body()` can return the HTTP response.\n * You can set headers with `.header()` and set HTTP status code with `.status`.\n * This can also be set in `.text()`, `.json()` and so on.\n *\n * @see {@link https://hono.dev/docs/api/context#body}\n *\n * @example\n * ```ts\n * app.get('/welcome', (c) => {\n * // Set headers\n * c.header('X-Message', 'Hello!')\n * c.header('Content-Type', 'text/plain')\n * // Set HTTP status code\n * c.status(201)\n *\n * // Return the response body\n * return c.body('Thank you for coming')\n * })\n * ```\n */\n body = (data, arg, headers) => this.#newResponse(data, arg, headers);\n /**\n * `.text()` can render text as `Content-Type:text/plain`.\n *\n * @see {@link https://hono.dev/docs/api/context#text}\n *\n * @example\n * ```ts\n * app.get('/say', (c) => {\n * return c.text('Hello!')\n * })\n * ```\n */\n text = (text, arg, headers) => {\n return !this.#preparedHeaders && !this.#status && !arg && !headers && !this.finalized ? new Response(text) : this.#newResponse(\n text,\n arg,\n setDefaultContentType(TEXT_PLAIN, headers)\n );\n };\n /**\n * `.json()` can render JSON as `Content-Type:application/json`.\n *\n * @see {@link https://hono.dev/docs/api/context#json}\n *\n * @example\n * ```ts\n * app.get('/api', (c) => {\n * return c.json({ message: 'Hello!' })\n * })\n * ```\n */\n json = (object, arg, headers) => {\n return this.#newResponse(\n JSON.stringify(object),\n arg,\n setDefaultContentType(\"application/json\", headers)\n );\n };\n html = (html, arg, headers) => {\n const res = (html2) => this.#newResponse(html2, arg, setDefaultContentType(\"text/html; charset=UTF-8\", headers));\n return typeof html === \"object\" ? resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then(res) : res(html);\n };\n /**\n * `.redirect()` can Redirect, default status code is 302.\n *\n * @see {@link https://hono.dev/docs/api/context#redirect}\n *\n * @example\n * ```ts\n * app.get('/redirect', (c) => {\n * return c.redirect('/')\n * })\n * app.get('/redirect-permanently', (c) => {\n * return c.redirect('/', 301)\n * })\n * ```\n */\n redirect = (location, status) => {\n const locationString = String(location);\n this.header(\n \"Location\",\n // Multibyes should be encoded\n // eslint-disable-next-line no-control-regex\n !/[^\\x00-\\xFF]/.test(locationString) ? locationString : encodeURI(locationString)\n );\n return this.newResponse(null, status ?? 302);\n };\n /**\n * `.notFound()` can return the Not Found Response.\n *\n * @see {@link https://hono.dev/docs/api/context#notfound}\n *\n * @example\n * ```ts\n * app.get('/notfound', (c) => {\n * return c.notFound()\n * })\n * ```\n */\n notFound = () => {\n this.#notFoundHandler ??= () => createResponseInstance();\n return this.#notFoundHandler(this);\n };\n};\nexport {\n Context,\n TEXT_PLAIN\n};\n","// src/router.ts\nvar METHOD_NAME_ALL = \"ALL\";\nvar METHOD_NAME_ALL_LOWERCASE = \"all\";\nvar METHODS = [\"get\", \"post\", \"put\", \"delete\", \"options\", \"patch\"];\nvar MESSAGE_MATCHER_IS_ALREADY_BUILT = \"Can not add a route since the matcher is already built.\";\nvar UnsupportedPathError = class extends Error {\n};\nexport {\n MESSAGE_MATCHER_IS_ALREADY_BUILT,\n METHODS,\n METHOD_NAME_ALL,\n METHOD_NAME_ALL_LOWERCASE,\n UnsupportedPathError\n};\n","// src/utils/constants.ts\nvar COMPOSED_HANDLER = \"__COMPOSED_HANDLER\";\nexport {\n COMPOSED_HANDLER\n};\n","// src/hono-base.ts\nimport { compose } from \"./compose.js\";\nimport { Context } from \"./context.js\";\nimport { METHODS, METHOD_NAME_ALL, METHOD_NAME_ALL_LOWERCASE } from \"./router.js\";\nimport { COMPOSED_HANDLER } from \"./utils/constants.js\";\nimport { getPath, getPathNoStrict, mergePath } from \"./utils/url.js\";\nvar notFoundHandler = (c) => {\n return c.text(\"404 Not Found\", 404);\n};\nvar errorHandler = (err, c) => {\n if (\"getResponse\" in err) {\n const res = err.getResponse();\n return c.newResponse(res.body, res);\n }\n console.error(err);\n return c.text(\"Internal Server Error\", 500);\n};\nvar Hono = class _Hono {\n get;\n post;\n put;\n delete;\n options;\n patch;\n all;\n on;\n use;\n /*\n This class is like an abstract class and does not have a router.\n To use it, inherit the class and implement router in the constructor.\n */\n router;\n getPath;\n // Cannot use `#` because it requires visibility at JavaScript runtime.\n _basePath = \"/\";\n #path = \"/\";\n routes = [];\n constructor(options = {}) {\n const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];\n allMethods.forEach((method) => {\n this[method] = (args1, ...args) => {\n if (typeof args1 === \"string\") {\n this.#path = args1;\n } else {\n this.#addRoute(method, this.#path, args1);\n }\n args.forEach((handler) => {\n this.#addRoute(method, this.#path, handler);\n });\n return this;\n };\n });\n this.on = (method, path, ...handlers) => {\n for (const p of [path].flat()) {\n this.#path = p;\n for (const m of [method].flat()) {\n handlers.map((handler) => {\n this.#addRoute(m.toUpperCase(), this.#path, handler);\n });\n }\n }\n return this;\n };\n this.use = (arg1, ...handlers) => {\n if (typeof arg1 === \"string\") {\n this.#path = arg1;\n } else {\n this.#path = \"*\";\n handlers.unshift(arg1);\n }\n handlers.forEach((handler) => {\n this.#addRoute(METHOD_NAME_ALL, this.#path, handler);\n });\n return this;\n };\n const { strict, ...optionsWithoutStrict } = options;\n Object.assign(this, optionsWithoutStrict);\n this.getPath = strict ?? true ? options.getPath ?? getPath : getPathNoStrict;\n }\n #clone() {\n const clone = new _Hono({\n router: this.router,\n getPath: this.getPath\n });\n clone.errorHandler = this.errorHandler;\n clone.#notFoundHandler = this.#notFoundHandler;\n clone.routes = this.routes;\n return clone;\n }\n #notFoundHandler = notFoundHandler;\n // Cannot use `#` because it requires visibility at JavaScript runtime.\n errorHandler = errorHandler;\n /**\n * `.route()` allows grouping other Hono instance in routes.\n *\n * @see {@link https://hono.dev/docs/api/routing#grouping}\n *\n * @param {string} path - base Path\n * @param {Hono} app - other Hono instance\n * @returns {Hono} routed Hono instance\n *\n * @example\n * ```ts\n * const app = new Hono()\n * const app2 = new Hono()\n *\n * app2.get(\"/user\", (c) => c.text(\"user\"))\n * app.route(\"/api\", app2) // GET /api/user\n * ```\n */\n route(path, app) {\n const subApp = this.basePath(path);\n app.routes.map((r) => {\n let handler;\n if (app.errorHandler === errorHandler) {\n handler = r.handler;\n } else {\n handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;\n handler[COMPOSED_HANDLER] = r.handler;\n }\n subApp.#addRoute(r.method, r.path, handler);\n });\n return this;\n }\n /**\n * `.basePath()` allows base paths to be specified.\n *\n * @see {@link https://hono.dev/docs/api/routing#base-path}\n *\n * @param {string} path - base Path\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * const api = new Hono().basePath('/api')\n * ```\n */\n basePath(path) {\n const subApp = this.#clone();\n subApp._basePath = mergePath(this._basePath, path);\n return subApp;\n }\n /**\n * `.onError()` handles an error and returns a customized Response.\n *\n * @see {@link https://hono.dev/docs/api/hono#error-handling}\n *\n * @param {ErrorHandler} handler - request Handler for error\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * app.onError((err, c) => {\n * console.error(`${err}`)\n * return c.text('Custom Error Message', 500)\n * })\n * ```\n */\n onError = (handler) => {\n this.errorHandler = handler;\n return this;\n };\n /**\n * `.notFound()` allows you to customize a Not Found Response.\n *\n * @see {@link https://hono.dev/docs/api/hono#not-found}\n *\n * @param {NotFoundHandler} handler - request handler for not-found\n * @returns {Hono} changed Hono instance\n *\n * @example\n * ```ts\n * app.notFound((c) => {\n * return c.text('Custom 404 Message', 404)\n * })\n * ```\n */\n notFound = (handler) => {\n this.#notFoundHandler = handler;\n return this;\n };\n /**\n * `.mount()` allows you to mount applications built with other frameworks into your Hono application.\n *\n * @see {@link https://hono.dev/docs/api/hono#mount}\n *\n * @param {string} path - base Path\n * @param {Function} applicationHandler - other Request Handler\n * @param {MountOptions} [options] - options of `.mount()`\n * @returns {Hono} mounted Hono instance\n *\n * @example\n * ```ts\n * import { Router as IttyRouter } from 'itty-router'\n * import { Hono } from 'hono'\n * // Create itty-router application\n * const ittyRouter = IttyRouter()\n * // GET /itty-router/hello\n * ittyRouter.get('/hello', () => new Response('Hello from itty-router'))\n *\n * const app = new Hono()\n * app.mount('/itty-router', ittyRouter.handle)\n * ```\n *\n * @example\n * ```ts\n * const app = new Hono()\n * // Send the request to another application without modification.\n * app.mount('/app', anotherApp, {\n * replaceRequest: (req) => req,\n * })\n * ```\n */\n mount(path, applicationHandler, options) {\n let replaceRequest;\n let optionHandler;\n if (options) {\n if (typeof options === \"function\") {\n optionHandler = options;\n } else {\n optionHandler = options.optionHandler;\n if (options.replaceRequest === false) {\n replaceRequest = (request) => request;\n } else {\n replaceRequest = options.replaceRequest;\n }\n }\n }\n const getOptions = optionHandler ? (c) => {\n const options2 = optionHandler(c);\n return Array.isArray(options2) ? options2 : [options2];\n } : (c) => {\n let executionContext = void 0;\n try {\n executionContext = c.executionCtx;\n } catch {\n }\n return [c.env, executionContext];\n };\n replaceRequest ||= (() => {\n const mergedPath = mergePath(this._basePath, path);\n const pathPrefixLength = mergedPath === \"/\" ? 0 : mergedPath.length;\n return (request) => {\n const url = new URL(request.url);\n url.pathname = url.pathname.slice(pathPrefixLength) || \"/\";\n return new Request(url, request);\n };\n })();\n const handler = async (c, next) => {\n const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));\n if (res) {\n return res;\n }\n await next();\n };\n this.#addRoute(METHOD_NAME_ALL, mergePath(path, \"*\"), handler);\n return this;\n }\n #addRoute(method, path, handler) {\n method = method.toUpperCase();\n path = mergePath(this._basePath, path);\n const r = { basePath: this._basePath, path, method, handler };\n this.router.add(method, path, [handler, r]);\n this.routes.push(r);\n }\n #handleError(err, c) {\n if (err instanceof Error) {\n return this.errorHandler(err, c);\n }\n throw err;\n }\n #dispatch(request, executionCtx, env, method) {\n if (method === \"HEAD\") {\n return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, \"GET\")))();\n }\n const path = this.getPath(request, { env });\n const matchResult = this.router.match(method, path);\n const c = new Context(request, {\n path,\n matchResult,\n env,\n executionCtx,\n notFoundHandler: this.#notFoundHandler\n });\n if (matchResult[0].length === 1) {\n let res;\n try {\n res = matchResult[0][0][0][0](c, async () => {\n c.res = await this.#notFoundHandler(c);\n });\n } catch (err) {\n return this.#handleError(err, c);\n }\n return res instanceof Promise ? res.then(\n (resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))\n ).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);\n }\n const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);\n return (async () => {\n try {\n const context = await composed(c);\n if (!context.finalized) {\n throw new Error(\n \"Context is not finalized. Did you forget to return a Response object or `await next()`?\"\n );\n }\n return context.res;\n } catch (err) {\n return this.#handleError(err, c);\n }\n })();\n }\n /**\n * `.fetch()` will be entry point of your app.\n *\n * @see {@link https://hono.dev/docs/api/hono#fetch}\n *\n * @param {Request} request - request Object of request\n * @param {Env} Env - env Object\n * @param {ExecutionContext} - context of execution\n * @returns {Response | Promise<Response>} response of request\n *\n */\n fetch = (request, ...rest) => {\n return this.#dispatch(request, rest[1], rest[0], request.method);\n };\n /**\n * `.request()` is a useful method for testing.\n * You can pass a URL or pathname to send a GET request.\n * app will return a Response object.\n * ```ts\n * test('GET /hello is ok', async () => {\n * const res = await app.request('/hello')\n * expect(res.status).toBe(200)\n * })\n * ```\n * @see https://hono.dev/docs/api/hono#request\n */\n request = (input, requestInit, Env, executionCtx) => {\n if (input instanceof Request) {\n return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);\n }\n input = input.toString();\n return this.fetch(\n new Request(\n /^https?:\\/\\//.test(input) ? input : `http://localhost${mergePath(\"/\", input)}`,\n requestInit\n ),\n Env,\n executionCtx\n );\n };\n /**\n * `.fire()` automatically adds a global fetch event listener.\n * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.\n * @deprecated\n * Use `fire` from `hono/service-worker` instead.\n * ```ts\n * import { Hono } from 'hono'\n * import { fire } from 'hono/service-worker'\n *\n * const app = new Hono()\n * // ...\n * fire(app)\n * ```\n * @see https://hono.dev/docs/api/hono#fire\n * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API\n * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/\n */\n fire = () => {\n addEventListener(\"fetch\", (event) => {\n event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method));\n });\n };\n};\nexport {\n Hono as HonoBase\n};\n","// src/router/reg-exp-router/matcher.ts\nimport { METHOD_NAME_ALL } from \"../../router.js\";\nvar emptyParam = [];\nfunction match(method, path) {\n const matchers = this.buildAllMatchers();\n const match2 = ((method2, path2) => {\n const matcher = matchers[method2] || matchers[METHOD_NAME_ALL];\n const staticMatch = matcher[2][path2];\n if (staticMatch) {\n return staticMatch;\n }\n const match3 = path2.match(matcher[0]);\n if (!match3) {\n return [[], emptyParam];\n }\n const index = match3.indexOf(\"\", 1);\n return [matcher[1][index], match3];\n });\n this.match = match2;\n return match2(method, path);\n}\nexport {\n emptyParam,\n match\n};\n","// src/router/reg-exp-router/node.ts\nvar LABEL_REG_EXP_STR = \"[^/]+\";\nvar ONLY_WILDCARD_REG_EXP_STR = \".*\";\nvar TAIL_WILDCARD_REG_EXP_STR = \"(?:|/.*)\";\nvar PATH_ERROR = /* @__PURE__ */ Symbol();\nvar regExpMetaChars = new Set(\".\\\\+*[^]$()\");\nfunction compareKey(a, b) {\n if (a.length === 1) {\n return b.length === 1 ? a < b ? -1 : 1 : -1;\n }\n if (b.length === 1) {\n return 1;\n }\n if (a === ONLY_WILDCARD_REG_EXP_STR || a === TAIL_WILDCARD_REG_EXP_STR) {\n return 1;\n } else if (b === ONLY_WILDCARD_REG_EXP_STR || b === TAIL_WILDCARD_REG_EXP_STR) {\n return -1;\n }\n if (a === LABEL_REG_EXP_STR) {\n return 1;\n } else if (b === LABEL_REG_EXP_STR) {\n return -1;\n }\n return a.length === b.length ? a < b ? -1 : 1 : b.length - a.length;\n}\nvar Node = class _Node {\n #index;\n #varIndex;\n #children = /* @__PURE__ */ Object.create(null);\n insert(tokens, index, paramMap, context, pathErrorCheckOnly) {\n if (tokens.length === 0) {\n if (this.#index !== void 0) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n this.#index = index;\n return;\n }\n const [token, ...restTokens] = tokens;\n const pattern = token === \"*\" ? restTokens.length === 0 ? [\"\", \"\", ONLY_WILDCARD_REG_EXP_STR] : [\"\", \"\", LABEL_REG_EXP_STR] : token === \"/*\" ? [\"\", \"\", TAIL_WILDCARD_REG_EXP_STR] : token.match(/^\\:([^\\{\\}]+)(?:\\{(.+)\\})?$/);\n let node;\n if (pattern) {\n const name = pattern[1];\n let regexpStr = pattern[2] || LABEL_REG_EXP_STR;\n if (name && pattern[2]) {\n if (regexpStr === \".*\") {\n throw PATH_ERROR;\n }\n regexpStr = regexpStr.replace(/^\\((?!\\?:)(?=[^)]+\\)$)/, \"(?:\");\n if (/\\((?!\\?:)/.test(regexpStr)) {\n throw PATH_ERROR;\n }\n }\n node = this.#children[regexpStr];\n if (!node) {\n if (Object.keys(this.#children).some(\n (k) => k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR\n )) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n node = this.#children[regexpStr] = new _Node();\n if (name !== \"\") {\n node.#varIndex = context.varIndex++;\n }\n }\n if (!pathErrorCheckOnly && name !== \"\") {\n paramMap.push([name, node.#varIndex]);\n }\n } else {\n node = this.#children[token];\n if (!node) {\n if (Object.keys(this.#children).some(\n (k) => k.length > 1 && k !== ONLY_WILDCARD_REG_EXP_STR && k !== TAIL_WILDCARD_REG_EXP_STR\n )) {\n throw PATH_ERROR;\n }\n if (pathErrorCheckOnly) {\n return;\n }\n node = this.#children[token] = new _Node();\n }\n }\n node.insert(restTokens, index, paramMap, context, pathErrorCheckOnly);\n }\n buildRegExpStr() {\n const childKeys = Object.keys(this.#children).sort(compareKey);\n const strList = childKeys.map((k) => {\n const c = this.#children[k];\n return (typeof c.#varIndex === \"number\" ? `(${k})@${c.#varIndex}` : regExpMetaChars.has(k) ? `\\\\${k}` : k) + c.buildRegExpStr();\n });\n if (typeof this.#index === \"number\") {\n strList.unshift(`#${this.#index}`);\n }\n if (strList.length === 0) {\n return \"\";\n }\n if (strList.length === 1) {\n return strList[0];\n }\n return \"(?:\" + strList.join(\"|\") + \")\";\n }\n};\nexport {\n Node,\n PATH_ERROR\n};\n","// src/router/reg-exp-router/trie.ts\nimport { Node } from \"./node.js\";\nvar Trie = class {\n #context = { varIndex: 0 };\n #root = new Node();\n insert(path, index, pathErrorCheckOnly) {\n const paramAssoc = [];\n const groups = [];\n for (let i = 0; ; ) {\n let replaced = false;\n path = path.replace(/\\{[^}]+\\}/g, (m) => {\n const mark = `@\\\\${i}`;\n groups[i] = [mark, m];\n i++;\n replaced = true;\n return mark;\n });\n if (!replaced) {\n break;\n }\n }\n const tokens = path.match(/(?::[^\\/]+)|(?:\\/\\*$)|./g) || [];\n for (let i = groups.length - 1; i >= 0; i--) {\n const [mark] = groups[i];\n for (let j = tokens.length - 1; j >= 0; j--) {\n if (tokens[j].indexOf(mark) !== -1) {\n tokens[j] = tokens[j].replace(mark, groups[i][1]);\n break;\n }\n }\n }\n this.#root.insert(tokens, index, paramAssoc, this.#context, pathErrorCheckOnly);\n return paramAssoc;\n }\n buildRegExp() {\n let regexp = this.#root.buildRegExpStr();\n if (regexp === \"\") {\n return [/^$/, [], []];\n }\n let captureIndex = 0;\n const indexReplacementMap = [];\n const paramReplacementMap = [];\n regexp = regexp.replace(/#(\\d+)|@(\\d+)|\\.\\*\\$/g, (_, handlerIndex, paramIndex) => {\n if (handlerIndex !== void 0) {\n indexReplacementMap[++captureIndex] = Number(handlerIndex);\n return \"$()\";\n }\n if (paramIndex !== void 0) {\n paramReplacementMap[Number(paramIndex)] = ++captureIndex;\n return \"\";\n }\n return \"\";\n });\n return [new RegExp(`^${regexp}`), indexReplacementMap, paramReplacementMap];\n }\n};\nexport {\n Trie\n};\n","// src/router/reg-exp-router/router.ts\nimport {\n MESSAGE_MATCHER_IS_ALREADY_BUILT,\n METHOD_NAME_ALL,\n UnsupportedPathError\n} from \"../../router.js\";\nimport { checkOptionalParameter } from \"../../utils/url.js\";\nimport { match, emptyParam } from \"./matcher.js\";\nimport { PATH_ERROR } from \"./node.js\";\nimport { Trie } from \"./trie.js\";\nvar nullMatcher = [/^$/, [], /* @__PURE__ */ Object.create(null)];\nvar wildcardRegExpCache = /* @__PURE__ */ Object.create(null);\nfunction buildWildcardRegExp(path) {\n return wildcardRegExpCache[path] ??= new RegExp(\n path === \"*\" ? \"\" : `^${path.replace(\n /\\/\\*$|([.\\\\+*[^\\]$()])/g,\n (_, metaChar) => metaChar ? `\\\\${metaChar}` : \"(?:|/.*)\"\n )}$`\n );\n}\nfunction clearWildcardRegExpCache() {\n wildcardRegExpCache = /* @__PURE__ */ Object.create(null);\n}\nfunction buildMatcherFromPreprocessedRoutes(routes) {\n const trie = new Trie();\n const handlerData = [];\n if (routes.length === 0) {\n return nullMatcher;\n }\n const routesWithStaticPathFlag = routes.map(\n (route) => [!/\\*|\\/:/.test(route[0]), ...route]\n ).sort(\n ([isStaticA, pathA], [isStaticB, pathB]) => isStaticA ? 1 : isStaticB ? -1 : pathA.length - pathB.length\n );\n const staticMap = /* @__PURE__ */ Object.create(null);\n for (let i = 0, j = -1, len = routesWithStaticPathFlag.length; i < len; i++) {\n const [pathErrorCheckOnly, path, handlers] = routesWithStaticPathFlag[i];\n if (pathErrorCheckOnly) {\n staticMap[path] = [handlers.map(([h]) => [h, /* @__PURE__ */ Object.create(null)]), emptyParam];\n } else {\n j++;\n }\n let paramAssoc;\n try {\n paramAssoc = trie.insert(path, j, pathErrorCheckOnly);\n } catch (e) {\n throw e === PATH_ERROR ? new UnsupportedPathError(path) : e;\n }\n if (pathErrorCheckOnly) {\n continue;\n }\n handlerData[j] = handlers.map(([h, paramCount]) => {\n const paramIndexMap = /* @__PURE__ */ Object.create(null);\n paramCount -= 1;\n for (; paramCount >= 0; paramCount--) {\n const [key, value] = paramAssoc[paramCount];\n paramIndexMap[key] = value;\n }\n return [h, paramIndexMap];\n });\n }\n const [regexp, indexReplacementMap, paramReplacementMap] = trie.buildRegExp();\n for (let i = 0, len = handlerData.length; i < len; i++) {\n for (let j = 0, len2 = handlerData[i].length; j < len2; j++) {\n const map = handlerData[i][j]?.[1];\n if (!map) {\n continue;\n }\n const keys = Object.keys(map);\n for (let k = 0, len3 = keys.length; k < len3; k++) {\n map[keys[k]] = paramReplacementMap[map[keys[k]]];\n }\n }\n }\n const handlerMap = [];\n for (const i in indexReplacementMap) {\n handlerMap[i] = handlerData[indexReplacementMap[i]];\n }\n return [regexp, handlerMap, staticMap];\n}\nfunction findMiddleware(middleware, path) {\n if (!middleware) {\n return void 0;\n }\n for (const k of Object.keys(middleware).sort((a, b) => b.length - a.length)) {\n if (buildWildcardRegExp(k).test(path)) {\n return [...middleware[k]];\n }\n }\n return void 0;\n}\nvar RegExpRouter = class {\n name = \"RegExpRouter\";\n #middleware;\n #routes;\n constructor() {\n this.#middleware = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };\n this.#routes = { [METHOD_NAME_ALL]: /* @__PURE__ */ Object.create(null) };\n }\n add(method, path, handler) {\n const middleware = this.#middleware;\n const routes = this.#routes;\n if (!middleware || !routes) {\n throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);\n }\n if (!middleware[method]) {\n ;\n [middleware, routes].forEach((handlerMap) => {\n handlerMap[method] = /* @__PURE__ */ Object.create(null);\n Object.keys(handlerMap[METHOD_NAME_ALL]).forEach((p) => {\n handlerMap[method][p] = [...handlerMap[METHOD_NAME_ALL][p]];\n });\n });\n }\n if (path === \"/*\") {\n path = \"*\";\n }\n const paramCount = (path.match(/\\/:/g) || []).length;\n if (/\\*$/.test(path)) {\n const re = buildWildcardRegExp(path);\n if (method === METHOD_NAME_ALL) {\n Object.keys(middleware).forEach((m) => {\n middleware[m][path] ||= findMiddleware(middleware[m], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];\n });\n } else {\n middleware[method][path] ||= findMiddleware(middleware[method], path) || findMiddleware(middleware[METHOD_NAME_ALL], path) || [];\n }\n Object.keys(middleware).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n Object.keys(middleware[m]).forEach((p) => {\n re.test(p) && middleware[m][p].push([handler, paramCount]);\n });\n }\n });\n Object.keys(routes).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n Object.keys(routes[m]).forEach(\n (p) => re.test(p) && routes[m][p].push([handler, paramCount])\n );\n }\n });\n return;\n }\n const paths = checkOptionalParameter(path) || [path];\n for (let i = 0, len = paths.length; i < len; i++) {\n const path2 = paths[i];\n Object.keys(routes).forEach((m) => {\n if (method === METHOD_NAME_ALL || method === m) {\n routes[m][path2] ||= [\n ...findMiddleware(middleware[m], path2) || findMiddleware(middleware[METHOD_NAME_ALL], path2) || []\n ];\n routes[m][path2].push([handler, paramCount - len + i + 1]);\n }\n });\n }\n }\n match = match;\n buildAllMatchers() {\n const matchers = /* @__PURE__ */ Object.create(null);\n Object.keys(this.#routes).concat(Object.keys(this.#middleware)).forEach((method) => {\n matchers[method] ||= this.#buildMatcher(method);\n });\n this.#middleware = this.#routes = void 0;\n clearWildcardRegExpCache();\n return matchers;\n }\n #buildMatcher(method) {\n const routes = [];\n let hasOwnRoute = method === METHOD_NAME_ALL;\n [this.#middleware, this.#routes].forEach((r) => {\n const ownRoute = r[method] ? Object.keys(r[method]).map((path) => [path, r[method][path]]) : [];\n if (ownRoute.length !== 0) {\n hasOwnRoute ||= true;\n routes.push(...ownRoute);\n } else if (method !== METHOD_NAME_ALL) {\n routes.push(\n ...Object.keys(r[METHOD_NAME_ALL]).map((path) => [path, r[METHOD_NAME_ALL][path]])\n );\n }\n });\n if (!hasOwnRoute) {\n return null;\n } else {\n return buildMatcherFromPreprocessedRoutes(routes);\n }\n }\n};\nexport {\n RegExpRouter\n};\n","// src/router/smart-router/router.ts\nimport { MESSAGE_MATCHER_IS_ALREADY_BUILT, UnsupportedPathError } from \"../../router.js\";\nvar SmartRouter = class {\n name = \"SmartRouter\";\n #routers = [];\n #routes = [];\n constructor(init) {\n this.#routers = init.routers;\n }\n add(method, path, handler) {\n if (!this.#routes) {\n throw new Error(MESSAGE_MATCHER_IS_ALREADY_BUILT);\n }\n this.#routes.push([method, path, handler]);\n }\n match(method, path) {\n if (!this.#routes) {\n throw new Error(\"Fatal error\");\n }\n const routers = this.#routers;\n const routes = this.#routes;\n const len = routers.length;\n let i = 0;\n let res;\n for (; i < len; i++) {\n const router = routers[i];\n try {\n for (let i2 = 0, len2 = routes.length; i2 < len2; i2++) {\n router.add(...routes[i2]);\n }\n res = router.match(method, path);\n } catch (e) {\n if (e instanceof UnsupportedPathError) {\n continue;\n }\n throw e;\n }\n this.match = router.match.bind(router);\n this.#routers = [router];\n this.#routes = void 0;\n break;\n }\n if (i === len) {\n throw new Error(\"Fatal error\");\n }\n this.name = `SmartRouter + ${this.activeRouter.name}`;\n return res;\n }\n get activeRouter() {\n if (this.#routes || this.#routers.length !== 1) {\n throw new Error(\"No active router has been determined yet.\");\n }\n return this.#routers[0];\n }\n};\nexport {\n SmartRouter\n};\n","// src/router/trie-router/node.ts\nimport { METHOD_NAME_ALL } from \"../../router.js\";\nimport { getPattern, splitPath, splitRoutingPath } from \"../../utils/url.js\";\nvar emptyParams = /* @__PURE__ */ Object.create(null);\nvar hasChildren = (children) => {\n for (const _ in children) {\n return true;\n }\n return false;\n};\nvar Node = class _Node {\n #methods;\n #children;\n #patterns;\n #order = 0;\n #params = emptyParams;\n constructor(method, handler, children) {\n this.#children = children || /* @__PURE__ */ Object.create(null);\n this.#methods = [];\n if (method && handler) {\n const m = /* @__PURE__ */ Object.create(null);\n m[method] = { handler, possibleKeys: [], score: 0 };\n this.#methods = [m];\n }\n this.#patterns = [];\n }\n insert(method, path, handler) {\n this.#order = ++this.#order;\n let curNode = this;\n const parts = splitRoutingPath(path);\n const possibleKeys = [];\n for (let i = 0, len = parts.length; i < len; i++) {\n const p = parts[i];\n const nextP = parts[i + 1];\n const pattern = getPattern(p, nextP);\n const key = Array.isArray(pattern) ? pattern[0] : p;\n if (key in curNode.#children) {\n curNode = curNode.#children[key];\n if (pattern) {\n possibleKeys.push(pattern[1]);\n }\n continue;\n }\n curNode.#children[key] = new _Node();\n if (pattern) {\n curNode.#patterns.push(pattern);\n possibleKeys.push(pattern[1]);\n }\n curNode = curNode.#children[key];\n }\n curNode.#methods.push({\n [method]: {\n handler,\n possibleKeys: possibleKeys.filter((v, i, a) => a.indexOf(v) === i),\n score: this.#order\n }\n });\n return curNode;\n }\n #pushHandlerSets(handlerSets, node, method, nodeParams, params) {\n for (let i = 0, len = node.#methods.length; i < len; i++) {\n const m = node.#methods[i];\n const handlerSet = m[method] || m[METHOD_NAME_ALL];\n const processedSet = {};\n if (handlerSet !== void 0) {\n handlerSet.params = /* @__PURE__ */ Object.create(null);\n handlerSets.push(handlerSet);\n if (nodeParams !== emptyParams || params && params !== emptyParams) {\n for (let i2 = 0, len2 = handlerSet.possibleKeys.length; i2 < len2; i2++) {\n const key = handlerSet.possibleKeys[i2];\n const processed = processedSet[handlerSet.score];\n handlerSet.params[key] = params?.[key] && !processed ? params[key] : nodeParams[key] ?? params?.[key];\n processedSet[handlerSet.score] = true;\n }\n }\n }\n }\n }\n search(method, path) {\n const handlerSets = [];\n this.#params = emptyParams;\n const curNode = this;\n let curNodes = [curNode];\n const parts = splitPath(path);\n const curNodesQueue = [];\n const len = parts.length;\n let partOffsets = null;\n for (let i = 0; i < len; i++) {\n const part = parts[i];\n const isLast = i === len - 1;\n const tempNodes = [];\n for (let j = 0, len2 = curNodes.length; j < len2; j++) {\n const node = curNodes[j];\n const nextNode = node.#children[part];\n if (nextNode) {\n nextNode.#params = node.#params;\n if (isLast) {\n if (nextNode.#children[\"*\"]) {\n this.#pushHandlerSets(handlerSets, nextNode.#children[\"*\"], method, node.#params);\n }\n this.#pushHandlerSets(handlerSets, nextNode, method, node.#params);\n } else {\n tempNodes.push(nextNode);\n }\n }\n for (let k = 0, len3 = node.#patterns.length; k < len3; k++) {\n const pattern = node.#patterns[k];\n const params = node.#params === emptyParams ? {} : { ...node.#params };\n if (pattern === \"*\") {\n const astNode = node.#children[\"*\"];\n if (astNode) {\n this.#pushHandlerSets(handlerSets, astNode, method, node.#params);\n astNode.#params = params;\n tempNodes.push(astNode);\n }\n continue;\n }\n const [key, name, matcher] = pattern;\n if (!part && !(matcher instanceof RegExp)) {\n continue;\n }\n const child = node.#children[key];\n if (matcher instanceof RegExp) {\n if (partOffsets === null) {\n partOffsets = new Array(len);\n let offset = path[0] === \"/\" ? 1 : 0;\n for (let p = 0; p < len; p++) {\n partOffsets[p] = offset;\n offset += parts[p].length + 1;\n }\n }\n const restPathString = path.substring(partOffsets[i]);\n const m = matcher.exec(restPathString);\n if (m) {\n params[name] = m[0];\n this.#pushHandlerSets(handlerSets, child, method, node.#params, params);\n if (hasChildren(child.#children)) {\n child.#params = params;\n const componentCount = m[0].match(/\\//)?.length ?? 0;\n const targetCurNodes = curNodesQueue[componentCount] ||= [];\n targetCurNodes.push(child);\n }\n continue;\n }\n }\n if (matcher === true || matcher.test(part)) {\n params[name] = part;\n if (isLast) {\n this.#pushHandlerSets(handlerSets, child, method, params, node.#params);\n if (child.#children[\"*\"]) {\n this.#pushHandlerSets(\n handlerSets,\n child.#children[\"*\"],\n method,\n params,\n node.#params\n );\n }\n } else {\n child.#params = params;\n tempNodes.push(child);\n }\n }\n }\n }\n const shifted = curNodesQueue.shift();\n curNodes = shifted ? tempNodes.concat(shifted) : tempNodes;\n }\n if (handlerSets.length > 1) {\n handlerSets.sort((a, b) => {\n return a.score - b.score;\n });\n }\n return [handlerSets.map(({ handler, params }) => [handler, params])];\n }\n};\nexport {\n Node\n};\n","// src/router/trie-router/router.ts\nimport { checkOptionalParameter } from \"../../utils/url.js\";\nimport { Node } from \"./node.js\";\nvar TrieRouter = class {\n name = \"TrieRouter\";\n #node;\n constructor() {\n this.#node = new Node();\n }\n add(method, path, handler) {\n const results = checkOptionalParameter(path);\n if (results) {\n for (let i = 0, len = results.length; i < len; i++) {\n this.#node.insert(method, results[i], handler);\n }\n return;\n }\n this.#node.insert(method, path, handler);\n }\n match(method, path) {\n return this.#node.search(method, path);\n }\n};\nexport {\n TrieRouter\n};\n","// src/hono.ts\nimport { HonoBase } from \"./hono-base.js\";\nimport { RegExpRouter } from \"./router/reg-exp-router/index.js\";\nimport { SmartRouter } from \"./router/smart-router/index.js\";\nimport { TrieRouter } from \"./router/trie-router/index.js\";\nvar Hono = class extends HonoBase {\n /**\n * Creates an instance of the Hono class.\n *\n * @param options - Optional configuration options for the Hono instance.\n */\n constructor(options = {}) {\n super(options);\n this.router = options.router ?? new SmartRouter({\n routers: [new RegExpRouter(), new TrieRouter()]\n });\n }\n};\nexport {\n Hono\n};\n","/**\n * Sync authentication: request signing, verification, and nonce management.\n *\n * Uses Node's native Ed25519 crypto for sign/verify and SHA-256 canonical\n * request hashing. No ssh-keygen shelling.\n */\n\nimport {\n\tcreateHash,\n\tcreatePrivateKey,\n\tcreatePublicKey,\n\trandomBytes,\n\tsign,\n\tverify,\n} from \"node:crypto\";\nimport { lt } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { loadPrivateKey } from \"./sync-identity.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Signature version. v1 = SSHSIG format (ssh-keygen era), v2 = raw Ed25519.\n *\n * v2 is a breaking change: Python peers using `ssh-keygen -Y verify` cannot\n * verify v2 signatures. Once all peers run the TS runtime, v1 support can\n * be removed. During migration, the verifier accepts both versions.\n */\nexport const SIGNATURE_VERSION = \"v2\";\nexport const DEFAULT_TIME_WINDOW_S = 300;\n\n// ---------------------------------------------------------------------------\n// Canonical request\n// ---------------------------------------------------------------------------\n\n/**\n * Build a canonical request buffer for signing/verification.\n *\n * SHA-256 hashes the body, then joins method/path/timestamp/nonce/bodyHash\n * with newlines and returns the UTF-8 encoded result.\n */\nexport function buildCanonicalRequest(\n\tmethod: string,\n\tpathWithQuery: string,\n\ttimestamp: string,\n\tnonce: string,\n\tbodyBytes: Buffer,\n): Buffer {\n\tconst bodyHash = createHash(\"sha256\").update(bodyBytes).digest(\"hex\");\n\tconst canonical = [method.toUpperCase(), pathWithQuery, timestamp, nonce, bodyHash].join(\"\\n\");\n\treturn Buffer.from(canonical, \"utf-8\");\n}\n\n// ---------------------------------------------------------------------------\n// Sign\n// ---------------------------------------------------------------------------\n\nexport interface SignRequestOptions {\n\tmethod: string;\n\turl: string;\n\tbodyBytes: Buffer;\n\tkeysDir?: string;\n\ttimestamp?: string;\n\tnonce?: string;\n}\n\n/**\n * Sign an HTTP request and return the auth headers.\n *\n * Uses Node's native Ed25519 crypto.sign() — no ssh-keygen shelling.\n * Returns X-Opencode-Timestamp, X-Opencode-Nonce, X-Opencode-Signature headers.\n */\nexport function signRequest(options: SignRequestOptions): Record<string, string> {\n\tconst ts = options.timestamp ?? String(Math.floor(Date.now() / 1000));\n\tconst nonceValue = options.nonce ?? randomBytes(16).toString(\"hex\");\n\n\tconst parsed = new URL(options.url);\n\tlet path = parsed.pathname || \"/\";\n\tif (parsed.search) {\n\t\tpath = `${path}${parsed.search}`;\n\t}\n\n\tconst canonical = buildCanonicalRequest(options.method, path, ts, nonceValue, options.bodyBytes);\n\n\tconst keyData = loadPrivateKey(options.keysDir);\n\tif (!keyData) {\n\t\tthrow new Error(\"private key missing\");\n\t}\n\n\t// Handle both OpenSSH format (existing keys) and PKCS8 PEM (newly generated)\n\tlet privateKeyObj: ReturnType<typeof createPrivateKey>;\n\ttry {\n\t\tprivateKeyObj = createPrivateKey(keyData);\n\t} catch {\n\t\tprivateKeyObj = createPrivateKey({ key: keyData, format: \"pem\", type: \"pkcs8\" });\n\t}\n\tconst signatureBytes = sign(null, canonical, privateKeyObj);\n\tconst signature = signatureBytes.toString(\"base64\");\n\n\treturn {\n\t\t\"X-Opencode-Timestamp\": ts,\n\t\t\"X-Opencode-Nonce\": nonceValue,\n\t\t\"X-Opencode-Signature\": `${SIGNATURE_VERSION}:${signature}`,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Verify\n// ---------------------------------------------------------------------------\n\nexport interface VerifySignatureOptions {\n\tmethod: string;\n\tpathWithQuery: string;\n\tbodyBytes: Buffer;\n\ttimestamp: string;\n\tnonce: string;\n\tsignature: string;\n\tpublicKey: string;\n\tdeviceId: string;\n\ttimeWindowS?: number;\n}\n\n/**\n * Verify a signed request.\n *\n * Checks timestamp freshness, signature version prefix, then uses\n * Node's native crypto.verify() with the sender's Ed25519 public key.\n */\nexport function verifySignature(options: VerifySignatureOptions): boolean {\n\tconst timeWindow = options.timeWindowS ?? DEFAULT_TIME_WINDOW_S;\n\n\t// Parse and validate timestamp — reject non-numeric strings (matches Python's int())\n\tif (!/^\\d+$/.test(options.timestamp)) return false;\n\tconst tsInt = Number.parseInt(options.timestamp, 10);\n\tif (Number.isNaN(tsInt)) return false;\n\n\tconst now = Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - tsInt) > timeWindow) return false;\n\n\t// Validate signature version prefix — accept both v1 and v2 during migration\n\tconst ACCEPTED_VERSIONS = [\"v1\", \"v2\"];\n\tconst colonIdx = options.signature.indexOf(\":\");\n\tif (colonIdx < 1) return false;\n\tconst sigVersion = options.signature.slice(0, colonIdx);\n\tif (!ACCEPTED_VERSIONS.includes(sigVersion)) return false;\n\n\tconst encoded = options.signature.slice(colonIdx + 1);\n\tif (!encoded) return false;\n\n\tlet signatureBytes: Buffer;\n\ttry {\n\t\tsignatureBytes = Buffer.from(encoded, \"base64\");\n\t\t// Verify it was valid base64 by round-tripping\n\t\tif (signatureBytes.toString(\"base64\") !== encoded) return false;\n\t} catch {\n\t\treturn false;\n\t}\n\n\tconst canonical = buildCanonicalRequest(\n\t\toptions.method,\n\t\toptions.pathWithQuery,\n\t\toptions.timestamp,\n\t\toptions.nonce,\n\t\toptions.bodyBytes,\n\t);\n\n\ttry {\n\t\tconst publicKeyObj = sshEd25519ToPublicKey(options.publicKey);\n\t\treturn verify(null, canonical, publicKeyObj, signatureBytes);\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Parse an SSH ed25519 public key string into a Node crypto KeyObject.\n *\n * SSH format: \"ssh-ed25519 <base64-wire-format>\"\n * Wire format: uint32 key-type-len + key-type + uint32 key-data-len + key-data\n */\nfunction sshEd25519ToPublicKey(sshPub: string): ReturnType<typeof createPublicKey> {\n\tconst parts = sshPub.trim().split(/\\s+/);\n\tif (parts.length < 2 || parts[0] !== \"ssh-ed25519\") {\n\t\tthrow new Error(\"not an ssh-ed25519 key\");\n\t}\n\tconst wireFormat = Buffer.from(parts[1]!, \"base64\");\n\n\t// Read key type length\n\tif (wireFormat.length < 4) throw new Error(\"truncated wire format\");\n\tconst typeLen = wireFormat.readUInt32BE(0);\n\tconst typeEnd = 4 + typeLen;\n\tif (wireFormat.length < typeEnd + 4) throw new Error(\"truncated wire format\");\n\n\t// Read key data length\n\tconst keyLen = wireFormat.readUInt32BE(typeEnd);\n\tconst keyStart = typeEnd + 4;\n\tif (wireFormat.length < keyStart + keyLen) throw new Error(\"truncated wire format\");\n\tconst rawKey = wireFormat.subarray(keyStart, keyStart + keyLen);\n\n\tif (rawKey.length !== 32) throw new Error(`unexpected Ed25519 key length: ${rawKey.length}`);\n\n\t// Wrap raw 32-byte key in SPKI DER: 12-byte Ed25519 header + 32-byte key\n\tconst ed25519SpkiPrefix = Buffer.from(\"302a300506032b6570032100\", \"hex\");\n\tconst spkiDer = Buffer.concat([ed25519SpkiPrefix, rawKey]);\n\n\treturn createPublicKey({ key: spkiDer, format: \"der\", type: \"spki\" });\n}\n\n// ---------------------------------------------------------------------------\n// Auth headers (convenience)\n// ---------------------------------------------------------------------------\n\nexport interface BuildAuthHeadersOptions {\n\tdeviceId: string;\n\tmethod: string;\n\turl: string;\n\tbodyBytes: Buffer;\n\tkeysDir?: string;\n\ttimestamp?: string;\n\tnonce?: string;\n}\n\n/**\n * Build full auth headers including device ID and request signature.\n */\nexport function buildAuthHeaders(options: BuildAuthHeadersOptions): Record<string, string> {\n\treturn {\n\t\t\"X-Opencode-Device\": options.deviceId,\n\t\t...signRequest({\n\t\t\tmethod: options.method,\n\t\t\turl: options.url,\n\t\t\tbodyBytes: options.bodyBytes,\n\t\t\tkeysDir: options.keysDir,\n\t\t\ttimestamp: options.timestamp,\n\t\t\tnonce: options.nonce,\n\t\t}),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Nonce management\n// ---------------------------------------------------------------------------\n\n/**\n * Record a nonce to prevent replay attacks.\n *\n * Returns true on success, false if the nonce was already recorded\n * (duplicate = potential replay).\n */\nexport function recordNonce(\n\tdb: Database,\n\tdeviceId: string,\n\tnonce: string,\n\tcreatedAt: string,\n): boolean {\n\tconst d = drizzle(db, { schema });\n\ttry {\n\t\td.insert(schema.syncNonces).values({ nonce, device_id: deviceId, created_at: createdAt }).run();\n\t\treturn true;\n\t} catch (err: unknown) {\n\t\tif (err instanceof Error && err.message.includes(\"UNIQUE constraint failed\")) {\n\t\t\treturn false;\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n/**\n * Remove nonces older than the given cutoff timestamp.\n */\nexport function cleanupNonces(db: Database, cutoff: string): void {\n\tconst d = drizzle(db, { schema });\n\td.delete(schema.syncNonces).where(lt(schema.syncNonces.created_at, cutoff)).run();\n}\n","/**\n * Coordinator API — Hono-based HTTP server for the coordinator relay.\n *\n * Manages device enrollment, presence, invites, and join requests.\n * Ported from codemem/coordinator_api.py.\n */\n\nimport type { Context } from \"hono\";\nimport { Hono } from \"hono\";\nimport type { InvitePayload } from \"./coordinator-invites.js\";\nimport { encodeInvitePayload, inviteLink } from \"./coordinator-invites.js\";\nimport { CoordinatorStore } from \"./coordinator-store.js\";\nimport { DEFAULT_TIME_WINDOW_S, verifySignature } from \"./sync-auth.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst MAX_BODY_BYTES = 64 * 1024;\nconst ADMIN_HEADER = \"X-Codemem-Coordinator-Admin\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction adminSecret(): string | null {\n\tconst value = (process.env.CODEMEM_SYNC_COORDINATOR_ADMIN_SECRET ?? \"\").trim();\n\treturn value || null;\n}\n\nfunction authorizeAdmin(headerValue: string | null | undefined): { ok: boolean; error: string } {\n\tconst expected = adminSecret();\n\tif (!expected) return { ok: false, error: \"admin_not_configured\" };\n\tconst provided = (headerValue ?? \"\").trim();\n\tif (!provided) return { ok: false, error: \"missing_admin_header\" };\n\tif (provided !== expected) return { ok: false, error: \"invalid_admin_secret\" };\n\treturn { ok: true, error: \"ok\" };\n}\n\n/** Extract path + query string from a full URL for signature verification. */\nfunction pathWithQuery(url: string): string {\n\tconst parsed = new URL(url);\n\treturn parsed.search ? `${parsed.pathname}${parsed.search}` : parsed.pathname;\n}\n\nfunction recordNonce(\n\tstore: CoordinatorStore,\n\tdeviceId: string,\n\tnonce: string,\n\tcreatedAt: string,\n): boolean {\n\ttry {\n\t\tstore.db\n\t\t\t.prepare(\"INSERT INTO request_nonces(device_id, nonce, created_at) VALUES (?, ?, ?)\")\n\t\t\t.run(deviceId, nonce, createdAt);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction cleanupNonces(store: CoordinatorStore, cutoff: string): void {\n\tstore.db.prepare(\"DELETE FROM request_nonces WHERE created_at < ?\").run(cutoff);\n}\n\ninterface AuthResult {\n\tok: boolean;\n\terror: string;\n\tenrollment: Record<string, unknown> | null;\n}\n\nfunction authorizeRequest(\n\tstore: CoordinatorStore,\n\topts: {\n\t\tmethod: string;\n\t\turl: string;\n\t\tgroupId: string;\n\t\tbody: Buffer;\n\t\tdeviceId: string | null;\n\t\tsignature: string | null;\n\t\ttimestamp: string | null;\n\t\tnonce: string | null;\n\t},\n): AuthResult {\n\tconst { deviceId, signature, timestamp, nonce } = opts;\n\tif (!deviceId || !signature || !timestamp || !nonce) {\n\t\treturn { ok: false, error: \"missing_headers\", enrollment: null };\n\t}\n\n\tconst enrollment = store.getEnrollment(opts.groupId, deviceId);\n\tif (!enrollment) {\n\t\treturn { ok: false, error: \"unknown_device\", enrollment: null };\n\t}\n\n\tlet valid: boolean;\n\ttry {\n\t\tvalid = verifySignature({\n\t\t\tmethod: opts.method,\n\t\t\tpathWithQuery: pathWithQuery(opts.url),\n\t\t\tbodyBytes: opts.body,\n\t\t\ttimestamp,\n\t\t\tnonce,\n\t\t\tsignature,\n\t\t\tpublicKey: String(enrollment.public_key),\n\t\t\tdeviceId,\n\t\t});\n\t} catch {\n\t\treturn { ok: false, error: \"signature_verification_error\", enrollment: null };\n\t}\n\n\tif (!valid) {\n\t\treturn { ok: false, error: \"invalid_signature\", enrollment: null };\n\t}\n\n\tconst createdAt = new Date().toISOString();\n\tif (!recordNonce(store, deviceId, nonce, createdAt)) {\n\t\treturn { ok: false, error: \"nonce_replay\", enrollment: null };\n\t}\n\n\tconst cutoff = new Date(Date.now() - DEFAULT_TIME_WINDOW_S * 2 * 1000).toISOString();\n\tcleanupNonces(store, cutoff);\n\n\treturn { ok: true, error: \"ok\", enrollment };\n}\n\n// ---------------------------------------------------------------------------\n// App factory\n// ---------------------------------------------------------------------------\n\nexport function createCoordinatorApp(opts?: { dbPath?: string }): InstanceType<typeof Hono> {\n\tconst dbPath = opts?.dbPath;\n\tconst app = new Hono();\n\n\t// -----------------------------------------------------------------------\n\t// POST /v1/presence — upsert device presence (authenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.post(\"/v1/presence\", async (c) => {\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) {\n\t\t\treturn c.json({ error: \"body_too_large\" }, 413);\n\t\t}\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tif (!groupId) {\n\t\t\treturn c.json({ error: \"group_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst auth = authorizeRequest(store, {\n\t\t\t\tmethod: c.req.method,\n\t\t\t\turl: c.req.url,\n\t\t\t\tgroupId,\n\t\t\t\tbody: raw,\n\t\t\t\tdeviceId: c.req.header(\"X-Opencode-Device\") ?? null,\n\t\t\t\tsignature: c.req.header(\"X-Opencode-Signature\") ?? null,\n\t\t\t\ttimestamp: c.req.header(\"X-Opencode-Timestamp\") ?? null,\n\t\t\t\tnonce: c.req.header(\"X-Opencode-Nonce\") ?? null,\n\t\t\t});\n\t\t\tif (!auth.ok || !auth.enrollment) {\n\t\t\t\treturn c.json({ error: auth.error }, 401);\n\t\t\t}\n\n\t\t\tif (data.fingerprint && String(data.fingerprint) !== String(auth.enrollment.fingerprint)) {\n\t\t\t\treturn c.json({ error: \"fingerprint_mismatch\" }, 401);\n\t\t\t}\n\n\t\t\tconst rawAddresses = data.addresses ?? [];\n\t\t\tif (!Array.isArray(rawAddresses) || !rawAddresses.every((item) => typeof item === \"string\")) {\n\t\t\t\treturn c.json({ error: \"addresses_must_be_list_of_strings\" }, 400);\n\t\t\t}\n\n\t\t\tlet ttlS: number;\n\t\t\ttry {\n\t\t\t\tttlS = Math.max(1, Number.parseInt(String(data.ttl_s ?? 180), 10));\n\t\t\t\tif (Number.isNaN(ttlS)) {\n\t\t\t\t\treturn c.json({ error: \"ttl_s_must_be_int\" }, 400);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\treturn c.json({ error: \"ttl_s_must_be_int\" }, 400);\n\t\t\t}\n\n\t\t\tconst response = store.upsertPresence({\n\t\t\t\tgroupId,\n\t\t\t\tdeviceId: String(auth.enrollment.device_id),\n\t\t\t\taddresses: rawAddresses as string[],\n\t\t\t\tttlS,\n\t\t\t\tcapabilities:\n\t\t\t\t\ttypeof data.capabilities === \"object\" &&\n\t\t\t\t\tdata.capabilities !== null &&\n\t\t\t\t\t!Array.isArray(data.capabilities)\n\t\t\t\t\t\t? (data.capabilities as Record<string, unknown>)\n\t\t\t\t\t\t: undefined,\n\t\t\t});\n\n\t\t\treturn c.json({ ok: true, ...response });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// GET /v1/peers — list group peers (authenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.get(\"/v1/peers\", (c) => {\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) {\n\t\t\treturn c.json({ error: \"group_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst auth = authorizeRequest(store, {\n\t\t\t\tmethod: c.req.method,\n\t\t\t\turl: c.req.url,\n\t\t\t\tgroupId,\n\t\t\t\tbody: Buffer.alloc(0),\n\t\t\t\tdeviceId: c.req.header(\"X-Opencode-Device\") ?? null,\n\t\t\t\tsignature: c.req.header(\"X-Opencode-Signature\") ?? null,\n\t\t\t\ttimestamp: c.req.header(\"X-Opencode-Timestamp\") ?? null,\n\t\t\t\tnonce: c.req.header(\"X-Opencode-Nonce\") ?? null,\n\t\t\t});\n\t\t\tif (!auth.ok || !auth.enrollment) {\n\t\t\t\treturn c.json({ error: auth.error }, 401);\n\t\t\t}\n\n\t\t\tconst items = store.listGroupPeers(groupId, String(auth.enrollment.device_id));\n\t\t\treturn c.json({ items });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// Admin routes\n\t// -----------------------------------------------------------------------\n\n\t// POST /v1/admin/devices — enroll a device\n\tapp.post(\"/v1/admin/devices\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst fingerprint = String(data.fingerprint ?? \"\").trim();\n\t\tconst publicKey = String(data.public_key ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim() || null;\n\n\t\tif (!groupId || !deviceId || !fingerprint || !publicKey) {\n\t\t\treturn c.json({ error: \"group_id_device_id_fingerprint_public_key_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tstore.createGroup(groupId);\n\t\t\tstore.enrollDevice(groupId, {\n\t\t\t\tdeviceId,\n\t\t\t\tfingerprint,\n\t\t\t\tpublicKey,\n\t\t\t\tdisplayName,\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\n\t\treturn c.json({ ok: true });\n\t});\n\n\t// GET /v1/admin/devices — list enrolled devices\n\tapp.get(\"/v1/admin/devices\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst includeDisabled = [\"1\", \"true\", \"yes\"].includes(\n\t\t\t(c.req.query(\"include_disabled\") ?? \"0\").trim().toLowerCase(),\n\t\t);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\treturn c.json({ items: store.listEnrolledDevices(groupId, includeDisabled) });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/rename\n\tapp.post(\"/v1/admin/devices/rename\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\t\tif (!displayName) {\n\t\t\treturn c.json({ error: \"display_name_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.renameDevice(groupId, deviceId, displayName);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/disable\n\tapp.post(\"/v1/admin/devices/disable\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.setDeviceEnabled(groupId, deviceId, false);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/devices/remove\n\tapp.post(\"/v1/admin/devices/remove\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\n\t\tif (!groupId || !deviceId) {\n\t\t\treturn c.json({ error: \"group_id_and_device_id_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst ok = store.removeDevice(groupId, deviceId);\n\t\t\tif (!ok) return c.json({ error: \"device_not_found\" }, 404);\n\t\t\treturn c.json({ ok: true });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/invites — create an invite\n\tapp.post(\"/v1/admin/invites\", async (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst groupId = String(data.group_id ?? \"\").trim();\n\t\tconst policy = String(data.policy ?? \"auto_admit\").trim();\n\t\tconst expiresAt = String(data.expires_at ?? \"\").trim();\n\t\tconst createdBy = String(data.created_by ?? \"\").trim() || null;\n\n\t\tif (!groupId || ![\"auto_admit\", \"approval_required\"].includes(policy) || !expiresAt) {\n\t\t\treturn c.json({ error: \"group_id_policy_and_expires_at_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst group = store.getGroup(groupId);\n\t\t\tif (!group) return c.json({ error: \"group_not_found\" }, 404);\n\n\t\t\tconst invite = store.createInvite({\n\t\t\t\tgroupId,\n\t\t\t\tpolicy,\n\t\t\t\texpiresAt,\n\t\t\t\tcreatedBy,\n\t\t\t});\n\n\t\t\tconst payload: InvitePayload = {\n\t\t\t\tv: 1,\n\t\t\t\tkind: \"coordinator_team_invite\",\n\t\t\t\tcoordinator_url: String(data.coordinator_url ?? \"\").trim(),\n\t\t\t\tgroup_id: groupId,\n\t\t\t\tpolicy,\n\t\t\t\ttoken: String(invite.token ?? \"\"),\n\t\t\t\texpires_at: expiresAt,\n\t\t\t\tteam_name: (invite.team_name_snapshot as string) ?? null,\n\t\t\t};\n\t\t\tconst encoded = encodeInvitePayload(payload);\n\n\t\t\t// Omit token from the returned invite object (matches Python)\n\t\t\tconst { token: _token, ...inviteWithoutToken } = invite;\n\n\t\t\treturn c.json({\n\t\t\t\tok: true,\n\t\t\t\tinvite: inviteWithoutToken,\n\t\t\t\tpayload,\n\t\t\t\tencoded,\n\t\t\t\tlink: inviteLink(encoded),\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// GET /v1/admin/invites — list invites\n\tapp.get(\"/v1/admin/invites\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst rows = store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT invite_id, group_id, policy, expires_at, created_at, created_by, team_name_snapshot, revoked_at\n\t\t\t\t\t FROM coordinator_invites\n\t\t\t\t\t WHERE group_id = ?\n\t\t\t\t\t ORDER BY created_at DESC`,\n\t\t\t\t)\n\t\t\t\t.all(groupId) as Record<string, unknown>[];\n\t\t\treturn c.json({ items: rows });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// POST /v1/admin/join-requests/approve\n\tapp.post(\"/v1/admin/join-requests/approve\", async (c) => {\n\t\treturn handleJoinRequestReview(c, true, dbPath);\n\t});\n\n\t// POST /v1/admin/join-requests/deny\n\tapp.post(\"/v1/admin/join-requests/deny\", async (c) => {\n\t\treturn handleJoinRequestReview(c, false, dbPath);\n\t});\n\n\t// GET /v1/admin/join-requests — list join requests\n\tapp.get(\"/v1/admin/join-requests\", (c) => {\n\t\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\t\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\t\tconst groupId = (c.req.query(\"group_id\") ?? \"\").trim();\n\t\tif (!groupId) return c.json({ error: \"group_id_required\" }, 400);\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\treturn c.json({ items: store.listJoinRequests(groupId) });\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\t// -----------------------------------------------------------------------\n\t// POST /v1/join — join via invite token (unauthenticated)\n\t// -----------------------------------------------------------------------\n\n\tapp.post(\"/v1/join\", async (c) => {\n\t\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\t\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\t\tlet data: Record<string, unknown>;\n\t\ttry {\n\t\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t\t}\n\t\t} catch {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\n\t\tconst token = String(data.token ?? \"\").trim();\n\t\tconst deviceId = String(data.device_id ?? \"\").trim();\n\t\tconst fingerprint = String(data.fingerprint ?? \"\").trim();\n\t\tconst publicKey = String(data.public_key ?? \"\").trim();\n\t\tconst displayName = String(data.display_name ?? \"\").trim() || null;\n\n\t\tif (!token || !deviceId || !fingerprint || !publicKey) {\n\t\t\treturn c.json({ error: \"token_device_id_fingerprint_public_key_required\" }, 400);\n\t\t}\n\n\t\tconst store = new CoordinatorStore(dbPath);\n\t\ttry {\n\t\t\tconst invite = store.getInviteByToken(token);\n\t\t\tif (!invite) return c.json({ error: \"invalid_token\" }, 404);\n\n\t\t\tif (invite.revoked_at) return c.json({ error: \"revoked_token\" }, 400);\n\n\t\t\tconst expiresAtStr = String(invite.expires_at ?? \"\");\n\t\t\tif (expiresAtStr) {\n\t\t\t\tconst expiresAt = new Date(expiresAtStr.replace(\"Z\", \"+00:00\"));\n\t\t\t\tif (expiresAt <= new Date()) {\n\t\t\t\t\treturn c.json({ error: \"expired_token\" }, 400);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst inviteGroupId = String(invite.group_id);\n\t\t\tconst existing = store.getEnrollment(inviteGroupId, deviceId);\n\t\t\tif (existing) {\n\t\t\t\treturn c.json({\n\t\t\t\t\tok: true,\n\t\t\t\t\tstatus: \"already_enrolled\",\n\t\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\t\tpolicy: invite.policy,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst invitePolicy = String(invite.policy);\n\t\t\tif (![\"auto_admit\", \"approval_required\"].includes(invitePolicy)) {\n\t\t\t\treturn c.json({ error: `unknown invite policy: ${invitePolicy}` }, 400);\n\t\t\t}\n\n\t\t\tif (invitePolicy === \"approval_required\") {\n\t\t\t\tconst request = store.createJoinRequest({\n\t\t\t\t\tgroupId: inviteGroupId,\n\t\t\t\t\tdeviceId,\n\t\t\t\t\tpublicKey,\n\t\t\t\t\tfingerprint,\n\t\t\t\t\tdisplayName,\n\t\t\t\t\ttoken,\n\t\t\t\t});\n\t\t\t\treturn c.json({\n\t\t\t\t\tok: true,\n\t\t\t\t\tstatus: \"pending\",\n\t\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\t\tpolicy: invite.policy,\n\t\t\t\t\trequest_id: request.request_id,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tstore.enrollDevice(inviteGroupId, {\n\t\t\t\tdeviceId,\n\t\t\t\tfingerprint,\n\t\t\t\tpublicKey,\n\t\t\t\tdisplayName,\n\t\t\t});\n\n\t\t\treturn c.json({\n\t\t\t\tok: true,\n\t\t\t\tstatus: \"enrolled\",\n\t\t\t\tgroup_id: invite.group_id,\n\t\t\t\tpolicy: invite.policy,\n\t\t\t});\n\t\t} finally {\n\t\t\tstore.close();\n\t\t}\n\t});\n\n\treturn app;\n}\n\n// ---------------------------------------------------------------------------\n// Shared handler for approve/deny\n// ---------------------------------------------------------------------------\n\nasync function handleJoinRequestReview(c: Context, approved: boolean, dbPath: string | undefined) {\n\tconst adminAuth = authorizeAdmin(c.req.header(ADMIN_HEADER));\n\tif (!adminAuth.ok) return c.json({ error: adminAuth.error }, 401);\n\n\tconst raw = Buffer.from(await c.req.arrayBuffer());\n\tif (raw.length > MAX_BODY_BYTES) return c.json({ error: \"body_too_large\" }, 413);\n\n\tlet data: Record<string, unknown>;\n\ttry {\n\t\tdata = JSON.parse(raw.toString(\"utf-8\"));\n\t\tif (typeof data !== \"object\" || data === null || Array.isArray(data)) {\n\t\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t\t}\n\t} catch {\n\t\treturn c.json({ error: \"invalid_json\" }, 400);\n\t}\n\n\tconst requestId = String(data.request_id ?? \"\").trim();\n\tconst reviewedBy = String(data.reviewed_by ?? \"\").trim() || null;\n\n\tif (!requestId) return c.json({ error: \"request_id_required\" }, 400);\n\n\tconst store = new CoordinatorStore(dbPath);\n\ttry {\n\t\tconst request = store.reviewJoinRequest({\n\t\t\trequestId,\n\t\t\tapproved,\n\t\t\treviewedBy,\n\t\t});\n\n\t\tif (!request) return c.json({ error: \"request_not_found\" }, 404);\n\n\t\tif (request._no_transition) {\n\t\t\treturn c.json({ error: \"request_not_pending\", status: request.status }, 409);\n\t\t}\n\n\t\treturn c.json({ ok: true, request });\n\t} finally {\n\t\tstore.close();\n\t}\n}\n","/**\n * Shared address normalization utilities for the sync system.\n *\n * Used by both coordinator-store and sync-discovery to ensure\n * consistent address handling across the sync pipeline.\n */\n\n/**\n * Normalize an address to a consistent URL form.\n *\n * Uses the built-in URL constructor for consistent normalization:\n * lowercases host, strips default ports (80/443), trims trailing slashes.\n * Returns empty string for invalid/empty input.\n */\nexport function normalizeAddress(address: string): string {\n\tconst value = address.trim();\n\tif (!value) return \"\";\n\n\tconst withScheme = value.includes(\"://\") ? value : `http://${value}`;\n\n\ttry {\n\t\tconst url = new URL(withScheme);\n\t\tif (!url.hostname) return \"\";\n\t\tif (url.port && (Number(url.port) <= 0 || Number(url.port) > 65535)) return \"\";\n\t\treturn url.origin + url.pathname.replace(/\\/+$/, \"\");\n\t} catch {\n\t\treturn \"\";\n\t}\n}\n\n/**\n * Produce a dedup key for an address (strips http scheme for comparison).\n */\nexport function addressDedupeKey(address: string): string {\n\tif (!address) return \"\";\n\ttry {\n\t\tconst url = new URL(address);\n\t\tconst host = url.hostname.toLowerCase();\n\t\tif (host && url.port) return `${host}:${url.port}`;\n\t\tif (host) return host;\n\t} catch {\n\t\t// Not parseable\n\t}\n\treturn address;\n}\n\n/**\n * Merge two address lists, normalizing and deduplicating.\n * Existing addresses come first, then new candidates.\n */\nexport function mergeAddresses(existing: string[], candidates: string[]): string[] {\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const address of [...existing, ...candidates]) {\n\t\tconst cleaned = normalizeAddress(address);\n\t\tconst key = addressDedupeKey(cleaned);\n\t\tif (!cleaned || seen.has(key)) continue;\n\t\tseen.add(key);\n\t\tnormalized.push(cleaned);\n\t}\n\treturn normalized;\n}\n","import { networkInterfaces } from \"node:os\";\nimport { mergeAddresses } from \"./address-utils.js\";\nimport { getCodememEnvOverrides, readCodememConfigFile } from \"./observer-config.js\";\nimport type { MemoryStore } from \"./store.js\";\nimport { buildAuthHeaders } from \"./sync-auth.js\";\nimport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity, loadPublicKey } from \"./sync-identity.js\";\n\ntype ConfigRecord = Record<string, unknown>;\n\nfunction clean(value: unknown): string {\n\treturn typeof value === \"string\" ? value.trim() : \"\";\n}\n\nfunction parseIntOr(value: unknown, fallback: number): number {\n\tif (typeof value === \"number\" && Number.isFinite(value)) return Math.trunc(value);\n\tif (typeof value === \"string\" && /^-?\\d+$/.test(value.trim()))\n\t\treturn Number.parseInt(value.trim(), 10);\n\treturn fallback;\n}\n\nfunction parseBoolOr(value: unknown, fallback: boolean): boolean {\n\tif (typeof value === \"boolean\") return value;\n\tif (typeof value === \"string\") {\n\t\tconst normalized = value.trim().toLowerCase();\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(normalized)) return true;\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(normalized)) return false;\n\t}\n\treturn fallback;\n}\n\nfunction parseStringList(value: unknown): string[] {\n\tif (Array.isArray(value)) {\n\t\treturn value\n\t\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean);\n\t}\n\tif (typeof value === \"string\") {\n\t\treturn value\n\t\t\t.split(\",\")\n\t\t\t.map((item) => item.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn [];\n}\n\nexport interface CoordinatorSyncConfig {\n\tsyncEnabled: boolean;\n\tsyncHost: string;\n\tsyncPort: number;\n\tsyncIntervalS: number;\n\tsyncAdvertise: string;\n\tsyncProjectsInclude: string[];\n\tsyncProjectsExclude: string[];\n\tsyncCoordinatorUrl: string;\n\tsyncCoordinatorGroup: string;\n\tsyncCoordinatorGroups: string[];\n\tsyncCoordinatorTimeoutS: number;\n\tsyncCoordinatorPresenceTtlS: number;\n\tsyncCoordinatorAdminSecret: string;\n}\n\ntype PresenceStatus = \"posted\" | \"not_enrolled\" | \"error\";\n\ninterface PresenceSnapshot {\n\tstatus: PresenceStatus;\n\terror: string | null;\n\tadvertisedAddresses: unknown;\n\tnextRefreshAtMs: number;\n}\n\nconst coordinatorPresenceCache = new Map<string, PresenceSnapshot>();\n\nfunction presenceCacheKey(store: MemoryStore, config: CoordinatorSyncConfig): string {\n\tconst groups = [...config.syncCoordinatorGroups].sort().join(\",\");\n\treturn `${store.dbPath}|${config.syncCoordinatorUrl}|${groups}`;\n}\n\nfunction presenceRefreshIntervalMs(config: CoordinatorSyncConfig): number {\n\tconst ttl = Math.max(1, config.syncCoordinatorPresenceTtlS);\n\tconst halfTtl = Math.floor(ttl / 2);\n\tconst refreshS = Math.max(5, Math.min(60, halfTtl > 0 ? halfTtl : 1));\n\treturn refreshS * 1000;\n}\n\nfunction presenceRetryIntervalMs(): number {\n\treturn 30_000;\n}\n\nexport function readCoordinatorSyncConfig(config?: ConfigRecord): CoordinatorSyncConfig {\n\tconst raw = { ...(config ?? readCodememConfigFile()) } as ConfigRecord;\n\tconst envOverrides = getCodememEnvOverrides();\n\tfor (const key of Object.keys(envOverrides)) {\n\t\tconst value = process.env[envOverrides[key] as string];\n\t\tif (value != null) raw[key] = value;\n\t}\n\tconst syncCoordinatorGroup = clean(raw.sync_coordinator_group);\n\tconst syncCoordinatorGroups = parseStringList(raw.sync_coordinator_groups);\n\treturn {\n\t\tsyncEnabled: parseBoolOr(raw.sync_enabled, false),\n\t\tsyncHost: clean(raw.sync_host) || \"0.0.0.0\",\n\t\tsyncPort: parseIntOr(raw.sync_port, 7337),\n\t\tsyncIntervalS: parseIntOr(raw.sync_interval_s, 120),\n\t\tsyncAdvertise: clean(raw.sync_advertise) || \"auto\",\n\t\tsyncProjectsInclude: parseStringList(raw.sync_projects_include),\n\t\tsyncProjectsExclude: parseStringList(raw.sync_projects_exclude),\n\t\tsyncCoordinatorUrl: clean(raw.sync_coordinator_url),\n\t\tsyncCoordinatorGroup,\n\t\tsyncCoordinatorGroups:\n\t\t\tsyncCoordinatorGroups.length > 0\n\t\t\t\t? syncCoordinatorGroups\n\t\t\t\t: syncCoordinatorGroup\n\t\t\t\t\t? [syncCoordinatorGroup]\n\t\t\t\t\t: [],\n\t\tsyncCoordinatorTimeoutS: parseIntOr(raw.sync_coordinator_timeout_s, 3),\n\t\tsyncCoordinatorPresenceTtlS: parseIntOr(raw.sync_coordinator_presence_ttl_s, 180),\n\t\tsyncCoordinatorAdminSecret: clean(raw.sync_coordinator_admin_secret),\n\t};\n}\n\nexport function coordinatorEnabled(config: CoordinatorSyncConfig): boolean {\n\treturn Boolean(config.syncCoordinatorUrl && config.syncCoordinatorGroups.length > 0);\n}\n\nfunction advertisedSyncAddresses(config: CoordinatorSyncConfig): string[] {\n\tconst advertise = config.syncAdvertise.toLowerCase();\n\tif (advertise && advertise !== \"auto\" && advertise !== \"default\") {\n\t\treturn mergeAddresses(\n\t\t\t[],\n\t\t\tadvertise\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((item) => item.trim())\n\t\t\t\t.filter(Boolean),\n\t\t);\n\t}\n\tif (config.syncHost && config.syncHost !== \"0.0.0.0\") {\n\t\treturn [`${config.syncHost}:${config.syncPort}`];\n\t}\n\tconst addresses = Object.values(networkInterfaces())\n\t\t.flatMap((entries) => entries ?? [])\n\t\t.filter((entry) => !entry.internal)\n\t\t.map((entry) => entry.address)\n\t\t.filter((address) => address && address !== \"127.0.0.1\" && address !== \"::1\")\n\t\t.map((address) => `${address}:${config.syncPort}`);\n\treturn [...new Set(addresses)];\n}\n\nexport async function registerCoordinatorPresence(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<{ groups: string[]; responses: Record<string, unknown>[] } | null> {\n\tif (!coordinatorEnabled(config)) return null;\n\tconst keysDir = process.env.CODEMEM_KEYS_DIR?.trim() || undefined;\n\tconst [deviceId, fingerprint] = ensureDeviceIdentity(store.db, { keysDir });\n\tconst publicKey = loadPublicKey(keysDir);\n\tif (!publicKey) throw new Error(\"public key missing\");\n\tconst baseUrl = buildBaseUrl(config.syncCoordinatorUrl);\n\tconst payload = {\n\t\tfingerprint,\n\t\tpublic_key: publicKey,\n\t\taddresses: advertisedSyncAddresses(config),\n\t\tttl_s: Math.max(1, config.syncCoordinatorPresenceTtlS),\n\t};\n\tconst responses: Record<string, unknown>[] = [];\n\tfor (const groupId of config.syncCoordinatorGroups) {\n\t\tconst groupPayload = { ...payload, group_id: groupId };\n\t\tconst bodyBytes = Buffer.from(JSON.stringify(groupPayload), \"utf8\");\n\t\tconst url = `${baseUrl}/v1/presence`;\n\t\tconst headers = buildAuthHeaders({ deviceId, method: \"POST\", url, bodyBytes, keysDir });\n\t\tconst [status, response] = await requestJson(\"POST\", url, {\n\t\t\theaders,\n\t\t\tbody: groupPayload,\n\t\t\tbodyBytes,\n\t\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t\t});\n\t\tif (status !== 200 || !response) {\n\t\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\t\tthrow new Error(`coordinator presence failed (${status}: ${detail})`);\n\t\t}\n\t\tresponses.push(response);\n\t}\n\treturn { groups: config.syncCoordinatorGroups, responses };\n}\n\nexport async function lookupCoordinatorPeers(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>[]> {\n\tif (!coordinatorEnabled(config)) return [];\n\tconst keysDir = process.env.CODEMEM_KEYS_DIR?.trim() || undefined;\n\tconst [deviceId] = ensureDeviceIdentity(store.db, { keysDir });\n\tconst baseUrl = buildBaseUrl(config.syncCoordinatorUrl);\n\tconst merged = new Map<string, Record<string, unknown>>();\n\tfor (const groupId of config.syncCoordinatorGroups) {\n\t\tconst url = `${baseUrl}/v1/peers?group_id=${encodeURIComponent(groupId)}`;\n\t\tconst headers = buildAuthHeaders({\n\t\t\tdeviceId,\n\t\t\tmethod: \"GET\",\n\t\t\turl,\n\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\tkeysDir,\n\t\t});\n\t\tconst [status, response] = await requestJson(\"GET\", url, {\n\t\t\theaders,\n\t\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t\t});\n\t\tif (status !== 200 || !response) {\n\t\t\tconst detail = typeof response?.error === \"string\" ? response.error : \"unknown\";\n\t\t\tthrow new Error(`coordinator lookup failed (${status}: ${detail})`);\n\t\t}\n\t\tconst items = Array.isArray(response.items) ? response.items : [];\n\t\tfor (const item of items) {\n\t\t\tif (!item || typeof item !== \"object\") continue;\n\t\t\tconst record = item as Record<string, unknown>;\n\t\t\tconst device = clean(record.device_id);\n\t\t\tconst fingerprint = clean(record.fingerprint);\n\t\t\tif (!device) continue;\n\t\t\tconst key = `${device}:${fingerprint}`;\n\t\t\tconst existing = merged.get(key);\n\t\t\tif (!existing) {\n\t\t\t\tmerged.set(key, {\n\t\t\t\t\t...record,\n\t\t\t\t\taddresses: mergeAddresses(\n\t\t\t\t\t\t[],\n\t\t\t\t\t\tArray.isArray(record.addresses)\n\t\t\t\t\t\t\t? record.addresses.filter((x): x is string => typeof x === \"string\")\n\t\t\t\t\t\t\t: [],\n\t\t\t\t\t),\n\t\t\t\t\tgroups: [groupId],\n\t\t\t\t});\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\texisting.addresses = mergeAddresses(\n\t\t\t\t(Array.isArray(existing.addresses) ? existing.addresses : []) as string[],\n\t\t\t\tArray.isArray(record.addresses)\n\t\t\t\t\t? record.addresses.filter((x): x is string => typeof x === \"string\")\n\t\t\t\t\t: [],\n\t\t\t);\n\t\t\texisting.groups = mergeAddresses(\n\t\t\t\t(Array.isArray(existing.groups) ? existing.groups : []) as string[],\n\t\t\t\t[groupId],\n\t\t\t);\n\t\t\texisting.stale = Boolean(existing.stale) && Boolean(record.stale);\n\t\t\tif (clean(record.last_seen_at) > clean(existing.last_seen_at)) {\n\t\t\t\texisting.last_seen_at = record.last_seen_at;\n\t\t\t\texisting.expires_at = record.expires_at;\n\t\t\t}\n\t\t}\n\t}\n\treturn [...merged.values()];\n}\n\nexport async function coordinatorStatusSnapshot(\n\tstore: MemoryStore,\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>> {\n\tconst pairedPeerCount = Number(\n\t\t(\n\t\t\tstore.db.prepare(\"SELECT COUNT(1) AS total FROM sync_peers\").get() as\n\t\t\t\t| { total?: number }\n\t\t\t\t| undefined\n\t\t)?.total ?? 0,\n\t);\n\tif (!coordinatorEnabled(config)) {\n\t\treturn {\n\t\t\tenabled: false,\n\t\t\tconfigured: false,\n\t\t\tgroups: config.syncCoordinatorGroups,\n\t\t\tpaired_peer_count: pairedPeerCount,\n\t\t};\n\t}\n\tconst snapshot: Record<string, unknown> = {\n\t\tenabled: true,\n\t\tconfigured: true,\n\t\tcoordinator_url: config.syncCoordinatorUrl,\n\t\tgroups: config.syncCoordinatorGroups,\n\t\tpaired_peer_count: pairedPeerCount,\n\t\tpresence_status: \"unknown\",\n\t\tpresence_error: null,\n\t\tadvertised_addresses: [],\n\t\tfresh_peer_count: 0,\n\t\tstale_peer_count: 0,\n\t\tdiscovered_peer_count: 0,\n\t};\n\tconst cacheKey = presenceCacheKey(store, config);\n\tconst now = Date.now();\n\tconst cachedPresence = coordinatorPresenceCache.get(cacheKey);\n\tif (cachedPresence && now < cachedPresence.nextRefreshAtMs) {\n\t\tsnapshot.presence_status = cachedPresence.status;\n\t\tsnapshot.presence_error = cachedPresence.error;\n\t\tsnapshot.advertised_addresses = cachedPresence.advertisedAddresses;\n\t} else {\n\t\ttry {\n\t\t\tconst registration = await registerCoordinatorPresence(store, config);\n\t\t\tconst first = registration?.responses?.[0];\n\t\t\tconst advertisedAddresses =\n\t\t\t\tfirst && typeof first === \"object\"\n\t\t\t\t\t? ((first as Record<string, unknown>).addresses ?? [])\n\t\t\t\t\t: [];\n\t\t\tsnapshot.presence_status = \"posted\";\n\t\t\tsnapshot.advertised_addresses = advertisedAddresses;\n\t\t\tcoordinatorPresenceCache.set(cacheKey, {\n\t\t\t\tstatus: \"posted\",\n\t\t\t\terror: null,\n\t\t\t\tadvertisedAddresses,\n\t\t\t\tnextRefreshAtMs: now + presenceRefreshIntervalMs(config),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\tconst status: PresenceStatus = message.includes(\"unknown_device\") ? \"not_enrolled\" : \"error\";\n\t\t\tconst nextRefreshAtMs = status === \"not_enrolled\" ? now : now + presenceRetryIntervalMs();\n\t\t\tsnapshot.presence_status = status;\n\t\t\tsnapshot.presence_error = message;\n\t\t\tcoordinatorPresenceCache.set(cacheKey, {\n\t\t\t\tstatus,\n\t\t\t\terror: message,\n\t\t\t\tadvertisedAddresses: [],\n\t\t\t\tnextRefreshAtMs,\n\t\t\t});\n\t\t}\n\t}\n\ttry {\n\t\tconst peers = await lookupCoordinatorPeers(store, config);\n\t\tsnapshot.discovered_peer_count = peers.length;\n\t\tsnapshot.fresh_peer_count = peers.filter((peer) => !peer.stale).length;\n\t\tsnapshot.stale_peer_count = peers.filter((peer) => Boolean(peer.stale)).length;\n\t} catch (error) {\n\t\tsnapshot.lookup_error = error instanceof Error ? error.message : String(error);\n\t}\n\treturn snapshot;\n}\n\nexport async function listCoordinatorJoinRequests(\n\tconfig: CoordinatorSyncConfig,\n): Promise<Record<string, unknown>[]> {\n\tconst groupId = config.syncCoordinatorGroup || config.syncCoordinatorGroups[0] || \"\";\n\tif (!groupId || !config.syncCoordinatorUrl || !config.syncCoordinatorAdminSecret) return [];\n\tconst url = `${buildBaseUrl(config.syncCoordinatorUrl)}/v1/admin/join-requests?group_id=${encodeURIComponent(groupId)}`;\n\tconst [status, response] = await requestJson(\"GET\", url, {\n\t\theaders: { \"X-Codemem-Coordinator-Admin\": config.syncCoordinatorAdminSecret },\n\t\ttimeoutS: Math.max(1, config.syncCoordinatorTimeoutS),\n\t});\n\tif (status !== 200 || !response) return [];\n\treturn Array.isArray(response.items)\n\t\t? response.items.filter(\n\t\t\t\t(item): item is Record<string, unknown> => Boolean(item) && typeof item === \"object\",\n\t\t\t)\n\t\t: [];\n}\n","/**\n * Embedding primitives for semantic search.\n *\n * Ports codemem/semantic.py — text chunking, hashing, and embedding via a\n * pluggable client interface. The default client uses `@xenova/transformers`\n * (same BAAI/bge-small-en-v1.5 model as Python's fastembed). When the\n * embedding runtime is unavailable the helpers return empty arrays and callers\n * fall back to FTS-only retrieval.\n *\n * Embeddings are always disabled when CODEMEM_EMBEDDING_DISABLED=1.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { isEmbeddingDisabled } from \"./db.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\n/** Minimal interface a concrete embedding backend must satisfy. */\nexport interface EmbeddingClient {\n\treadonly model: string;\n\treadonly dimensions: number;\n\tembed(texts: string[]): Promise<Float32Array[]>;\n}\n\n// ---------------------------------------------------------------------------\n// Text helpers (ports of semantic.py)\n// ---------------------------------------------------------------------------\n\n/** SHA-256 hex digest of UTF-8 encoded text. */\nexport function hashText(text: string): string {\n\treturn createHash(\"sha256\").update(text, \"utf-8\").digest(\"hex\");\n}\n\n/**\n * Split long text into ≤ `maxChars` chunks, preferring paragraph then\n * sentence boundaries. Matches Python's `chunk_text()`.\n */\nexport function chunkText(text: string, maxChars = 1200): string[] {\n\tconst cleaned = text.trim();\n\tif (!cleaned) return [];\n\tif (cleaned.length <= maxChars) return [cleaned];\n\n\tconst paragraphs = cleaned\n\t\t.split(/\\n{2,}/)\n\t\t.map((p) => p.trim())\n\t\t.filter(Boolean);\n\tconst chunks: string[] = [];\n\tlet buffer: string[] = [];\n\tlet bufferLen = 0;\n\n\tfor (const paragraph of paragraphs) {\n\t\tif (bufferLen + paragraph.length + 2 <= maxChars) {\n\t\t\tbuffer.push(paragraph);\n\t\t\tbufferLen += paragraph.length + 2;\n\t\t\tcontinue;\n\t\t}\n\t\tif (buffer.length > 0) {\n\t\t\tchunks.push(buffer.join(\"\\n\\n\"));\n\t\t\tbuffer = [];\n\t\t\tbufferLen = 0;\n\t\t}\n\t\tif (paragraph.length <= maxChars) {\n\t\t\tchunks.push(paragraph);\n\t\t\tcontinue;\n\t\t}\n\t\t// Split long paragraph by sentence\n\t\tconst sentences = paragraph\n\t\t\t.split(/(?<=[.!?])\\s+/)\n\t\t\t.map((s) => s.trim())\n\t\t\t.filter(Boolean);\n\t\tconst sentBuf: string[] = [];\n\t\tlet sentLen = 0;\n\t\tfor (const sentence of sentences) {\n\t\t\t// Hard-split sentences that exceed maxChars on their own\n\t\t\tif (sentence.length > maxChars) {\n\t\t\t\tif (sentBuf.length > 0) {\n\t\t\t\t\tchunks.push(sentBuf.join(\" \"));\n\t\t\t\t\tsentBuf.length = 0;\n\t\t\t\t\tsentLen = 0;\n\t\t\t\t}\n\t\t\t\tfor (let i = 0; i < sentence.length; i += maxChars) {\n\t\t\t\t\tchunks.push(sentence.slice(i, i + maxChars));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (sentLen + sentence.length + 1 <= maxChars) {\n\t\t\t\tsentBuf.push(sentence);\n\t\t\t\tsentLen += sentence.length + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (sentBuf.length > 0) chunks.push(sentBuf.join(\" \"));\n\t\t\tsentBuf.length = 0;\n\t\t\tsentBuf.push(sentence);\n\t\t\tsentLen = sentence.length;\n\t\t}\n\t\tif (sentBuf.length > 0) chunks.push(sentBuf.join(\" \"));\n\t}\n\tif (buffer.length > 0) chunks.push(buffer.join(\"\\n\\n\"));\n\treturn chunks;\n}\n\n// ---------------------------------------------------------------------------\n// Lazy singleton client\n// ---------------------------------------------------------------------------\n\nlet _client: EmbeddingClient | null | undefined;\n\n/** Reset the singleton (for tests). */\nexport function _resetEmbeddingClient(): void {\n\t_client = undefined;\n}\n\n/**\n * Get the shared embedding client, creating it lazily on first call.\n * Returns null when embeddings are disabled or the runtime is unavailable.\n */\nexport async function getEmbeddingClient(): Promise<EmbeddingClient | null> {\n\tif (_client !== undefined) return _client;\n\tif (isEmbeddingDisabled()) {\n\t\t_client = null;\n\t\treturn null;\n\t}\n\tconst model = process.env.CODEMEM_EMBEDDING_MODEL || \"BAAI/bge-small-en-v1.5\";\n\ttry {\n\t\t_client = await createTransformersClient(model);\n\t} catch {\n\t\t_client = null;\n\t}\n\treturn _client;\n}\n\n/**\n * Embed texts using the shared client.\n * Returns an empty array when embeddings are unavailable.\n */\nexport async function embedTexts(texts: string[]): Promise<Float32Array[]> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return [];\n\treturn client.embed(texts);\n}\n\n// ---------------------------------------------------------------------------\n// @xenova/transformers backend (runtime-detected)\n// ---------------------------------------------------------------------------\n\nasync function createTransformersClient(model: string): Promise<EmbeddingClient> {\n\t// Dynamic import so the package is optional at install time\n\tconst { pipeline } = await import(\"@xenova/transformers\");\n\tconst extractor = await pipeline(\"feature-extraction\", model, {\n\t\tquantized: true,\n\t});\n\n\t// Infer dimensions from a probe embedding\n\tconst probe = await extractor(\"probe\", { pooling: \"mean\", normalize: true });\n\tconst dims = probe.dims?.at(-1) ?? 384;\n\n\treturn {\n\t\tmodel,\n\t\tdimensions: dims,\n\t\tasync embed(texts: string[]): Promise<Float32Array[]> {\n\t\t\tconst results: Float32Array[] = [];\n\t\t\tfor (const text of texts) {\n\t\t\t\tconst output = await extractor(text, { pooling: \"mean\", normalize: true });\n\t\t\t\t// output.data is a Float32Array of shape [1, dims]\n\t\t\t\tresults.push(new Float32Array(output.data));\n\t\t\t}\n\t\t\treturn results;\n\t\t},\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Serialization helpers (sqlite-vec wire format)\n// ---------------------------------------------------------------------------\n\n/** Serialize a Float32Array to a little-endian Buffer for sqlite-vec. */\nexport function serializeFloat32(vector: Float32Array): Buffer {\n\tconst buf = Buffer.alloc(vector.length * 4);\n\tfor (let i = 0; i < vector.length; i++) {\n\t\tbuf.writeFloatLE(vector[i] ?? 0, i * 4);\n\t}\n\treturn buf;\n}\n","import { readFileSync } from \"node:fs\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport {\n\tassertSchemaReady,\n\tconnect,\n\ttype Database,\n\tfromJson,\n\tresolveDbPath,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nimport { expandUserPath } from \"./observer-config.js\";\nimport { projectColumnClause, resolveProject as resolveProjectName } from \"./project.js\";\nimport * as schema from \"./schema.js\";\n\ntype JsonObject = Record<string, unknown>;\ntype MemoryInsert = typeof schema.memoryItems.$inferInsert;\n\nexport interface ExportOptions {\n\tdbPath?: string;\n\tproject?: string | null;\n\tallProjects?: boolean;\n\tincludeInactive?: boolean;\n\tsince?: string | null;\n\tcwd?: string;\n}\n\nexport interface ImportOptions {\n\tdbPath?: string;\n\tremapProject?: string | null;\n\tdryRun?: boolean;\n}\n\nexport interface ExportPayload {\n\tversion: \"1.0\";\n\texported_at: string;\n\texport_metadata: {\n\t\ttool_version: \"codemem\";\n\t\tprojects: string[];\n\t\ttotal_memories: number;\n\t\ttotal_sessions: number;\n\t\tinclude_inactive: boolean;\n\t\tfilters: JsonObject;\n\t};\n\tsessions: JsonObject[];\n\tmemory_items: JsonObject[];\n\tsession_summaries: JsonObject[];\n\tuser_prompts: JsonObject[];\n}\n\nexport interface ImportResult {\n\tsessions: number;\n\tuser_prompts: number;\n\tmemory_items: number;\n\tsession_summaries: number;\n\tdryRun: boolean;\n}\n\nconst SUMMARY_METADATA_KEYS = [\n\t\"request\",\n\t\"investigated\",\n\t\"learned\",\n\t\"completed\",\n\t\"next_steps\",\n\t\"notes\",\n\t\"files_read\",\n\t\"files_modified\",\n\t\"prompt_number\",\n\t\"request_original\",\n\t\"discovery_tokens\",\n\t\"discovery_source\",\n] as const;\n\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction nowEpochMs(): number {\n\treturn Date.now();\n}\n\nfunction parseDbObject(raw: unknown): unknown {\n\tif (typeof raw !== \"string\" || raw.trim().length === 0) return null;\n\ttry {\n\t\treturn JSON.parse(raw);\n\t} catch {\n\t\treturn fromJson(raw);\n\t}\n}\n\nfunction parseRowJsonFields<T extends JsonObject>(row: T, fields: string[]): JsonObject {\n\tconst parsed: JsonObject = { ...row };\n\tfor (const field of fields) {\n\t\tparsed[field] = parseDbObject(row[field]);\n\t}\n\treturn parsed;\n}\n\nfunction normalizeImportMetadata(importMetadata: unknown): JsonObject | null {\n\tif (importMetadata == null) return null;\n\tif (typeof importMetadata === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(importMetadata) as unknown;\n\t\t\treturn parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)\n\t\t\t\t? (parsed as JsonObject)\n\t\t\t\t: null;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn typeof importMetadata === \"object\" && !Array.isArray(importMetadata)\n\t\t? ({ ...(importMetadata as JsonObject) } as JsonObject)\n\t\t: null;\n}\n\nexport function buildImportKey(\n\tsource: string,\n\trecordType: string,\n\toriginalId: unknown,\n\tparts?: { project?: string | null; createdAt?: string | null; sourceDb?: string | null },\n): string {\n\tconst values = [source, recordType, String(originalId ?? \"unknown\")];\n\tif (parts?.project) values.push(parts.project);\n\tif (parts?.createdAt) values.push(parts.createdAt);\n\tif (parts?.sourceDb) values.push(parts.sourceDb);\n\treturn values.join(\"|\");\n}\n\nexport function mergeSummaryMetadata(metadata: JsonObject, importMetadata: unknown): JsonObject {\n\tconst parsed = normalizeImportMetadata(importMetadata);\n\tif (!parsed) return metadata;\n\tconst merged: JsonObject = { ...metadata };\n\tfor (const key of SUMMARY_METADATA_KEYS) {\n\t\tif (!(key in parsed)) continue;\n\t\tconst current = merged[key];\n\t\tlet shouldFill = !(key in merged);\n\t\tif (!shouldFill) {\n\t\t\tif (key === \"discovery_tokens\" || key === \"prompt_number\") {\n\t\t\t\tshouldFill = current == null;\n\t\t\t} else if (typeof current === \"string\") {\n\t\t\t\tshouldFill = current.trim().length === 0;\n\t\t\t} else if (Array.isArray(current)) {\n\t\t\t\tshouldFill = current.length === 0;\n\t\t\t} else {\n\t\t\t\tshouldFill = current == null;\n\t\t\t}\n\t\t}\n\t\tif (shouldFill) merged[key] = parsed[key];\n\t}\n\tmerged.import_metadata = importMetadata;\n\treturn merged;\n}\n\nfunction normalizeImportedProject(project: unknown): string | null {\n\tif (typeof project !== \"string\") return null;\n\tconst trimmed = project.trim();\n\tif (!trimmed) return null;\n\tif (/[\\\\/]/.test(trimmed)) {\n\t\tconst normalized = trimmed.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\t\tconst parts = normalized.split(\"/\");\n\t\treturn parts[parts.length - 1] || null;\n\t}\n\treturn trimmed;\n}\n\nfunction resolveExportProject(opts: ExportOptions): string | null {\n\tif (opts.allProjects) return null;\n\treturn resolveProjectName(opts.cwd ?? process.cwd(), opts.project ?? null);\n}\n\nfunction querySessions(db: Database, project: string | null, since: string | null): JsonObject[] {\n\tlet sql = \"SELECT * FROM sessions\";\n\tconst params: unknown[] = [];\n\tconst clauses: string[] = [];\n\tif (project) {\n\t\tconst clause = projectColumnClause(\"project\", project);\n\t\tif (clause.clause) {\n\t\t\tclauses.push(clause.clause);\n\t\t\tparams.push(...clause.params);\n\t\t}\n\t}\n\tif (since) {\n\t\tclauses.push(\"started_at >= ?\");\n\t\tparams.push(since);\n\t}\n\tif (clauses.length > 0) sql += ` WHERE ${clauses.join(\" AND \")}`;\n\tsql += \" ORDER BY started_at ASC\";\n\tconst rows = db.prepare(sql).all(...params) as JsonObject[];\n\treturn rows.map((row) => parseRowJsonFields(row, [\"metadata_json\"]));\n}\n\nfunction fetchBySessionIds(\n\tdb: Database,\n\ttable: string,\n\tsessionIds: number[],\n\torderBy: string,\n\textraWhere = \"\",\n): JsonObject[] {\n\tif (sessionIds.length === 0) return [];\n\tconst placeholders = sessionIds.map(() => \"?\").join(\",\");\n\tconst sql = `SELECT * FROM ${table} WHERE session_id IN (${placeholders})${extraWhere} ORDER BY ${orderBy}`;\n\treturn db.prepare(sql).all(...sessionIds) as JsonObject[];\n}\n\nexport function exportMemories(opts: ExportOptions = {}): ExportPayload {\n\tconst db = connect(resolveDbPath(opts.dbPath));\n\ttry {\n\t\tassertSchemaReady(db);\n\t\tconst resolvedProject = resolveExportProject(opts);\n\t\tconst filters: JsonObject = {};\n\t\tif (resolvedProject) filters.project = resolvedProject;\n\t\tif (opts.since) filters.since = opts.since;\n\n\t\tconst sessions = querySessions(db, resolvedProject, opts.since ?? null);\n\t\tconst sessionIds = sessions.map((row) => Number(row.id)).filter(Number.isFinite);\n\n\t\tconst memories = fetchBySessionIds(\n\t\t\tdb,\n\t\t\t\"memory_items\",\n\t\t\tsessionIds,\n\t\t\t\"created_at ASC\",\n\t\t\topts.includeInactive ? \"\" : \" AND active = 1\",\n\t\t).map((row) =>\n\t\t\tparseRowJsonFields(row, [\n\t\t\t\t\"metadata_json\",\n\t\t\t\t\"facts\",\n\t\t\t\t\"concepts\",\n\t\t\t\t\"files_read\",\n\t\t\t\t\"files_modified\",\n\t\t\t]),\n\t\t);\n\n\t\tconst summaries = fetchBySessionIds(\n\t\t\tdb,\n\t\t\t\"session_summaries\",\n\t\t\tsessionIds,\n\t\t\t\"created_at_epoch ASC\",\n\t\t).map((row) => parseRowJsonFields(row, [\"metadata_json\", \"files_read\", \"files_edited\"]));\n\n\t\tconst prompts = fetchBySessionIds(db, \"user_prompts\", sessionIds, \"created_at_epoch ASC\").map(\n\t\t\t(row) => parseRowJsonFields(row, [\"metadata_json\"]),\n\t\t);\n\n\t\tconst promptImportKeys = new Map<number, string>();\n\t\tfor (const prompt of prompts) {\n\t\t\tif (typeof prompt.id === \"number\" && typeof prompt.import_key === \"string\") {\n\t\t\t\tpromptImportKeys.set(prompt.id, prompt.import_key);\n\t\t\t}\n\t\t}\n\t\tfor (const memory of memories) {\n\t\t\tif (typeof memory.user_prompt_id === \"number\") {\n\t\t\t\tmemory.user_prompt_import_key = promptImportKeys.get(memory.user_prompt_id) ?? null;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tversion: \"1.0\",\n\t\t\texported_at: nowIso(),\n\t\t\texport_metadata: {\n\t\t\t\ttool_version: \"codemem\",\n\t\t\t\tprojects: [...new Set(sessions.map((s) => String(s.project ?? \"\")).filter(Boolean))],\n\t\t\t\ttotal_memories: memories.length,\n\t\t\t\ttotal_sessions: sessions.length,\n\t\t\t\tinclude_inactive: Boolean(opts.includeInactive),\n\t\t\t\tfilters,\n\t\t\t},\n\t\t\tsessions,\n\t\t\tmemory_items: memories,\n\t\t\tsession_summaries: summaries,\n\t\t\tuser_prompts: prompts,\n\t\t};\n\t} finally {\n\t\tdb.close();\n\t}\n}\n\nexport function readImportPayload(inputFile: string): ExportPayload {\n\tconst raw =\n\t\tinputFile === \"-\" ? readFileSync(0, \"utf8\") : readFileSync(expandUserPath(inputFile), \"utf8\");\n\tconst parsed = JSON.parse(raw) as unknown;\n\tif (parsed == null || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n\t\tthrow new Error(\"Import payload must be a JSON object\");\n\t}\n\tconst payload = parsed as ExportPayload;\n\tif (payload.version !== \"1.0\") {\n\t\tthrow new Error(\n\t\t\t`Unsupported export version: ${String((parsed as JsonObject).version ?? \"unknown\")}`,\n\t\t);\n\t}\n\treturn payload;\n}\n\nfunction findImportedId(db: Database, table: string, importKey: string): number | null {\n\tconst row = db.prepare(`SELECT id FROM ${table} WHERE import_key = ? LIMIT 1`).get(importKey) as\n\t\t| { id: number }\n\t\t| undefined;\n\treturn row?.id ?? null;\n}\n\nfunction nextUserName(): string {\n\treturn process.env.USER?.trim() || process.env.USERNAME?.trim() || \"import\";\n}\n\ntype DrizzleDb = ReturnType<typeof drizzle>;\n\nfunction insertSession(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: typeof row.started_at === \"string\" ? row.started_at : nowIso(),\n\t\t\tended_at: typeof row.ended_at === \"string\" ? row.ended_at : null,\n\t\t\tcwd: String(row.cwd ?? process.cwd()),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\tgit_remote: row.git_remote == null ? null : String(row.git_remote),\n\t\t\tgit_branch: row.git_branch == null ? null : String(row.git_branch),\n\t\t\tuser: String(row.user ?? nextUserName()),\n\t\t\ttool_version: String(row.tool_version ?? \"import\"),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"session insert returned no id\");\n\treturn id;\n}\n\nfunction insertPrompt(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.userPrompts)\n\t\t.values({\n\t\t\tsession_id: Number(row.session_id),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\tprompt_text: String(row.prompt_text ?? \"\"),\n\t\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : nowIso(),\n\t\t\tcreated_at_epoch:\n\t\t\t\ttypeof row.created_at_epoch === \"number\" ? row.created_at_epoch : nowEpochMs(),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.userPrompts.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"prompt insert returned no id\");\n\treturn id;\n}\n\nfunction insertMemory(d: DrizzleDb, row: JsonObject): number {\n\tconst now = nowIso();\n\tconst parsedActive = row.active == null ? 1 : Number(row.active);\n\tconst active = Number.isFinite(parsedActive) ? parsedActive : 1;\n\tconst deletedAt =\n\t\ttypeof row.deleted_at === \"string\" && row.deleted_at.trim().length > 0 ? row.deleted_at : null;\n\tconst values: MemoryInsert = {\n\t\tsession_id: Number(row.session_id),\n\t\tkind: String(row.kind ?? \"observation\"),\n\t\ttitle: String(row.title ?? \"Untitled\"),\n\t\tsubtitle: row.subtitle == null ? null : String(row.subtitle),\n\t\tbody_text: String(row.body_text ?? row.narrative ?? \"\"),\n\t\tconfidence: Number(row.confidence ?? 0.5),\n\t\ttags_text: String(row.tags_text ?? \"\"),\n\t\tactive,\n\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : now,\n\t\tupdated_at: typeof row.updated_at === \"string\" ? row.updated_at : now,\n\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\tactor_id: row.actor_id == null ? null : String(row.actor_id),\n\t\tactor_display_name: row.actor_display_name == null ? null : String(row.actor_display_name),\n\t\tvisibility: row.visibility == null ? null : String(row.visibility),\n\t\tworkspace_id: row.workspace_id == null ? null : String(row.workspace_id),\n\t\tworkspace_kind: row.workspace_kind == null ? null : String(row.workspace_kind),\n\t\torigin_device_id: row.origin_device_id == null ? null : String(row.origin_device_id),\n\t\torigin_source: row.origin_source == null ? null : String(row.origin_source),\n\t\ttrust_state: row.trust_state == null ? null : String(row.trust_state),\n\t\tfacts: toJsonNullable(row.facts),\n\t\tnarrative: row.narrative == null ? null : String(row.narrative),\n\t\tconcepts: toJsonNullable(row.concepts),\n\t\tfiles_read: toJsonNullable(row.files_read),\n\t\tfiles_modified: toJsonNullable(row.files_modified),\n\t\tuser_prompt_id: row.user_prompt_id == null ? null : Number(row.user_prompt_id),\n\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\tdeleted_at: deletedAt,\n\t\trev: Number(row.rev ?? 1),\n\t\timport_key: String(row.import_key),\n\t};\n\tconst rows = d\n\t\t.insert(schema.memoryItems)\n\t\t.values(values)\n\t\t.returning({ id: schema.memoryItems.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"memory insert returned no id\");\n\treturn id;\n}\n\nfunction insertSummary(d: DrizzleDb, row: JsonObject): number {\n\tconst rows = d\n\t\t.insert(schema.sessionSummaries)\n\t\t.values({\n\t\t\tsession_id: Number(row.session_id),\n\t\t\tproject: row.project == null ? null : String(row.project),\n\t\t\trequest: String(row.request ?? \"\"),\n\t\t\tinvestigated: String(row.investigated ?? \"\"),\n\t\t\tlearned: String(row.learned ?? \"\"),\n\t\t\tcompleted: String(row.completed ?? \"\"),\n\t\t\tnext_steps: String(row.next_steps ?? \"\"),\n\t\t\tnotes: String(row.notes ?? \"\"),\n\t\t\tfiles_read: toJsonNullable(row.files_read),\n\t\t\tfiles_edited: toJsonNullable(row.files_edited),\n\t\t\tprompt_number: row.prompt_number == null ? null : Number(row.prompt_number),\n\t\t\tcreated_at: typeof row.created_at === \"string\" ? row.created_at : nowIso(),\n\t\t\tcreated_at_epoch:\n\t\t\t\ttypeof row.created_at_epoch === \"number\" ? row.created_at_epoch : nowEpochMs(),\n\t\t\tmetadata_json: toJson(row.metadata_json ?? null),\n\t\t\timport_key: String(row.import_key),\n\t\t})\n\t\t.returning({ id: schema.sessionSummaries.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"summary insert returned no id\");\n\treturn id;\n}\n\nexport function importMemories(payload: ExportPayload, opts: ImportOptions = {}): ImportResult {\n\tconst sessionsData = Array.isArray(payload.sessions) ? payload.sessions : [];\n\tconst memoriesData = Array.isArray(payload.memory_items) ? payload.memory_items : [];\n\tconst summariesData = Array.isArray(payload.session_summaries) ? payload.session_summaries : [];\n\tconst promptsData = Array.isArray(payload.user_prompts) ? payload.user_prompts : [];\n\n\tif (opts.dryRun) {\n\t\treturn {\n\t\t\tsessions: sessionsData.length,\n\t\t\tuser_prompts: promptsData.length,\n\t\t\tmemory_items: memoriesData.length,\n\t\t\tsession_summaries: summariesData.length,\n\t\t\tdryRun: true,\n\t\t};\n\t}\n\n\tconst db = connect(resolveDbPath(opts.dbPath));\n\ttry {\n\t\tassertSchemaReady(db);\n\t\tconst d = drizzle(db, { schema });\n\t\treturn db.transaction(() => {\n\t\t\tconst sessionMapping = new Map<number, number>();\n\t\t\tconst promptMapping = new Map<number, number>();\n\t\t\tconst promptImportKeyMapping = new Map<string, number>();\n\t\t\tlet importedSessions = 0;\n\t\t\tlet importedPrompts = 0;\n\t\t\tlet importedMemories = 0;\n\t\t\tlet importedSummaries = 0;\n\n\t\t\tfor (const session of sessionsData) {\n\t\t\t\tconst oldSessionId = Number(session.id);\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(session.project);\n\t\t\t\tconst importKey = buildImportKey(\"export\", \"session\", session.id, {\n\t\t\t\t\tproject,\n\t\t\t\t\tcreatedAt: typeof session.started_at === \"string\" ? session.started_at : null,\n\t\t\t\t});\n\t\t\t\tconst existingId = findImportedId(db, \"sessions\", importKey);\n\t\t\t\tif (existingId != null) {\n\t\t\t\t\tsessionMapping.set(oldSessionId, existingId);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_session_id: session.id,\n\t\t\t\t\toriginal_started_at: session.started_at ?? null,\n\t\t\t\t\toriginal_ended_at: session.ended_at ?? null,\n\t\t\t\t\timport_metadata: session.metadata_json ?? null,\n\t\t\t\t\timport_key: importKey,\n\t\t\t\t};\n\t\t\t\tconst newId = insertSession(d, {\n\t\t\t\t\t...session,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: importKey,\n\t\t\t\t});\n\t\t\t\tsessionMapping.set(oldSessionId, newId);\n\t\t\t\timportedSessions += 1;\n\t\t\t}\n\n\t\t\tfor (const prompt of promptsData) {\n\t\t\t\tconst oldSessionId = Number(prompt.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(prompt.project);\n\t\t\t\tconst promptImportKey =\n\t\t\t\t\ttypeof prompt.import_key === \"string\" && prompt.import_key.trim()\n\t\t\t\t\t\t? prompt.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"prompt\", prompt.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof prompt.created_at === \"string\" ? prompt.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tconst existingId = findImportedId(db, \"user_prompts\", promptImportKey);\n\t\t\t\tif (existingId != null) {\n\t\t\t\t\tif (typeof prompt.id === \"number\") promptMapping.set(prompt.id, existingId);\n\t\t\t\t\tpromptImportKeyMapping.set(promptImportKey, existingId);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_prompt_id: prompt.id ?? null,\n\t\t\t\t\toriginal_created_at: prompt.created_at ?? null,\n\t\t\t\t\timport_metadata: prompt.metadata_json ?? null,\n\t\t\t\t\timport_key: promptImportKey,\n\t\t\t\t};\n\t\t\t\tconst newId = insertPrompt(d, {\n\t\t\t\t\t...prompt,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: promptImportKey,\n\t\t\t\t});\n\t\t\t\tif (typeof prompt.id === \"number\") promptMapping.set(prompt.id, newId);\n\t\t\t\tpromptImportKeyMapping.set(promptImportKey, newId);\n\t\t\t\timportedPrompts += 1;\n\t\t\t}\n\n\t\t\tfor (const memory of memoriesData) {\n\t\t\t\tconst oldSessionId = Number(memory.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(memory.project);\n\t\t\t\tconst memoryImportKey =\n\t\t\t\t\ttypeof memory.import_key === \"string\" && memory.import_key.trim()\n\t\t\t\t\t\t? memory.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"memory\", memory.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof memory.created_at === \"string\" ? memory.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tif (findImportedId(db, \"memory_items\", memoryImportKey) != null) continue;\n\n\t\t\t\tlet linkedPromptId: number | null = null;\n\t\t\t\tif (\n\t\t\t\t\ttypeof memory.user_prompt_import_key === \"string\" &&\n\t\t\t\t\tmemory.user_prompt_import_key.trim()\n\t\t\t\t) {\n\t\t\t\t\tlinkedPromptId =\n\t\t\t\t\t\tpromptImportKeyMapping.get(memory.user_prompt_import_key.trim()) ??\n\t\t\t\t\t\tfindImportedId(db, \"user_prompts\", memory.user_prompt_import_key.trim());\n\t\t\t\t} else if (typeof memory.user_prompt_id === \"number\") {\n\t\t\t\t\tlinkedPromptId = promptMapping.get(memory.user_prompt_id) ?? null;\n\t\t\t\t}\n\n\t\t\t\tconst baseMetadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_memory_id: memory.id ?? null,\n\t\t\t\t\toriginal_created_at: memory.created_at ?? null,\n\t\t\t\t\timport_metadata: memory.metadata_json ?? null,\n\t\t\t\t\timport_key: memoryImportKey,\n\t\t\t\t};\n\t\t\t\tif (\n\t\t\t\t\ttypeof memory.user_prompt_import_key === \"string\" &&\n\t\t\t\t\tmemory.user_prompt_import_key.trim()\n\t\t\t\t) {\n\t\t\t\t\tbaseMetadata.user_prompt_import_key = memory.user_prompt_import_key.trim();\n\t\t\t\t}\n\t\t\t\tconst metadata =\n\t\t\t\t\tmemory.kind === \"session_summary\"\n\t\t\t\t\t\t? mergeSummaryMetadata(baseMetadata, memory.metadata_json ?? null)\n\t\t\t\t\t\t: baseMetadata;\n\n\t\t\t\tinsertMemory(d, {\n\t\t\t\t\t...memory,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tuser_prompt_id: linkedPromptId,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: memoryImportKey,\n\t\t\t\t});\n\t\t\t\timportedMemories += 1;\n\t\t\t}\n\n\t\t\tfor (const summary of summariesData) {\n\t\t\t\tconst oldSessionId = Number(summary.session_id);\n\t\t\t\tconst newSessionId = sessionMapping.get(oldSessionId);\n\t\t\t\tif (newSessionId == null) continue;\n\t\t\t\tconst project = opts.remapProject || normalizeImportedProject(summary.project);\n\t\t\t\tconst summaryImportKey =\n\t\t\t\t\ttypeof summary.import_key === \"string\" && summary.import_key.trim()\n\t\t\t\t\t\t? summary.import_key.trim()\n\t\t\t\t\t\t: buildImportKey(\"export\", \"summary\", summary.id, {\n\t\t\t\t\t\t\t\tproject,\n\t\t\t\t\t\t\t\tcreatedAt: typeof summary.created_at === \"string\" ? summary.created_at : null,\n\t\t\t\t\t\t\t});\n\t\t\t\tif (findImportedId(db, \"session_summaries\", summaryImportKey) != null) continue;\n\t\t\t\tconst metadata: JsonObject = {\n\t\t\t\t\tsource: \"export\",\n\t\t\t\t\toriginal_summary_id: summary.id ?? null,\n\t\t\t\t\toriginal_created_at: summary.created_at ?? null,\n\t\t\t\t\timport_metadata: summary.metadata_json ?? null,\n\t\t\t\t\timport_key: summaryImportKey,\n\t\t\t\t};\n\t\t\t\tinsertSummary(d, {\n\t\t\t\t\t...summary,\n\t\t\t\t\tsession_id: newSessionId,\n\t\t\t\t\tproject,\n\t\t\t\t\tmetadata_json: metadata,\n\t\t\t\t\timport_key: summaryImportKey,\n\t\t\t\t});\n\t\t\t\timportedSummaries += 1;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tsessions: importedSessions,\n\t\t\t\tuser_prompts: importedPrompts,\n\t\t\t\tmemory_items: importedMemories,\n\t\t\t\tsession_summaries: importedSummaries,\n\t\t\t\tdryRun: false,\n\t\t\t};\n\t\t})();\n\t} finally {\n\t\tdb.close();\n\t}\n}\n","/**\n * Filter builder for memory_items queries.\n *\n * Mirrors Python's _extend_memory_filter_clauses in codemem/store/search.py.\n * Builds WHERE clause fragments and parameter arrays from a MemoryFilters object.\n */\n\nimport { projectClause } from \"./project.js\";\nimport type { MemoryFilters } from \"./types.js\";\n\nexport interface OwnershipFilterContext {\n\tactorId: string;\n\tdeviceId: string;\n}\n\nexport interface FilterResult {\n\tclauses: string[];\n\tparams: unknown[];\n\tjoinSessions: boolean;\n}\n\n/**\n * Normalize a filter value that may be a single string or an array of strings\n * into a clean, non-empty array of trimmed strings.\n */\nexport function normalizeFilterStrings(value: string | string[] | undefined | null): string[] {\n\tif (value == null) return [];\n\tconst items = Array.isArray(value) ? value : [value];\n\tconst seen = new Set<string>();\n\tconst normalized: string[] = [];\n\tfor (const item of items) {\n\t\tconst candidate = String(item).trim();\n\t\tif (!candidate || seen.has(candidate)) continue;\n\t\tseen.add(candidate);\n\t\tnormalized.push(candidate);\n\t}\n\treturn normalized;\n}\n\nexport function normalizeWorkspaceKinds(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter(\n\t\t\t(raw, idx, arr) => (raw === \"personal\" || raw === \"shared\") && arr.indexOf(raw) === idx,\n\t\t);\n}\n\nexport function normalizeVisibilityValues(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter((raw, idx, arr) => (raw === \"private\" || raw === \"shared\") && arr.indexOf(raw) === idx);\n}\n\nexport function normalizeTrustStates(value: string | string[] | undefined | null): string[] {\n\treturn normalizeFilterStrings(value)\n\t\t.map((raw) => raw.toLowerCase())\n\t\t.filter(\n\t\t\t(raw, idx, arr) =>\n\t\t\t\t(raw === \"trusted\" || raw === \"legacy_unknown\" || raw === \"unreviewed\") &&\n\t\t\t\tarr.indexOf(raw) === idx,\n\t\t);\n}\n\n/**\n * Add IN / NOT IN clauses for a multi-value filter column.\n * Mirrors Python's _add_multi_value_filter.\n */\nfunction addMultiValueFilter(\n\tclauses: string[],\n\tparams: unknown[],\n\tcolumn: string,\n\tincludeValues: string[],\n\texcludeValues: string[],\n): void {\n\tif (includeValues.length > 0) {\n\t\tconst placeholders = includeValues.map(() => \"?\").join(\", \");\n\t\tclauses.push(`${column} IN (${placeholders})`);\n\t\tparams.push(...includeValues);\n\t}\n\tif (excludeValues.length > 0) {\n\t\tconst placeholders = excludeValues.map(() => \"?\").join(\", \");\n\t\tclauses.push(`(${column} IS NULL OR ${column} NOT IN (${placeholders}))`);\n\t\tparams.push(...excludeValues);\n\t}\n}\n\n/**\n * Build WHERE clause fragments from a MemoryFilters object.\n *\n * Returns clauses, params, and whether the sessions table must be joined.\n * The caller is responsible for prepending `memory_items.active = 1` or\n * similar base conditions — this function only appends filter-specific clauses.\n */\nexport function buildFilterClauses(filters: MemoryFilters | undefined | null): FilterResult {\n\treturn buildFilterClausesWithContext(filters);\n}\n\nexport function buildFilterClausesWithContext(\n\tfilters: MemoryFilters | undefined | null,\n\townership?: OwnershipFilterContext,\n): FilterResult {\n\tconst result: FilterResult = { clauses: [], params: [], joinSessions: false };\n\tif (!filters) return result;\n\n\tconst { clauses, params } = result;\n\n\t// Single kind filter\n\tif (filters.kind) {\n\t\tclauses.push(\"memory_items.kind = ?\");\n\t\tparams.push(filters.kind);\n\t}\n\tif (filters.session_id) {\n\t\tclauses.push(\"memory_items.session_id = ?\");\n\t\tparams.push(filters.session_id);\n\t}\n\tif (filters.since) {\n\t\tclauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(filters.since);\n\t}\n\n\tconst ownershipScope = String(filters.ownership_scope ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (ownership && (ownershipScope === \"mine\" || ownershipScope === \"theirs\")) {\n\t\tconst ownedClause =\n\t\t\t\"(COALESCE(memory_items.actor_id, '') = ? OR COALESCE(memory_items.origin_device_id, '') = ?)\";\n\t\tif (ownershipScope === \"mine\") {\n\t\t\tclauses.push(ownedClause);\n\t\t} else {\n\t\t\tclauses.push(\n\t\t\t\t\"(COALESCE(memory_items.actor_id, '') != ? AND COALESCE(memory_items.origin_device_id, '') != ?)\",\n\t\t\t);\n\t\t}\n\t\tparams.push(ownership.actorId, ownership.deviceId);\n\t}\n\n\t// Project scoping — requires sessions JOIN\n\tif (filters.project) {\n\t\tconst { clause, params: projectParams } = projectClause(filters.project);\n\t\tif (clause) {\n\t\t\tclauses.push(clause);\n\t\t\tparams.push(...projectParams);\n\t\t\tresult.joinSessions = true;\n\t\t}\n\t}\n\n\t// Visibility\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.visibility\",\n\t\tnormalizeVisibilityValues(filters.include_visibility ?? filters.visibility),\n\t\tnormalizeVisibilityValues(filters.exclude_visibility),\n\t);\n\n\t// Workspace IDs\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.workspace_id\",\n\t\tnormalizeFilterStrings(filters.include_workspace_ids),\n\t\tnormalizeFilterStrings(filters.exclude_workspace_ids),\n\t);\n\n\t// Workspace kinds\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.workspace_kind\",\n\t\tnormalizeWorkspaceKinds(filters.include_workspace_kinds),\n\t\tnormalizeWorkspaceKinds(filters.exclude_workspace_kinds),\n\t);\n\n\t// Actor IDs\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.actor_id\",\n\t\tnormalizeFilterStrings(filters.include_actor_ids),\n\t\tnormalizeFilterStrings(filters.exclude_actor_ids),\n\t);\n\n\t// Trust states\n\taddMultiValueFilter(\n\t\tclauses,\n\t\tparams,\n\t\t\"memory_items.trust_state\",\n\t\tnormalizeTrustStates(filters.include_trust_states),\n\t\tnormalizeTrustStates(filters.exclude_trust_states),\n\t);\n\n\treturn result;\n}\n","/**\n * Text sanitization for the ingest pipeline.\n *\n * Ports codemem/ingest_sanitize.py — strips <private> blocks and redacts\n * sensitive field names from payloads before they reach the observer LLM.\n */\n\n// ---------------------------------------------------------------------------\n// Private content stripping\n// ---------------------------------------------------------------------------\n\nconst PRIVATE_BLOCK_RE = /<private>.*?<\\/private>/gis;\nconst PRIVATE_OPEN_RE = /<private>/i;\nconst PRIVATE_CLOSE_RE = /<\\/private>/gi;\n\n/**\n * Remove `<private>…</private>` blocks from text.\n *\n * Handles matched pairs, orphaned opening tags (truncates at the tag),\n * and stray closing tags (removed).\n */\nexport function stripPrivate(text: string): string {\n\tif (!text) return \"\";\n\t// Remove matched pairs\n\tlet redacted = text.replace(PRIVATE_BLOCK_RE, \"\");\n\t// Orphaned opening tag — truncate everything after it\n\tconst openMatch = PRIVATE_OPEN_RE.exec(redacted);\n\tif (openMatch) {\n\t\tredacted = redacted.slice(0, openMatch.index);\n\t}\n\t// Stray closing tags\n\tredacted = redacted.replace(PRIVATE_CLOSE_RE, \"\");\n\treturn redacted;\n}\n\n// ---------------------------------------------------------------------------\n// Sensitive field detection\n// ---------------------------------------------------------------------------\n\nconst SENSITIVE_FIELD_RE =\n\t/(?:^|_|-)(?:token|secret|password|passwd|api[_-]?key|authorization|private[_-]?key|cookie)(?:$|_|-)/i;\n\nconst REDACTED_VALUE = \"[REDACTED]\";\n\nexport function isSensitiveFieldName(fieldName: string): boolean {\n\tconst normalized = fieldName.trim().toLowerCase();\n\tif (!normalized) return false;\n\treturn SENSITIVE_FIELD_RE.test(normalized);\n}\n\n// ---------------------------------------------------------------------------\n// Deep sanitization\n// ---------------------------------------------------------------------------\n\nexport function stripPrivateObj(value: unknown): unknown {\n\tif (typeof value === \"string\") return stripPrivate(value);\n\tif (Array.isArray(value)) return value.map(stripPrivateObj);\n\tif (value != null && typeof value === \"object\") {\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, item] of Object.entries(value as Record<string, unknown>)) {\n\t\t\tif (isSensitiveFieldName(key)) {\n\t\t\t\tresult[key] = REDACTED_VALUE;\n\t\t\t} else {\n\t\t\t\tresult[key] = stripPrivateObj(item);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\n// ---------------------------------------------------------------------------\n// Payload sanitization (truncation + private stripping)\n// ---------------------------------------------------------------------------\n\nfunction truncateText(text: string, maxChars: number): string {\n\tif (maxChars <= 0) return \"\";\n\tif (text.length <= maxChars) return text;\n\treturn `${text.slice(0, maxChars)}... (truncated)`;\n}\n\nexport function sanitizePayload(value: unknown, maxChars: number): unknown {\n\tif (value == null) return null;\n\tif (typeof value === \"string\") {\n\t\treturn truncateText(stripPrivate(value), maxChars);\n\t}\n\tconst cleaned = stripPrivateObj(value);\n\ttry {\n\t\tconst serialized = JSON.stringify(cleaned);\n\t\tif (maxChars > 0 && serialized.length > maxChars) {\n\t\t\treturn truncateText(serialized, maxChars);\n\t\t}\n\t} catch {\n\t\tconst asStr = String(cleaned);\n\t\tif (maxChars > 0 && asStr.length > maxChars) {\n\t\t\treturn truncateText(asStr, maxChars);\n\t\t}\n\t}\n\treturn cleaned;\n}\n\n// ---------------------------------------------------------------------------\n// Low-signal output detection\n// ---------------------------------------------------------------------------\n\nconst LOW_SIGNAL_OUTPUTS = new Set([\n\t\"wrote file successfully.\",\n\t\"wrote file successfully\",\n\t\"file written successfully.\",\n\t\"read file successfully.\",\n\t\"read file successfully\",\n\t\"<file>\",\n\t\"<image>\",\n]);\n\nfunction isLowSignalOutput(output: string): boolean {\n\tif (!output) return true;\n\tconst lines = output\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.filter(Boolean);\n\tif (lines.length === 0) return true;\n\treturn lines.every((line) => LOW_SIGNAL_OUTPUTS.has(line.toLowerCase()));\n}\n\nexport function sanitizeToolOutput(_tool: string, output: unknown, maxChars: number): unknown {\n\tif (output == null) return null;\n\tconst sanitized = sanitizePayload(output, maxChars);\n\tconst text = String(sanitized ?? \"\");\n\tif (isLowSignalOutput(text)) return \"\";\n\treturn sanitized;\n}\n","/**\n * Event extraction and budgeting for the ingest pipeline.\n *\n * Ports codemem/ingest/events.py + codemem/ingest_tool_events.py —\n * converts raw plugin events into ToolEvent structs, filters low-signal\n * tools, deduplicates, and budgets the list to fit observer token limits.\n */\n\nimport { sanitizePayload, sanitizeToolOutput } from \"./ingest-sanitize.js\";\nimport type { ToolEvent } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/**\n * Tools whose output is too noisy / low-signal to send to the observer.\n * Matches Python's LOW_SIGNAL_TOOLS set.\n */\nexport const LOW_SIGNAL_TOOLS = new Set([\n\t\"tui\",\n\t\"shell\",\n\t\"cmd\",\n\t\"task\",\n\t\"slashcommand\",\n\t\"skill\",\n\t\"todowrite\",\n\t\"askuserquestion\",\n]);\n\n// ---------------------------------------------------------------------------\n// Tool name helpers\n// ---------------------------------------------------------------------------\n\n/** Return true for codemem's own MCP tools (avoids feedback loops). */\nexport function isInternalMemoryTool(tool: string): boolean {\n\treturn tool.startsWith(\"codemem_memory_\");\n}\n\n/** Extract a clean, lowercase tool name from a raw event. */\nexport function normalizeToolName(event: Record<string, unknown>): string {\n\tlet tool = String(event.tool ?? event.type ?? \"tool\").toLowerCase();\n\tif (tool.includes(\".\")) tool = tool.split(\".\").pop() ?? tool;\n\tif (tool.includes(\":\")) tool = tool.split(\":\").pop() ?? tool;\n\treturn tool;\n}\n\n// ---------------------------------------------------------------------------\n// Output compaction\n// ---------------------------------------------------------------------------\n\nfunction compactReadOutput(text: string, maxLines = 80, maxChars = 2000): string {\n\tif (!text) return \"\";\n\tconst allLines = text.split(\"\\n\");\n\tlet lines = allLines;\n\tif (lines.length > maxLines) {\n\t\tlines = [...lines.slice(0, maxLines), `... (+${allLines.length - maxLines} more lines)`];\n\t}\n\tlet compacted = lines.join(\"\\n\");\n\tif (maxChars > 0 && compacted.length > maxChars) {\n\t\tcompacted = `${compacted.slice(0, maxChars)}\\n... (truncated)`;\n\t}\n\treturn compacted;\n}\n\nfunction compactListOutput(text: string): string {\n\treturn compactReadOutput(text, 120, 2400);\n}\n\n// ---------------------------------------------------------------------------\n// Event → ToolEvent conversion\n// ---------------------------------------------------------------------------\n\n/** Convert a single raw event to a ToolEvent, or null if it should be skipped. */\nexport function eventToToolEvent(\n\tevent: Record<string, unknown>,\n\tmaxChars: number,\n\tlowSignalTools: Set<string> = LOW_SIGNAL_TOOLS,\n): ToolEvent | null {\n\tif (event.type !== \"tool.execute.after\") return null;\n\n\tconst tool = normalizeToolName(event);\n\tif (isInternalMemoryTool(tool)) return null;\n\tif (lowSignalTools.has(tool)) return null;\n\n\tconst rawArgs = event.args;\n\tconst args =\n\t\trawArgs != null && typeof rawArgs === \"object\" && !Array.isArray(rawArgs)\n\t\t\t? (rawArgs as Record<string, unknown>)\n\t\t\t: {};\n\n\tlet result = sanitizeToolOutput(tool, event.result, maxChars);\n\tif (tool === \"read\" && typeof result === \"string\") result = compactReadOutput(result);\n\tif (tool === \"bash\" && typeof result === \"string\") result = compactReadOutput(result);\n\tif ((tool === \"glob\" || tool === \"grep\") && typeof result === \"string\") {\n\t\tresult = compactListOutput(result);\n\t}\n\n\tconst error = sanitizePayload(event.error, maxChars);\n\n\treturn {\n\t\ttoolName: tool,\n\t\ttoolInput: sanitizePayload(args, maxChars),\n\t\ttoolOutput: result,\n\t\ttoolError: error,\n\t\ttimestamp: typeof event.timestamp === \"string\" ? event.timestamp : null,\n\t\tcwd:\n\t\t\t(typeof event.cwd === \"string\" ? event.cwd : null) ??\n\t\t\t(typeof args.cwd === \"string\" ? args.cwd : null),\n\t};\n}\n\n/** Filter and convert all events to ToolEvents. */\nexport function extractToolEvents(\n\tevents: Record<string, unknown>[],\n\tmaxChars: number,\n): ToolEvent[] {\n\tconst result: ToolEvent[] = [];\n\tfor (const event of events) {\n\t\tconst te = eventToToolEvent(event, maxChars);\n\t\tif (te) result.push(te);\n\t}\n\treturn result;\n}\n\n// ---------------------------------------------------------------------------\n// Adapter event extraction (schema v1.0)\n// ---------------------------------------------------------------------------\n\n/** Extract validated _adapter event, or null if not a valid v1.0 adapter. */\nexport function extractAdapterEvent(\n\tevent: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst adapter = event._adapter;\n\tif (adapter == null || typeof adapter !== \"object\" || Array.isArray(adapter)) return null;\n\tconst a = adapter as Record<string, unknown>;\n\n\tif (a.schema_version !== \"1.0\") return null;\n\tif (typeof a.source !== \"string\" || !a.source.trim()) return null;\n\tif (typeof a.session_id !== \"string\" || !a.session_id.trim()) return null;\n\tif (typeof a.event_id !== \"string\" || !a.event_id.trim()) return null;\n\n\tconst eventType = a.event_type;\n\tif (typeof eventType !== \"string\") return null;\n\tconst validTypes = new Set([\n\t\t\"prompt\",\n\t\t\"assistant\",\n\t\t\"tool_call\",\n\t\t\"tool_result\",\n\t\t\"session_start\",\n\t\t\"session_end\",\n\t\t\"error\",\n\t]);\n\tif (!validTypes.has(eventType)) return null;\n\n\tif (a.payload == null || typeof a.payload !== \"object\") return null;\n\tif (typeof a.ts !== \"string\" || !a.ts.trim()) return null;\n\n\t// Validate timestamp parses\n\ttry {\n\t\tconst d = new Date(a.ts as string);\n\t\tif (Number.isNaN(d.getTime())) return null;\n\t} catch {\n\t\treturn null;\n\t}\n\n\treturn a;\n}\n\n/** Project an adapter tool_result into the flat event format expected by eventToToolEvent. */\nexport function projectAdapterToolEvent(\n\tadapter: Record<string, unknown>,\n\tevent: Record<string, unknown>,\n): Record<string, unknown> | null {\n\tconst eventType = String(adapter.event_type ?? \"\");\n\tconst payload = adapter.payload as Record<string, unknown> | undefined;\n\tif (!payload || typeof payload !== \"object\") return null;\n\tif (eventType !== \"tool_result\") return null;\n\n\tlet toolInput = payload.tool_input;\n\tif (toolInput == null || typeof toolInput !== \"object\") toolInput = {};\n\n\tlet toolError = payload.tool_error ?? null;\n\tif (toolError == null && payload.status === \"error\") toolError = payload.error ?? null;\n\n\tlet toolOutput = payload.tool_output ?? null;\n\tif (toolOutput == null && \"output\" in payload) toolOutput = payload.output ?? null;\n\n\treturn {\n\t\ttype: \"tool.execute.after\",\n\t\ttool: payload.tool_name,\n\t\targs: toolInput,\n\t\tresult: toolOutput,\n\t\terror: toolError,\n\t\ttimestamp: adapter.ts,\n\t\tcwd: event.cwd,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Tool event budgeting\n// ---------------------------------------------------------------------------\n\nfunction toolEventSignature(event: ToolEvent): string {\n\tif (event.toolName === \"bash\" && event.toolInput != null && typeof event.toolInput === \"object\") {\n\t\tconst input = event.toolInput as Record<string, unknown>;\n\t\tconst cmd = String(input.command ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tif ((cmd === \"git status\" || cmd === \"git diff\") && !event.toolError) {\n\t\t\treturn `bash:${cmd}`;\n\t\t}\n\t}\n\tconst parts: string[] = [event.toolName];\n\ttry {\n\t\tparts.push(JSON.stringify(event.toolInput));\n\t} catch {\n\t\tparts.push(String(event.toolInput));\n\t}\n\tif (event.toolError) parts.push(String(event.toolError).slice(0, 200));\n\tif (typeof event.toolOutput === \"string\" && event.toolOutput) {\n\t\tparts.push(event.toolOutput.slice(0, 200));\n\t}\n\treturn parts.join(\"|\");\n}\n\nfunction toolEventImportance(event: ToolEvent): number {\n\tlet score = 0;\n\tif (event.toolError) score += 100;\n\tconst tool = (event.toolName || \"\").toLowerCase();\n\tif (tool === \"edit\" || tool === \"write\") score += 50;\n\telse if (tool === \"bash\") score += 30;\n\telse if (tool === \"read\") score += 20;\n\telse score += 10;\n\treturn score;\n}\n\nfunction estimateEventSize(event: ToolEvent): number {\n\ttry {\n\t\treturn JSON.stringify(event).length;\n\t} catch {\n\t\treturn String(event).length;\n\t}\n}\n\n/**\n * Deduplicate, rank, and trim tool events to fit within budget.\n *\n * 1. Deduplicates (keeping last occurrence).\n * 2. If over maxEvents, keeps the most important ones.\n * 3. If over maxTotalChars, keeps the most important that fit.\n */\nexport function budgetToolEvents(\n\ttoolEvents: ToolEvent[],\n\tmaxTotalChars: number,\n\tmaxEvents: number,\n): ToolEvent[] {\n\tif (!toolEvents.length || maxTotalChars <= 0 || maxEvents <= 0) return [];\n\n\t// Deduplicate, preferring last occurrence\n\tconst seen = new Set<string>();\n\tconst deduped: ToolEvent[] = [];\n\tfor (let i = toolEvents.length - 1; i >= 0; i--) {\n\t\tconst evt = toolEvents[i];\n\t\tif (!evt) continue;\n\t\tconst sig = toolEventSignature(evt);\n\t\tif (seen.has(sig)) continue;\n\t\tseen.add(sig);\n\t\tdeduped.push(evt);\n\t}\n\tdeduped.reverse();\n\n\t// Trim to maxEvents by importance\n\tlet result = deduped;\n\tif (result.length > maxEvents) {\n\t\tconst indexed = result.map((e, i) => ({ event: e, idx: i }));\n\t\tindexed.sort((a, b) => {\n\t\t\tconst impDiff = toolEventImportance(b.event) - toolEventImportance(a.event);\n\t\t\tif (impDiff !== 0) return impDiff;\n\t\t\treturn a.idx - b.idx; // prefer earlier for same importance\n\t\t});\n\t\tconst keepIdxs = new Set(indexed.slice(0, maxEvents).map((x) => x.idx));\n\t\tresult = result.filter((_, i) => keepIdxs.has(i));\n\t}\n\n\t// Check total size\n\tconst totalSize = result.reduce((sum, e) => sum + estimateEventSize(e), 0);\n\tif (totalSize <= maxTotalChars) return result;\n\n\t// Budget by size — pick most important that fit\n\tconst indexed = result.map((e, i) => ({ event: e, idx: i }));\n\tindexed.sort((a, b) => {\n\t\tconst impDiff = toolEventImportance(b.event) - toolEventImportance(a.event);\n\t\tif (impDiff !== 0) return impDiff;\n\t\treturn a.idx - b.idx;\n\t});\n\n\tconst kept: { event: ToolEvent; idx: number }[] = [];\n\tlet runningTotal = 0;\n\tfor (const item of indexed) {\n\t\tconst size = estimateEventSize(item.event);\n\t\tif (runningTotal + size > maxTotalChars && kept.length > 0) continue;\n\t\tkept.push(item);\n\t\trunningTotal += size;\n\t\tif (runningTotal >= maxTotalChars) break;\n\t}\n\n\t// Restore original order\n\tkept.sort((a, b) => a.idx - b.idx);\n\treturn kept.map((x) => x.event);\n}\n","/**\n * Observer prompt construction for the ingest pipeline.\n *\n * Ports codemem/observer_prompts.py — builds the system + user prompt\n * sent to the observer LLM that extracts memories from session transcripts.\n */\n\nimport type { ObserverContext, ToolEvent } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants — prompt fragments matching Python's observer_prompts.py\n// ---------------------------------------------------------------------------\n\nconst OBSERVATION_TYPES = \"bugfix, feature, refactor, change, discovery, decision, exploration\";\n\nconst SYSTEM_IDENTITY =\n\t\"You are a memory observer creating searchable records of development work \" +\n\t\"FOR FUTURE SESSIONS. Record what was BUILT/FIXED/DEPLOYED/CONFIGURED/LEARNED, \" +\n\t\"not what you (the observer) are doing. These memories help developers \" +\n\t\"recall past work, decisions, learnings, and investigations.\";\n\nconst RECORDING_FOCUS = `Focus on deliverables, capabilities, AND learnings:\n- What the system NOW DOES differently (new capabilities)\n- What shipped to users/production (features, fixes, configs, docs)\n- What was LEARNED through debugging, investigation, or testing\n- How systems work and why they behave the way they do\n- Changes in technical domains (auth, data, UI, infra, DevOps)\n\nUse outcome-focused verbs: implemented, fixed, deployed, configured, migrated, optimized, added, refactored, discovered, learned, debugged.\nOnly describe actions that are clearly supported by the observed context.\nNever invent file changes, API behavior, or code edits that are not explicitly present in the session evidence.\n\nGOOD examples (describes what was built or learned):\n- \"Authentication now supports OAuth2 with PKCE flow\"\n- \"Deployment pipeline runs canary releases with auto-rollback\"\n- \"Fixed race condition in session handler causing duplicate events\"\n- \"Discovered flush timing strategy needed adaptation for multi-session environment\"\n- \"Learned transcript building was broken - empty strings passed instead of conversation\"\n\nBAD examples (describes observation process - DO NOT DO THIS):\n- \"Analyzed authentication implementation and stored findings in database\"\n- \"Tracked deployment steps and logged outcomes to memory system\"\n- \"Recorded investigation results for later reference\"`;\n\nconst SKIP_GUIDANCE = `Skip routine operations WITHOUT learnings:\n- Empty status checks or listings (unless revealing important state)\n- Package installations with no errors or insights\n- Simple file reads with no discoveries\n- Repetitive operations already documented with no new findings\nIf nothing meaningful happened AND nothing was learned:\n- Output no <observation> blocks\n- Output <skip_summary reason=\"low-signal\"/> instead of a <summary> block.`;\n\nconst NARRATIVE_GUIDANCE = `Create narratives that tell the complete story:\n- Context: What was the problem or goal? What prompted this work?\n- Investigation: What was examined? What was discovered?\n- Learning: How does it work? Why does it exist? Any gotchas?\n- Implementation: What was changed? What does the code do now?\n- Impact: What's better? What does the system do differently?\n- Next steps: What remains? What should future sessions know?\n\nAim for ~120-400 words per significant work item.\nPrefer fewer, higher-signal observations over many small ones.\nInclude specific details when present: file paths, function names, configuration values.`;\n\nconst OUTPUT_GUIDANCE =\n\t\"Output only XML. Do not include commentary outside XML.\\n\\n\" +\n\t\"ALWAYS emit at least one <observation> block for any meaningful work. \" +\n\t\"Observations are the PRIMARY output - they capture what was built, fixed, learned, or decided. \" +\n\t\"Also emit a <summary> block to track session progress.\\n\\n\" +\n\t\"Prefer fewer, more comprehensive observations over many small ones.\";\n\nconst OBSERVATION_SCHEMA = `<observation>\n <type>[ ${OBSERVATION_TYPES} ]</type>\n <!--\n type MUST be EXACTLY one of these 7 options:\n - bugfix: something was broken, now fixed\n - feature: new capability or functionality added\n - refactor: code restructured, behavior unchanged\n - change: generic modification (docs, config, misc)\n - discovery: learning about existing system, debugging insights\n - decision: architectural/design choice with rationale\n - exploration: attempted approach that was tried but NOT shipped\n\n IMPORTANT: Use 'exploration' when:\n - Multiple approaches were tried for the same problem\n - An implementation was attempted but then replaced or reverted\n - Something was tested/experimented with but not kept\n - The attempt provides useful \"why we didn't do X\" context\n\n Exploration memories are valuable. They prevent repeating failed approaches.\n Include what was tried AND why it didn't work out.\n -->\n <title>[Short outcome-focused title - what was achieved or learned]</title>\n <!-- GOOD: \"OAuth2 PKCE flow added to authentication\" -->\n <!-- GOOD: \"Discovered flush strategy fails in multi-session environments\" -->\n <!-- GOOD (exploration): \"Tried emoji theme toggle - poor contrast on light backgrounds\" -->\n <!-- BAD: \"Analyzed authentication code\" (too vague, no outcome) -->\n <subtitle>[One sentence explanation of the outcome (max 24 words)]</subtitle>\n <facts>\n <fact>[Specific, self-contained statement with concrete details]</fact>\n <fact>[Include: file paths, function names, config values, error messages]</fact>\n <fact>[Each fact must stand alone - no pronouns like \"it\" or \"this\"]</fact>\n </facts>\n <narrative>[\n Full context: What was done, how it works, why it matters.\n For discoveries/debugging: what was investigated, what was found, what it means.\n For explorations: what was tried, why it didn't work, what was done instead.\n Include specific details: file paths, function names, configuration values.\n Aim for 100-500 words - enough to be useful, not overwhelming.\n ]</narrative>\n <concepts>\n <concept>[how-it-works, why-it-exists, what-changed, problem-solution, gotcha, pattern, trade-off]</concept>\n </concepts>\n <!-- concepts: 2-5 knowledge categories from the list above -->\n <files_read>\n <file>[full path from project root]</file>\n </files_read>\n <files_modified>\n <file>[full path from project root]</file>\n </files_modified>\n</observation>`;\n\nconst SUMMARY_SCHEMA = `<summary>\n <request>[What did the user request? What was the goal of this work session?]</request>\n\n <investigated>[What was explored or examined? What files, systems, logs were reviewed?\n What questions were asked? What did you try to understand?]</investigated>\n\n <learned>[What was learned about how things work? Any discoveries about the codebase,\n architecture, or domain? Gotchas or surprises? Understanding gained?]</learned>\n\n <completed>[What work was done? What shipped? What does the system do now that it\n didn't before? Be specific: files changed, features added, bugs fixed.]</completed>\n\n <next_steps>[What are the logical next steps? What remains to be done? What should\n the next session pick up? Any blockers or dependencies?]</next_steps>\n\n <notes>[Additional context, insights, or warnings. Anything future sessions should\n know that doesn't fit above. Design decisions, trade-offs, alternatives considered.]</notes>\n\n <files_read>\n <file>[path]</file>\n </files_read>\n <files_modified>\n <file>[path]</file>\n </files_modified>\n</summary>\n\nIf nothing meaningful happened, emit <skip_summary reason=\"low-signal\"/> and do not emit <summary>.\nOtherwise, write a summary that explains the current state of the PRIMARY work (not your observation process).\n\nIf the user prompt is a short approval or acknowledgement (\"yes\", \"ok\", \"approved\"),\ninfer the request from the observed work and the completed/learned sections instead.\n\nOnly summarize what is evidenced in the session context. Do not infer or fabricate\nfile edits, behaviors, or outcomes that are not explicitly observed.\n\nKeep summaries concise (aim for ~150-450 words total across all fields).\n\nThis summary helps future sessions understand where this work left off.`;\n\n// ---------------------------------------------------------------------------\n// XML escaping\n// ---------------------------------------------------------------------------\n\nfunction escapeXml(text: string): string {\n\treturn text\n\t\t.replace(/&/g, \"&amp;\")\n\t\t.replace(/</g, \"&lt;\")\n\t\t.replace(/>/g, \"&gt;\")\n\t\t.replace(/\"/g, \"&quot;\")\n\t\t.replace(/'/g, \"&apos;\");\n}\n\n// ---------------------------------------------------------------------------\n// Formatting helpers\n// ---------------------------------------------------------------------------\n\nfunction formatJson(value: unknown): string {\n\tif (value == null) return \"\";\n\tif (typeof value === \"string\") return value;\n\ttry {\n\t\treturn JSON.stringify(value, null, 2);\n\t} catch {\n\t\treturn String(value);\n\t}\n}\n\nfunction formatToolEvent(event: ToolEvent): string {\n\tconst parts = [\"<observed_from_primary_session>\"];\n\tparts.push(` <what_happened>${escapeXml(event.toolName)}</what_happened>`);\n\tif (event.timestamp) {\n\t\tparts.push(` <occurred_at>${escapeXml(event.timestamp)}</occurred_at>`);\n\t}\n\tif (event.cwd) {\n\t\tparts.push(` <working_directory>${escapeXml(event.cwd)}</working_directory>`);\n\t}\n\tconst params = escapeXml(formatJson(event.toolInput));\n\tconst outcome = escapeXml(formatJson(event.toolOutput));\n\tconst error = escapeXml(formatJson(event.toolError));\n\tif (params) parts.push(` <parameters>${params}</parameters>`);\n\tif (outcome) parts.push(` <outcome>${outcome}</outcome>`);\n\tif (error) parts.push(` <error>${error}</error>`);\n\tparts.push(\"</observed_from_primary_session>\");\n\treturn parts.join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Build the observer prompt from session context.\n *\n * Returns `{ system, user }` — the system prompt contains identity + schema,\n * the user prompt contains the observed session context.\n *\n * The Python version concatenates everything into one prompt string passed as\n * the user message. We split system/user for cleaner API mapping, but the\n * content is equivalent.\n */\nexport function buildObserverPrompt(context: ObserverContext): {\n\tsystem: string;\n\tuser: string;\n} {\n\t// System prompt: identity + guidance + schemas\n\tconst systemBlocks: string[] = [\n\t\tSYSTEM_IDENTITY,\n\t\t\"\",\n\t\tRECORDING_FOCUS,\n\t\t\"\",\n\t\tSKIP_GUIDANCE,\n\t\t\"\",\n\t\tNARRATIVE_GUIDANCE,\n\t\t\"\",\n\t\tOUTPUT_GUIDANCE,\n\t\t\"\",\n\t\t\"Observation XML schema:\",\n\t\tOBSERVATION_SCHEMA,\n\t];\n\tif (context.includeSummary) {\n\t\tsystemBlocks.push(\"\", \"Summary XML schema:\", SUMMARY_SCHEMA);\n\t}\n\tconst system = systemBlocks.join(\"\\n\\n\").trim();\n\n\t// User prompt: observed session context\n\tconst userBlocks: string[] = [\"Observed session context:\"];\n\n\tif (context.userPrompt) {\n\t\tconst promptBlock = [\"<observed_from_primary_session>\"];\n\t\tpromptBlock.push(` <user_request>${escapeXml(context.userPrompt)}</user_request>`);\n\t\tif (context.promptNumber != null) {\n\t\t\tpromptBlock.push(` <prompt_number>${context.promptNumber}</prompt_number>`);\n\t\t}\n\t\tif (context.project) {\n\t\t\tpromptBlock.push(` <project>${escapeXml(context.project)}</project>`);\n\t\t}\n\t\tpromptBlock.push(\"</observed_from_primary_session>\");\n\t\tuserBlocks.push(promptBlock.join(\"\\n\"));\n\t}\n\n\tif (context.diffSummary) {\n\t\tuserBlocks.push(\n\t\t\t`<observed_from_primary_session>\\n <diff_summary>${escapeXml(context.diffSummary)}</diff_summary>\\n</observed_from_primary_session>`,\n\t\t);\n\t}\n\n\tif (context.recentFiles) {\n\t\tuserBlocks.push(\n\t\t\t`<observed_from_primary_session>\\n <recent_files>${escapeXml(context.recentFiles)}</recent_files>\\n</observed_from_primary_session>`,\n\t\t);\n\t}\n\n\tfor (const event of context.toolEvents) {\n\t\tuserBlocks.push(formatToolEvent(event));\n\t}\n\n\tif (context.includeSummary && context.lastAssistantMessage) {\n\t\tuserBlocks.push(\"Summary context:\");\n\t\tuserBlocks.push(\n\t\t\t`<summary_context>\\n <assistant_response>${escapeXml(context.lastAssistantMessage)}</assistant_response>\\n</summary_context>`,\n\t\t);\n\t}\n\n\tconst user = userBlocks.join(\"\\n\\n\").trim();\n\n\treturn { system, user };\n}\n","/**\n * Transcript building and request analysis for the ingest pipeline.\n *\n * Ports codemem/ingest/transcript.py + relevant extraction functions\n * from codemem/plugin_ingest.py.\n */\n\nimport { stripPrivate } from \"./ingest-sanitize.js\";\nimport type { ParsedSummary } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Trivial request detection\n// ---------------------------------------------------------------------------\n\nexport const TRIVIAL_REQUESTS = new Set([\n\t\"yes\",\n\t\"y\",\n\t\"ok\",\n\t\"okay\",\n\t\"approved\",\n\t\"approve\",\n\t\"looks good\",\n\t\"lgtm\",\n\t\"ship it\",\n\t\"sounds good\",\n\t\"sure\",\n\t\"go ahead\",\n\t\"proceed\",\n]);\n\n/** Normalize request text for comparison: trim, strip quotes, collapse whitespace, lowercase. */\nexport function normalizeRequestText(text: string | null): string {\n\tif (!text) return \"\";\n\tlet cleaned = text\n\t\t.trim()\n\t\t.replace(/^[\"']+|[\"']+$/g, \"\")\n\t\t.trim();\n\tcleaned = cleaned.replace(/\\s+/g, \" \");\n\treturn cleaned.toLowerCase();\n}\n\n/** Check whether the text is a trivial approval/acknowledgement. */\nexport function isTrivialRequest(text: string | null): boolean {\n\tconst normalized = normalizeRequestText(text);\n\tif (!normalized) return true;\n\treturn TRIVIAL_REQUESTS.has(normalized);\n}\n\n// ---------------------------------------------------------------------------\n// Sentence extraction\n// ---------------------------------------------------------------------------\n\n/** Extract the first sentence from text (strip markdown prefixes). */\nexport function firstSentence(text: string): string {\n\tlet cleaned = text\n\t\t.split(\"\\n\")\n\t\t.map((l) => l.trim())\n\t\t.filter(Boolean)\n\t\t.join(\" \");\n\tcleaned = cleaned.replace(/^[#*\\-\\d.\\s]+/, \"\");\n\tconst parts = cleaned.split(/(?<=[.!?])\\s+/);\n\treturn (parts[0] ?? cleaned).trim();\n}\n\n/** Derive a meaningful request from summary fields when the original is trivial. */\nexport function deriveRequest(summary: ParsedSummary): string {\n\tconst candidates = [\n\t\tsummary.completed,\n\t\tsummary.learned,\n\t\tsummary.investigated,\n\t\tsummary.nextSteps,\n\t\tsummary.notes,\n\t];\n\tfor (const candidate of candidates) {\n\t\tif (candidate) return firstSentence(candidate);\n\t}\n\treturn \"\";\n}\n\n// ---------------------------------------------------------------------------\n// Transcript building\n// ---------------------------------------------------------------------------\n\n/**\n * Build a chronological transcript from user prompts and assistant messages.\n * Strips private content before including text.\n */\nexport function buildTranscript(events: Record<string, unknown>[]): string {\n\tconst parts: string[] = [];\n\tfor (const event of events) {\n\t\tconst eventType = event.type;\n\t\tif (eventType === \"user_prompt\") {\n\t\t\tconst promptText = stripPrivate(String(event.prompt_text ?? \"\")).trim();\n\t\t\tif (promptText) parts.push(`User: ${promptText}`);\n\t\t} else if (eventType === \"assistant_message\") {\n\t\t\tconst assistantText = stripPrivate(String(event.assistant_text ?? \"\")).trim();\n\t\t\tif (assistantText) parts.push(`Assistant: ${assistantText}`);\n\t\t}\n\t}\n\treturn parts.join(\"\\n\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Extraction helpers\n// ---------------------------------------------------------------------------\n\n/** Extract assistant text messages from raw events. */\nexport function extractAssistantMessages(events: Record<string, unknown>[]): string[] {\n\tconst messages: string[] = [];\n\tfor (const event of events) {\n\t\tif (event.type !== \"assistant_message\") continue;\n\t\tconst text = String(event.assistant_text ?? \"\").trim();\n\t\tif (text) messages.push(text);\n\t}\n\treturn messages;\n}\n\n/** Extract token usage events from assistant_usage events. */\nexport function extractAssistantUsage(events: Record<string, unknown>[]): Record<string, number>[] {\n\tconst usageEvents: Record<string, number>[] = [];\n\tfor (const event of events) {\n\t\tif (event.type !== \"assistant_usage\") continue;\n\t\tconst usage = event.usage;\n\t\tif (usage == null || typeof usage !== \"object\") continue;\n\t\tconst u = usage as Record<string, unknown>;\n\t\tconst inputTokens = Number(u.input_tokens ?? 0) || 0;\n\t\tconst outputTokens = Number(u.output_tokens ?? 0) || 0;\n\t\tconst cacheCreation = Number(u.cache_creation_input_tokens ?? 0) || 0;\n\t\tconst cacheRead = Number(u.cache_read_input_tokens ?? 0) || 0;\n\t\tconst total = inputTokens + outputTokens + cacheCreation;\n\t\tif (total <= 0) continue;\n\t\tusageEvents.push({\n\t\t\tinput_tokens: inputTokens,\n\t\t\toutput_tokens: outputTokens,\n\t\t\tcache_creation_input_tokens: cacheCreation,\n\t\t\tcache_read_input_tokens: cacheRead,\n\t\t\ttotal_tokens: total,\n\t\t});\n\t}\n\treturn usageEvents;\n}\n\n/** Extract user prompts with prompt numbers from raw events. */\nexport function extractPrompts(\n\tevents: Record<string, unknown>[],\n): { promptText: string; promptNumber: number | null; timestamp: string | null }[] {\n\tconst prompts: { promptText: string; promptNumber: number | null; timestamp: string | null }[] =\n\t\t[];\n\tfor (const event of events) {\n\t\tif (event.type !== \"user_prompt\") continue;\n\t\tconst promptText = String(event.prompt_text ?? \"\").trim();\n\t\tif (!promptText) continue;\n\t\tprompts.push({\n\t\t\tpromptText,\n\t\t\tpromptNumber: typeof event.prompt_number === \"number\" ? event.prompt_number : null,\n\t\t\ttimestamp: typeof event.timestamp === \"string\" ? event.timestamp : null,\n\t\t});\n\t}\n\treturn prompts;\n}\n\n/**\n * Project adapter events into the flat event format used by transcript building.\n *\n * Adapter events use the `_adapter` envelope (schema v1.0). This function\n * converts prompt and assistant adapter events into the standard\n * `user_prompt` / `assistant_message` shapes so buildTranscript can\n * process them uniformly.\n */\nexport function normalizeAdapterEvents(\n\tevents: Record<string, unknown>[],\n): Record<string, unknown>[] {\n\treturn events.map((event) => {\n\t\tconst adapter = event._adapter;\n\t\tif (adapter == null || typeof adapter !== \"object\" || Array.isArray(adapter)) return event;\n\t\tconst a = adapter as Record<string, unknown>;\n\t\tif (a.schema_version !== \"1.0\") return event;\n\n\t\tconst payload = a.payload;\n\t\tif (payload == null || typeof payload !== \"object\") return event;\n\t\tconst p = payload as Record<string, unknown>;\n\n\t\tconst eventType = String(a.event_type ?? \"\");\n\t\tif (eventType === \"prompt\") {\n\t\t\tconst text = String(p.text ?? \"\").trim();\n\t\t\tif (!text) return event;\n\t\t\treturn {\n\t\t\t\ttype: \"user_prompt\",\n\t\t\t\tprompt_text: text,\n\t\t\t\tprompt_number: p.prompt_number ?? null,\n\t\t\t\ttimestamp: a.ts ?? null,\n\t\t\t};\n\t\t}\n\t\tif (eventType === \"assistant\") {\n\t\t\tconst text = String(p.text ?? \"\").trim();\n\t\t\tif (!text) return event;\n\t\t\treturn {\n\t\t\t\ttype: \"assistant_message\",\n\t\t\t\tassistant_text: text,\n\t\t\t\ttimestamp: a.ts ?? null,\n\t\t\t};\n\t\t}\n\t\treturn event;\n\t});\n}\n","/**\n * XML response parser for the observer LLM output.\n *\n * Ports codemem/xml_parser.py — uses regex-based parsing to extract\n * observations and session summaries from the observer's XML response.\n *\n * The observer output is structured XML, not arbitrary HTML, so regex\n * is sufficient (no DOM parser needed).\n */\n\nimport type { ParsedObservation, ParsedOutput, ParsedSummary } from \"./ingest-types.js\";\n\n// ---------------------------------------------------------------------------\n// Regex patterns\n// ---------------------------------------------------------------------------\n\n// Match <observation> with optional attributes (LLMs sometimes add kind=\"...\")\nconst OBSERVATION_BLOCK_RE = /<observation[^>]*>.*?<\\/observation>/gs;\nconst SUMMARY_BLOCK_RE = /<summary[^>]*>.*?<\\/summary>/gs;\nconst SKIP_SUMMARY_RE = /<skip_summary(?:\\s+reason=\"(?<reason>[^\"]+)\")?\\s*\\/>/i;\nconst CODE_FENCE_RE = /```(?:xml)?/gi;\n\n// ---------------------------------------------------------------------------\n// Text extraction helpers\n// ---------------------------------------------------------------------------\n\n/** Remove code fences and trim whitespace. */\nfunction cleanXmlText(text: string): string {\n\treturn text.replace(CODE_FENCE_RE, \"\").trim();\n}\n\n/** Extract text content from within a single XML tag. Returns empty string if not found. */\nfunction extractTagText(xml: string, tag: string): string {\n\tconst re = new RegExp(`<${tag}[^>]*>([\\\\s\\\\S]*?)</${tag}>`, \"i\");\n\tconst match = re.exec(xml);\n\tif (!match?.[1]) return \"\";\n\treturn match[1].trim();\n}\n\n/** Extract text content of repeated child elements within a parent tag. */\nfunction extractChildTexts(xml: string, parentTag: string, childTag: string): string[] {\n\tconst parentRe = new RegExp(`<${parentTag}[^>]*>([\\\\s\\\\S]*?)</${parentTag}>`, \"i\");\n\tconst parentMatch = parentRe.exec(xml);\n\tif (!parentMatch?.[1]) return [];\n\n\tconst childRe = new RegExp(`<${childTag}[^>]*>([\\\\s\\\\S]*?)</${childTag}>`, \"gi\");\n\tconst items: string[] = [];\n\tfor (\n\t\tlet match = childRe.exec(parentMatch[1]);\n\t\tmatch !== null;\n\t\tmatch = childRe.exec(parentMatch[1])\n\t) {\n\t\tconst text = match[1]?.trim();\n\t\tif (text) items.push(text);\n\t}\n\treturn items;\n}\n\n// ---------------------------------------------------------------------------\n// Block parsers\n// ---------------------------------------------------------------------------\n\nfunction parseObservationBlock(block: string): ParsedObservation | null {\n\t// Minimal validation — must have at least a type or title\n\tconst kind = extractTagText(block, \"type\");\n\tconst title = extractTagText(block, \"title\");\n\tif (!kind && !title) return null;\n\n\treturn {\n\t\tkind,\n\t\ttitle,\n\t\tnarrative: extractTagText(block, \"narrative\"),\n\t\tsubtitle: extractTagText(block, \"subtitle\") || null,\n\t\tfacts: extractChildTexts(block, \"facts\", \"fact\"),\n\t\tconcepts: extractChildTexts(block, \"concepts\", \"concept\"),\n\t\tfilesRead: extractChildTexts(block, \"files_read\", \"file\"),\n\t\tfilesModified: extractChildTexts(block, \"files_modified\", \"file\"),\n\t};\n}\n\nfunction parseSummaryBlock(block: string): ParsedSummary | null {\n\tconst request = extractTagText(block, \"request\");\n\tconst investigated = extractTagText(block, \"investigated\");\n\tconst learned = extractTagText(block, \"learned\");\n\tconst completed = extractTagText(block, \"completed\");\n\tconst nextSteps = extractTagText(block, \"next_steps\");\n\tconst notes = extractTagText(block, \"notes\");\n\n\t// At least one field must be populated\n\tif (!request && !investigated && !learned && !completed && !nextSteps && !notes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\trequest,\n\t\tinvestigated,\n\t\tlearned,\n\t\tcompleted,\n\t\tnextSteps,\n\t\tnotes,\n\t\tfilesRead: extractChildTexts(block, \"files_read\", \"file\"),\n\t\tfilesModified: extractChildTexts(block, \"files_modified\", \"file\"),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Parse the observer LLM's XML response into structured data.\n *\n * Extracts all `<observation>` blocks and the last `<summary>` block.\n * Handles missing/empty tags gracefully.\n */\nexport function parseObserverResponse(raw: string): ParsedOutput {\n\tconst cleaned = cleanXmlText(raw);\n\n\t// Extract observations\n\tconst observations: ParsedObservation[] = [];\n\tconst obsBlocks = cleaned.match(OBSERVATION_BLOCK_RE) ?? [];\n\tfor (const block of obsBlocks) {\n\t\tconst parsed = parseObservationBlock(block);\n\t\tif (parsed) observations.push(parsed);\n\t}\n\n\t// Extract summary (use last block if multiple)\n\tlet summary: ParsedSummary | null = null;\n\tconst summaryBlocks = cleaned.match(SUMMARY_BLOCK_RE) ?? [];\n\tconst lastSummaryBlock = summaryBlocks.at(-1);\n\tif (lastSummaryBlock) {\n\t\tsummary = parseSummaryBlock(lastSummaryBlock);\n\t}\n\n\t// Check for skip_summary\n\tconst skipMatch = SKIP_SUMMARY_RE.exec(cleaned);\n\tconst skipReason = skipMatch?.groups?.reason ?? null;\n\n\treturn { observations, summary, skipSummaryReason: skipReason };\n}\n\n/** Return true if at least one observation has a title or narrative. */\nexport function hasMeaningfulObservation(observations: ParsedObservation[]): boolean {\n\treturn observations.some((obs) => obs.title || obs.narrative);\n}\n","/**\n * Tag derivation — port of Python's codemem/store/tags.py.\n *\n * Derives searchable tags from memory kind, title, concepts, and file paths.\n * Used at ingest time to populate memory_items.tags_text inline (matching\n * Python's store_observation → derive_tags flow).\n */\n\nconst STOPWORDS = new Set([\n\t\"the\",\n\t\"and\",\n\t\"for\",\n\t\"with\",\n\t\"this\",\n\t\"that\",\n\t\"from\",\n\t\"are\",\n\t\"was\",\n\t\"were\",\n\t\"has\",\n\t\"have\",\n\t\"had\",\n\t\"not\",\n\t\"but\",\n\t\"can\",\n\t\"will\",\n\t\"all\",\n\t\"been\",\n\t\"each\",\n\t\"which\",\n\t\"their\",\n\t\"said\",\n\t\"its\",\n\t\"into\",\n\t\"than\",\n\t\"other\",\n\t\"some\",\n\t\"could\",\n\t\"them\",\n\t\"about\",\n\t\"then\",\n\t\"made\",\n\t\"after\",\n\t\"many\",\n\t\"also\",\n\t\"did\",\n\t\"just\",\n\t\"should\",\n\t\"over\",\n\t\"such\",\n\t\"there\",\n\t\"would\",\n\t\"more\",\n\t\"now\",\n\t\"very\",\n\t\"when\",\n\t\"what\",\n\t\"your\",\n\t\"how\",\n\t\"out\",\n\t\"our\",\n\t\"his\",\n\t\"her\",\n\t\"she\",\n\t\"him\",\n\t\"most\",\n]);\n\n/**\n * Normalize a raw value into a tag-safe lowercase slug.\n * Returns empty string if the value is empty, a stopword, or too short.\n */\nexport function normalizeTag(value: string, stopwords?: Set<string>): string {\n\tlet lowered = (value || \"\").trim().toLowerCase();\n\tif (!lowered) return \"\";\n\tlowered = lowered\n\t\t.replace(/[^a-z0-9_]+/g, \"-\")\n\t\t.replace(/-+/g, \"-\")\n\t\t.replace(/^-|-$/g, \"\");\n\tif (!lowered) return \"\";\n\tconst words = stopwords ?? STOPWORDS;\n\tif (words.has(lowered)) return \"\";\n\tif (lowered.length > 40) lowered = lowered.slice(0, 40).replace(/-$/, \"\");\n\treturn lowered;\n}\n\n/**\n * Extract tags from a file path (basename, parent dir, top-level dir).\n */\nexport function fileTags(pathValue: string, stopwords?: Set<string>): string[] {\n\tconst raw = (pathValue || \"\").trim();\n\tif (!raw) return [];\n\tconst parts = raw.split(/[\\\\/]+/).filter((p): p is string => !!p && p !== \".\" && p !== \"..\");\n\tif (!parts.length) return [];\n\n\tconst tags: string[] = [];\n\tconst last = parts.at(-1);\n\tif (last) {\n\t\tconst basename = normalizeTag(last, stopwords);\n\t\tif (basename) tags.push(basename);\n\t}\n\tif (parts.length >= 2) {\n\t\tconst secondLast = parts.at(-2);\n\t\tif (secondLast) {\n\t\t\tconst parent = normalizeTag(secondLast, stopwords);\n\t\t\tif (parent) tags.push(parent);\n\t\t}\n\t}\n\tif (parts.length >= 3) {\n\t\tconst first = parts.at(0);\n\t\tif (first) {\n\t\t\tconst top = normalizeTag(first, stopwords);\n\t\t\tif (top) tags.push(top);\n\t\t}\n\t}\n\treturn tags;\n}\n\n/**\n * Derive tags from memory metadata, matching Python's derive_tags().\n *\n * Sources (in priority order):\n * 1. Memory kind\n * 2. Concepts (from observer XML output)\n * 3. File paths (read + modified)\n * 4. Title tokens (fallback when nothing else produced tags)\n */\nexport function deriveTags(opts: {\n\tkind: string;\n\ttitle?: string;\n\tconcepts?: string[];\n\tfilesRead?: string[];\n\tfilesModified?: string[];\n\tstopwords?: Set<string>;\n}): string[] {\n\tconst words = opts.stopwords ?? STOPWORDS;\n\tconst tags: string[] = [];\n\n\tconst kindTag = normalizeTag(opts.kind, words);\n\tif (kindTag) tags.push(kindTag);\n\n\tfor (const concept of opts.concepts ?? []) {\n\t\tconst normalized = normalizeTag(concept, words);\n\t\tif (normalized) tags.push(normalized);\n\t}\n\n\tfor (const pathValue of [...(opts.filesRead ?? []), ...(opts.filesModified ?? [])]) {\n\t\ttags.push(...fileTags(pathValue, words));\n\t}\n\n\t// Fallback: extract tokens from title if no other tags were found\n\tif (!tags.length && opts.title) {\n\t\tfor (const match of opts.title.toLowerCase().matchAll(/[a-z0-9_]+/g)) {\n\t\t\tconst normalized = normalizeTag(match[0], words);\n\t\t\tif (normalized) tags.push(normalized);\n\t\t}\n\t}\n\n\t// Dedupe while preserving order, cap at 20\n\tconst deduped: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const tag of tags) {\n\t\tif (seen.has(tag)) continue;\n\t\tseen.add(tag);\n\t\tdeduped.push(tag);\n\t\tif (deduped.length >= 20) break;\n\t}\n\treturn deduped;\n}\n","/**\n * Vector store operations for semantic search.\n *\n * Ports codemem/store/vectors.py — backfill and on-insert vector writes\n * against the sqlite-vec `memory_vectors` virtual table.\n *\n * All functions accept a raw better-sqlite3 Database so they work outside\n * the MemoryStore class. Embedding is async; callers await then write\n * synchronously (matches the runtime-topology decision: main thread owns DB).\n */\n\nimport type { Database } from \"./db.js\";\nimport {\n\tchunkText,\n\tembedTexts,\n\tgetEmbeddingClient,\n\thashText,\n\tserializeFloat32,\n} from \"./embeddings.js\";\nimport { projectClause } from \"./project.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface BackfillVectorsResult {\n\tchecked: number;\n\tembedded: number;\n\tinserted: number;\n\tskipped: number;\n}\n\nexport interface BackfillVectorsOptions {\n\tlimit?: number | null;\n\tsince?: string | null;\n\tproject?: string | null;\n\tactiveOnly?: boolean;\n\tdryRun?: boolean;\n\tmemoryIds?: number[] | null;\n}\n\n// ---------------------------------------------------------------------------\n// storeVectors — called inline when a memory is created/remembered\n// ---------------------------------------------------------------------------\n\n/**\n * Embed and store vectors for a single memory item.\n * No-op when embeddings are disabled or the client is unavailable.\n */\nexport async function storeVectors(\n\tdb: Database,\n\tmemoryId: number,\n\ttitle: string,\n\tbodyText: string,\n): Promise<void> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return;\n\n\tconst text = `${title}\\n${bodyText}`.trim();\n\tconst chunks = chunkText(text);\n\tif (chunks.length === 0) return;\n\n\tconst embeddings = await embedTexts(chunks);\n\tif (embeddings.length === 0) return;\n\n\tconst model = client.model;\n\tconst stmt = db.prepare(\n\t\t\"INSERT INTO memory_vectors(embedding, memory_id, chunk_index, content_hash, model) VALUES (?, ?, ?, ?, ?)\",\n\t);\n\n\tfor (let i = 0; i < chunks.length && i < embeddings.length; i++) {\n\t\tconst vector = embeddings[i];\n\t\tif (!vector || vector.length === 0) continue;\n\t\tstmt.run(serializeFloat32(vector), memoryId, i, hashText(chunks[i]!), model);\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// backfillVectors — CLI batch backfill\n// ---------------------------------------------------------------------------\n\n/**\n * Backfill vectors for memories that don't have them yet.\n * Matches Python's `backfill_vectors()` in store/vectors.py.\n */\nexport async function backfillVectors(\n\tdb: Database,\n\topts: BackfillVectorsOptions = {},\n): Promise<BackfillVectorsResult> {\n\tconst client = await getEmbeddingClient();\n\tif (!client) return { checked: 0, embedded: 0, inserted: 0, skipped: 0 };\n\n\tconst { limit, since, project, activeOnly = true, dryRun = false, memoryIds } = opts;\n\n\tconst params: unknown[] = [];\n\tconst whereClauses: string[] = [];\n\n\tif (activeOnly) whereClauses.push(\"memory_items.active = 1\");\n\tif (since) {\n\t\twhereClauses.push(\"memory_items.created_at >= ?\");\n\t\tparams.push(since);\n\t}\n\tif (project) {\n\t\tconst pc = projectClause(project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t}\n\t}\n\tif (memoryIds && memoryIds.length > 0) {\n\t\tconst placeholders = memoryIds.map(() => \"?\").join(\",\");\n\t\twhereClauses.push(`memory_items.id IN (${placeholders})`);\n\t\tparams.push(...memoryIds);\n\t}\n\n\tconst where = whereClauses.length > 0 ? whereClauses.join(\" AND \") : \"1=1\";\n\tconst joinSessions = project != null;\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\tconst limitClause = limit != null && limit > 0 ? \"LIMIT ?\" : \"\";\n\tif (limit != null && limit > 0) params.push(limit);\n\n\tconst rows = db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.id, memory_items.title, memory_items.body_text\n\t\t\t FROM memory_items ${joinClause}\n\t\t\t WHERE ${where}\n\t\t\t ORDER BY memory_items.created_at ASC ${limitClause}`,\n\t\t)\n\t\t.all(...params) as Array<{ id: number; title: string | null; body_text: string | null }>;\n\n\tconst model = client.model;\n\tlet checked = 0;\n\tlet embedded = 0;\n\tlet inserted = 0;\n\tlet skipped = 0;\n\n\tfor (const row of rows) {\n\t\tchecked++;\n\t\tconst text = `${row.title ?? \"\"}\\n${row.body_text ?? \"\"}`.trim();\n\t\tconst chunks = chunkText(text);\n\t\tif (chunks.length === 0) continue;\n\n\t\t// Check existing hashes\n\t\tconst existingRows = db\n\t\t\t.prepare(\"SELECT content_hash FROM memory_vectors WHERE memory_id = ? AND model = ?\")\n\t\t\t.all(row.id, model) as Array<{ content_hash: string | null }>;\n\t\tconst existingHashes = new Set(\n\t\t\texistingRows.map((r) => r.content_hash).filter((h): h is string => h != null),\n\t\t);\n\n\t\tconst pendingChunks: string[] = [];\n\t\tconst pendingHashes: string[] = [];\n\t\tfor (const chunk of chunks) {\n\t\t\tconst h = hashText(chunk);\n\t\t\tif (existingHashes.has(h)) {\n\t\t\t\tskipped++;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpendingChunks.push(chunk);\n\t\t\tpendingHashes.push(h);\n\t\t}\n\n\t\tif (pendingChunks.length === 0) continue;\n\n\t\tconst embeddings = await embedTexts(pendingChunks);\n\t\tif (embeddings.length === 0) continue;\n\t\tembedded += embeddings.length;\n\n\t\tif (dryRun) {\n\t\t\tinserted += embeddings.length;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst stmt = db.prepare(\n\t\t\t\"INSERT INTO memory_vectors(embedding, memory_id, chunk_index, content_hash, model) VALUES (?, ?, ?, ?, ?)\",\n\t\t);\n\t\tfor (let i = 0; i < embeddings.length; i++) {\n\t\t\tconst vector = embeddings[i];\n\t\t\tif (!vector || vector.length === 0) continue;\n\t\t\tstmt.run(serializeFloat32(vector), row.id, i, pendingHashes[i], model);\n\t\t\tinserted++;\n\t\t}\n\t}\n\n\treturn { checked, embedded, inserted, skipped };\n}\n\n// ---------------------------------------------------------------------------\n// semanticSearch — vector KNN query\n// ---------------------------------------------------------------------------\n\nexport interface SemanticSearchResult {\n\tid: number;\n\tkind: string;\n\ttitle: string;\n\tbody_text: string;\n\tconfidence: number;\n\ttags_text: string;\n\tmetadata_json: string | null;\n\tcreated_at: string;\n\tupdated_at: string;\n\tsession_id: number;\n\tscore: number;\n\tdistance: number;\n}\n\n/**\n * Search for memories by vector similarity (KNN via sqlite-vec MATCH).\n * Returns an empty array when embeddings are disabled or unavailable.\n *\n * Matches Python's `_semantic_search()` in store/search.py.\n */\nexport async function semanticSearch(\n\tdb: Database,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: { project?: string | null } | null,\n): Promise<SemanticSearchResult[]> {\n\tif (query.trim().length < 3) return [];\n\n\tconst embeddings = await embedTexts([query]);\n\tif (embeddings.length === 0) return [];\n\n\tconst firstEmbedding = embeddings[0];\n\tif (!firstEmbedding) return [];\n\tconst queryEmbedding = serializeFloat32(firstEmbedding);\n\tconst params: unknown[] = [queryEmbedding, limit];\n\tconst whereClauses: string[] = [\"memory_items.active = 1\"];\n\tlet joinSessions = false;\n\n\tif (filters?.project) {\n\t\tconst pc = projectClause(filters.project);\n\t\tif (pc.clause) {\n\t\t\twhereClauses.push(pc.clause);\n\t\t\tparams.push(...pc.params);\n\t\t\tjoinSessions = true;\n\t\t}\n\t}\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = joinSessions ? \"JOIN sessions ON sessions.id = memory_items.session_id\" : \"\";\n\n\tconst sql = `\n\t\tSELECT memory_items.*, memory_vectors.distance\n\t\tFROM memory_vectors\n\t\tJOIN memory_items ON memory_items.id = memory_vectors.memory_id\n\t\t${joinClause}\n\t\tWHERE memory_vectors.embedding MATCH ?\n\t\t AND k = ?\n\t\t AND ${where}\n\t\tORDER BY memory_vectors.distance ASC\n\t`;\n\n\tconst rows = db.prepare(sql).all(...params) as Array<Record<string, unknown>>;\n\n\treturn rows.map((row) => ({\n\t\tid: Number(row.id),\n\t\tkind: String(row.kind ?? \"observation\"),\n\t\ttitle: String(row.title ?? \"\"),\n\t\tbody_text: String(row.body_text ?? \"\"),\n\t\tconfidence: Number(row.confidence ?? 0),\n\t\ttags_text: String(row.tags_text ?? \"\"),\n\t\tmetadata_json: row.metadata_json == null ? null : String(row.metadata_json),\n\t\tcreated_at: String(row.created_at ?? \"\"),\n\t\tupdated_at: String(row.updated_at ?? \"\"),\n\t\tsession_id: Number(row.session_id),\n\t\tscore: 1.0 / (1.0 + Number(row.distance ?? 0)),\n\t\tdistance: Number(row.distance ?? 0),\n\t}));\n}\n","/**\n * Main ingest pipeline — processes raw coding session events, calls the\n * observer LLM, and stores extracted memories.\n *\n * Ports the `ingest()` function from codemem/plugin_ingest.py.\n *\n * Pipeline stages:\n * 1. Extract session context, events, cwd\n * 2. Create/find session in store\n * 3. Extract prompts, tool events, assistant messages\n * 4. Build transcript\n * 5. Budget tool events\n * 6. Build observer context + prompt\n * 7. Call observer LLM via ObserverClient.observe()\n * 8. Parse XML response\n * 9. Filter low-signal observations\n * 10. Persist observations as memories\n * 11. Persist session summary\n * 12. End session\n */\n\nimport { and, isNull, lt } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { normalizeProjectLabel } from \"./claude-hooks.js\";\nimport { toJson } from \"./db.js\";\nimport {\n\tbudgetToolEvents,\n\teventToToolEvent,\n\textractAdapterEvent,\n\textractToolEvents,\n\tprojectAdapterToolEvent,\n} from \"./ingest-events.js\";\nimport { isLowSignalObservation } from \"./ingest-filters.js\";\nimport { buildObserverPrompt } from \"./ingest-prompts.js\";\nimport {\n\tbuildTranscript,\n\tderiveRequest,\n\textractAssistantMessages,\n\textractAssistantUsage,\n\textractPrompts,\n\tfirstSentence,\n\tisTrivialRequest,\n\tnormalizeAdapterEvents,\n} from \"./ingest-transcript.js\";\nimport type {\n\tIngestPayload,\n\tObserverContext,\n\tParsedSummary,\n\tSessionContext,\n\tToolEvent,\n} from \"./ingest-types.js\";\nimport { hasMeaningfulObservation, parseObserverResponse } from \"./ingest-xml-parser.js\";\nimport type { ObserverClient } from \"./observer-client.js\";\nimport { resolveProject } from \"./project.js\";\nimport * as schema from \"./schema.js\";\nimport type { MemoryStore } from \"./store.js\";\nimport { deriveTags } from \"./tags.js\";\nimport { storeVectors } from \"./vectors.js\";\n\n// ---------------------------------------------------------------------------\n// Allowed memory kinds (matches Python)\n// ---------------------------------------------------------------------------\n\nconst ALLOWED_KINDS = new Set([\n\t\"bugfix\",\n\t\"feature\",\n\t\"refactor\",\n\t\"change\",\n\t\"discovery\",\n\t\"decision\",\n\t\"exploration\",\n]);\n\n// ---------------------------------------------------------------------------\n// Path normalization\n// ---------------------------------------------------------------------------\n\nfunction normalizePath(path: string, repoRoot: string | null): string {\n\tif (!path) return \"\";\n\tconst cleaned = path.trim();\n\tif (!repoRoot) return cleaned;\n\tconst root = repoRoot.replace(/\\/+$/, \"\");\n\tif (cleaned === root) return \".\";\n\tif (cleaned.startsWith(`${root}/`)) return cleaned.slice(root.length + 1);\n\treturn cleaned;\n}\n\nfunction normalizePaths(paths: string[], repoRoot: string | null): string[] {\n\treturn paths.map((p) => normalizePath(p, repoRoot)).filter(Boolean);\n}\n\n// ---------------------------------------------------------------------------\n// Summary body formatting\n// ---------------------------------------------------------------------------\n\nfunction summaryBody(summary: ParsedSummary): string {\n\tconst sections: [string, string][] = [\n\t\t[\"Request\", summary.request],\n\t\t[\"Completed\", summary.completed],\n\t\t[\"Learned\", summary.learned],\n\t\t[\"Investigated\", summary.investigated],\n\t\t[\"Next steps\", summary.nextSteps],\n\t\t[\"Notes\", summary.notes],\n\t];\n\treturn sections\n\t\t.filter(([, value]) => value)\n\t\t.map(([label, value]) => `## ${label}\\n${value}`)\n\t\t.join(\"\\n\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Event normalization (adapter projection)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert raw events with adapter envelopes into normalized flat events.\n * Handles both tool events (via adapter projection) and transcript events\n * (via normalizeAdapterEvents).\n */\nfunction normalizeEventsForToolExtraction(\n\tevents: Record<string, unknown>[],\n\tmaxChars: number,\n): ToolEvent[] {\n\tconst toolEvents: ToolEvent[] = [];\n\tfor (const event of events) {\n\t\tconst adapter = extractAdapterEvent(event);\n\t\tif (adapter) {\n\t\t\t// Skip tool_call events (only tool_result matters)\n\t\t\tif (adapter.event_type === \"tool_call\") continue;\n\t\t\tconst projected = projectAdapterToolEvent(adapter, event);\n\t\t\tif (projected) {\n\t\t\t\tconst te = eventToToolEvent(projected, maxChars);\n\t\t\t\tif (te) {\n\t\t\t\t\ttoolEvents.push(te);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Direct (non-adapter) events\n\t\tconst directEvents = extractToolEvents([event], maxChars);\n\t\ttoolEvents.push(...directEvents);\n\t}\n\treturn toolEvents;\n}\n\n// ---------------------------------------------------------------------------\n// Main pipeline\n// ---------------------------------------------------------------------------\n\nexport interface IngestOptions {\n\t/** Observer LLM client. */\n\tobserver: ObserverClient;\n\t/** Maximum chars per tool event payload (from config). Default 12000. */\n\tmaxChars?: number;\n\t/** Maximum chars for observer total budget. Default 12000. */\n\tobserverMaxChars?: number;\n\t/** Whether to store summaries. Default true. */\n\tstoreSummary?: boolean;\n\t/** Whether to store typed observations. Default true. */\n\tstoreTyped?: boolean;\n}\n\n/**\n * Process a batch of raw coding session events through the full ingest pipeline.\n *\n * Extracts prompts, tool events, and assistant messages from the payload,\n * builds a transcript, calls the observer LLM, parses the response,\n * filters low-signal content, and persists observations + summary.\n */\nexport async function ingest(\n\tpayload: IngestPayload,\n\tstore: MemoryStore,\n\toptions: IngestOptions,\n): Promise<void> {\n\tconst cwd = payload.cwd ?? process.cwd();\n\tconst events = payload.events ?? [];\n\tif (!Array.isArray(events) || events.length === 0) return;\n\n\tconst sessionContext = payload.sessionContext ?? {};\n\tconst storeSummary = options.storeSummary ?? true;\n\tconst storeTyped = options.storeTyped ?? true;\n\tconst maxChars = options.maxChars ?? 12_000;\n\tconst observerMaxChars = options.observerMaxChars ?? 12_000;\n\n\tconst d = drizzle(store.db, { schema });\n\tconst now = new Date().toISOString();\n\tconst project = normalizeProjectLabel(payload.project) ?? resolveProject(cwd) ?? null;\n\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: now,\n\t\t\tcwd,\n\t\t\tproject,\n\t\t\tuser: process.env.USER ?? \"unknown\",\n\t\t\ttool_version: \"plugin-ts\",\n\t\t\tmetadata_json: toJson({\n\t\t\t\tsource: \"plugin\",\n\t\t\t\tevent_count: events.length,\n\t\t\t\tstarted_at: payload.startedAt,\n\t\t\t\tsession_context: sessionContext,\n\t\t\t}),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst sessionId = rows[0]?.id;\n\tif (sessionId == null) throw new Error(\"session insert returned no id\");\n\n\ttry {\n\t\t// ------------------------------------------------------------------\n\t\t// Extract data from events\n\t\t// ------------------------------------------------------------------\n\t\tconst normalizedEvents = normalizeAdapterEvents(events);\n\t\tconst prompts = extractPrompts(normalizedEvents);\n\t\tconst promptNumber =\n\t\t\tprompts.length > 0 ? (prompts[prompts.length - 1]?.promptNumber ?? prompts.length) : null;\n\n\t\t// Tool events — handle adapter projection\n\t\tlet toolEvents = normalizeEventsForToolExtraction(events, maxChars);\n\n\t\t// Budget tool events\n\t\tconst toolBudget = Math.max(2000, Math.min(8000, observerMaxChars - 5000));\n\t\ttoolEvents = budgetToolEvents(toolEvents, toolBudget, 30);\n\n\t\t// Assistant messages\n\t\tconst assistantMessages = extractAssistantMessages(normalizedEvents);\n\t\tconst assistantUsageEvents = extractAssistantUsage(normalizedEvents);\n\t\tconst lastAssistantMessage = assistantMessages.at(-1) ?? null;\n\n\t\t// Latest prompt\n\t\tconst latestPrompt =\n\t\t\tsessionContext.firstPrompt ??\n\t\t\t(prompts.length > 0 ? prompts[prompts.length - 1]?.promptText : null) ??\n\t\t\tnull;\n\n\t\t// ------------------------------------------------------------------\n\t\t// Should we process?\n\t\t// ------------------------------------------------------------------\n\t\tlet shouldProcess =\n\t\t\ttoolEvents.length > 0 ||\n\t\t\tBoolean(latestPrompt) ||\n\t\t\t(storeSummary && Boolean(lastAssistantMessage));\n\n\t\tif (\n\t\t\tlatestPrompt &&\n\t\t\tisTrivialRequest(latestPrompt) &&\n\t\t\ttoolEvents.length === 0 &&\n\t\t\t!lastAssistantMessage\n\t\t) {\n\t\t\tshouldProcess = false;\n\t\t}\n\n\t\tif (!shouldProcess) {\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t\treturn;\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// Build transcript\n\t\t// ------------------------------------------------------------------\n\t\tconst transcript = buildTranscript(normalizedEvents);\n\n\t\t// ------------------------------------------------------------------\n\t\t// Build observer prompt\n\t\t// ------------------------------------------------------------------\n\t\tconst sessionSummaryParts: string[] = [];\n\t\tif ((sessionContext.promptCount ?? 0) > 1) {\n\t\t\tsessionSummaryParts.push(`Session had ${sessionContext.promptCount} prompts`);\n\t\t}\n\t\tif ((sessionContext.toolCount ?? 0) > 0) {\n\t\t\tsessionSummaryParts.push(`${sessionContext.toolCount} tool executions`);\n\t\t}\n\t\tif ((sessionContext.durationMs ?? 0) > 0) {\n\t\t\tconst durationMin = (sessionContext.durationMs ?? 0) / 60000;\n\t\t\tsessionSummaryParts.push(`~${durationMin.toFixed(1)} minutes of work`);\n\t\t}\n\t\tif (sessionContext.filesModified?.length) {\n\t\t\tsessionSummaryParts.push(`Modified: ${sessionContext.filesModified.slice(0, 5).join(\", \")}`);\n\t\t}\n\t\tif (sessionContext.filesRead?.length) {\n\t\t\tsessionSummaryParts.push(`Read: ${sessionContext.filesRead.slice(0, 5).join(\", \")}`);\n\t\t}\n\t\tconst sessionInfoText = sessionSummaryParts.join(\"; \");\n\n\t\tlet observerPrompt = latestPrompt ?? \"\";\n\t\tif (sessionInfoText) {\n\t\t\tobserverPrompt = observerPrompt\n\t\t\t\t? `${observerPrompt}\\n\\n[Session context: ${sessionInfoText}]`\n\t\t\t\t: `[Session context: ${sessionInfoText}]`;\n\t\t}\n\n\t\tconst observerContext: ObserverContext = {\n\t\t\tproject,\n\t\t\tuserPrompt: observerPrompt,\n\t\t\tpromptNumber,\n\t\t\ttoolEvents,\n\t\t\tlastAssistantMessage: storeSummary ? lastAssistantMessage : null,\n\t\t\tincludeSummary: storeSummary,\n\t\t\tdiffSummary: \"\",\n\t\t\trecentFiles: \"\",\n\t\t};\n\n\t\tconst { system, user } = buildObserverPrompt(observerContext);\n\n\t\t// ------------------------------------------------------------------\n\t\t// Call observer LLM\n\t\t// ------------------------------------------------------------------\n\t\tconst response = await options.observer.observe(system, user);\n\n\t\tif (!response.raw) {\n\t\t\t// Raw-event flushes must be lossless: if the observer returns no output,\n\t\t\t// fail the flush so we do NOT advance last_flushed_event_seq.\n\t\t\tif (sessionContext?.flusher === \"raw_events\") {\n\t\t\t\tthrow new Error(\"observer failed during raw-event flush\");\n\t\t\t}\n\n\t\t\t// Surface the failure for normal ingest paths.\n\t\t\tconst status = options.observer.getStatus();\n\t\t\tconsole.warn(\n\t\t\t\t`[codemem] Observer returned no output (provider=${response.provider}, model=${response.model}` +\n\t\t\t\t\t`${status.lastError ? `, error=${status.lastError}` : \"\"}). No memories will be created for this session.`,\n\t\t\t);\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t\treturn;\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// Parse response\n\t\t// ------------------------------------------------------------------\n\t\tconst rawText = response.raw;\n\t\tconst parsed = parseObserverResponse(rawText);\n\n\t\tconst observationsToStore: typeof parsed.observations = [];\n\t\tif (storeTyped && hasMeaningfulObservation(parsed.observations)) {\n\t\t\tfor (const obs of parsed.observations) {\n\t\t\t\tconst kind = obs.kind.trim().toLowerCase();\n\t\t\t\tif (!ALLOWED_KINDS.has(kind)) continue;\n\t\t\t\tif (!obs.title && !obs.narrative) continue;\n\t\t\t\tif (isLowSignalObservation(obs.title) || isLowSignalObservation(obs.narrative)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tobs.filesRead = normalizePaths(obs.filesRead, cwd);\n\t\t\t\tobs.filesModified = normalizePaths(obs.filesModified, cwd);\n\t\t\t\tobservationsToStore.push(obs);\n\t\t\t}\n\t\t}\n\n\t\tlet summaryToStore: { summary: ParsedSummary; request: string; body: string } | null = null;\n\t\tif (storeSummary && parsed.summary && !parsed.skipSummaryReason) {\n\t\t\tconst summary = parsed.summary;\n\t\t\tif (\n\t\t\t\tsummary.request ||\n\t\t\t\tsummary.investigated ||\n\t\t\t\tsummary.learned ||\n\t\t\t\tsummary.completed ||\n\t\t\t\tsummary.nextSteps ||\n\t\t\t\tsummary.notes\n\t\t\t) {\n\t\t\t\tsummary.filesRead = normalizePaths(summary.filesRead, cwd);\n\t\t\t\tsummary.filesModified = normalizePaths(summary.filesModified, cwd);\n\n\t\t\t\tlet request = summary.request;\n\t\t\t\tif (isTrivialRequest(request)) {\n\t\t\t\t\tconst derived = deriveRequest(summary);\n\t\t\t\t\tif (derived) request = derived;\n\t\t\t\t}\n\n\t\t\t\tconst body = summaryBody(summary);\n\t\t\t\tif (body && !isLowSignalObservation(firstSentence(body))) {\n\t\t\t\t\tsummaryToStore = { summary, request, body };\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (sessionContext?.flusher === \"raw_events\") {\n\t\t\tconst storableCount = observationsToStore.length + (summaryToStore ? 1 : 0);\n\t\t\tif (storableCount === 0) {\n\t\t\t\tthrow new Error(\"observer produced no storable output for raw-event flush\");\n\t\t\t}\n\t\t}\n\n\t\tconst vectorWriteInputs: Array<{ memoryId: number; title: string; bodyText: string }> = [];\n\n\t\t// Persist all observations, summary, and usage atomically\n\t\tstore.db.transaction(() => {\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Filter and persist observations\n\t\t\t// ------------------------------------------------------------------\n\t\t\tfor (const obs of observationsToStore) {\n\t\t\t\tconst kind = obs.kind.trim().toLowerCase();\n\n\t\t\t\tconst bodyParts: string[] = [];\n\t\t\t\tif (obs.narrative) bodyParts.push(obs.narrative);\n\t\t\t\tif (obs.facts.length > 0) {\n\t\t\t\t\tbodyParts.push(obs.facts.map((f) => `- ${f}`).join(\"\\n\"));\n\t\t\t\t}\n\t\t\t\tconst bodyText = bodyParts.join(\"\\n\\n\");\n\n\t\t\t\tconst memoryTitle = obs.title || obs.narrative;\n\t\t\t\tconst tags = deriveTags({\n\t\t\t\t\tkind,\n\t\t\t\t\ttitle: memoryTitle,\n\t\t\t\t\tconcepts: obs.concepts,\n\t\t\t\t\tfilesRead: obs.filesRead,\n\t\t\t\t\tfilesModified: obs.filesModified,\n\t\t\t\t});\n\t\t\t\tconst memoryId = store.remember(sessionId, kind, memoryTitle, bodyText, 0.5, tags, {\n\t\t\t\t\tsubtitle: obs.subtitle,\n\t\t\t\t\tfacts: obs.facts,\n\t\t\t\t\tconcepts: obs.concepts,\n\t\t\t\t\tfiles_read: obs.filesRead,\n\t\t\t\t\tfiles_modified: obs.filesModified,\n\t\t\t\t\tprompt_number: promptNumber,\n\t\t\t\t\tsource: \"observer\",\n\t\t\t\t});\n\t\t\t\tvectorWriteInputs.push({ memoryId, title: memoryTitle, bodyText });\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Persist session summary\n\t\t\t// ------------------------------------------------------------------\n\t\t\tif (summaryToStore) {\n\t\t\t\tconst { summary, request, body } = summaryToStore;\n\t\t\t\tconst summaryTitle = request || \"Session summary\";\n\t\t\t\tconst summaryTags = deriveTags({\n\t\t\t\t\tkind: \"change\",\n\t\t\t\t\ttitle: summaryTitle,\n\t\t\t\t\tfilesRead: summary.filesRead,\n\t\t\t\t\tfilesModified: summary.filesModified,\n\t\t\t\t});\n\t\t\t\tconst memoryId = store.remember(sessionId, \"change\", summaryTitle, body, 0.3, summaryTags, {\n\t\t\t\t\tis_summary: true,\n\t\t\t\t\trequest,\n\t\t\t\t\tinvestigated: summary.investigated,\n\t\t\t\t\tlearned: summary.learned,\n\t\t\t\t\tcompleted: summary.completed,\n\t\t\t\t\tnext_steps: summary.nextSteps,\n\t\t\t\t\tnotes: summary.notes,\n\t\t\t\t\tprompt_number: promptNumber,\n\t\t\t\t\tfiles_read: summary.filesRead,\n\t\t\t\t\tfiles_modified: summary.filesModified,\n\t\t\t\t\tsource: \"observer_summary\",\n\t\t\t\t});\n\t\t\t\tvectorWriteInputs.push({ memoryId, title: summaryTitle, bodyText: body });\n\t\t\t}\n\n\t\t\t// ------------------------------------------------------------------\n\t\t\t// Record observer usage\n\t\t\t// ------------------------------------------------------------------\n\t\t\tconst usageTokenTotal = assistantUsageEvents.reduce(\n\t\t\t\t(sum, e) => sum + (e.total_tokens ?? 0),\n\t\t\t\t0,\n\t\t\t);\n\t\t\td.insert(schema.usageEvents)\n\t\t\t\t.values({\n\t\t\t\t\tsession_id: sessionId,\n\t\t\t\t\tevent: \"observer_call\",\n\t\t\t\t\ttokens_read: rawText.length,\n\t\t\t\t\ttokens_written: transcript.length,\n\t\t\t\t\tcreated_at: new Date().toISOString(),\n\t\t\t\t\tmetadata_json: toJson({\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tobservation_count: observationsToStore.length,\n\t\t\t\t\t\thas_summary: summaryToStore != null,\n\t\t\t\t\t\tprovider: response.provider,\n\t\t\t\t\t\tmodel: response.model,\n\t\t\t\t\t\tsession_usage_tokens: usageTokenTotal,\n\t\t\t\t\t}),\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t})();\n\n\t\tfor (const input of vectorWriteInputs) {\n\t\t\ttry {\n\t\t\t\tawait storeVectors(store.db, input.memoryId, input.title, input.bodyText);\n\t\t\t} catch {\n\t\t\t\t// Non-fatal — ingestion should not fail when embeddings are unavailable\n\t\t\t}\n\t\t}\n\n\t\t// ------------------------------------------------------------------\n\t\t// End session\n\t\t// ------------------------------------------------------------------\n\t\tendSession(store, sessionId, events.length, sessionContext);\n\t} catch (err) {\n\t\t// End session even on error\n\t\ttry {\n\t\t\tendSession(store, sessionId, events.length, sessionContext);\n\t\t} catch {\n\t\t\t// ignore cleanup errors\n\t\t}\n\t\tthrow err;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Session lifecycle helpers\n// ---------------------------------------------------------------------------\n\nfunction endSession(\n\tstore: MemoryStore,\n\tsessionId: number,\n\teventCount: number,\n\tsessionContext: SessionContext,\n): void {\n\t// Use store.endSession() which merges metadata instead of replacing it,\n\t// preserving fields set during session creation (startedAt, session_context, etc.)\n\tstore.endSession(sessionId, {\n\t\tpost: {},\n\t\tsource: \"plugin\",\n\t\tevent_count: eventCount,\n\t\tsession_context: sessionContext,\n\t});\n}\n\n// ---------------------------------------------------------------------------\n// Orphan session cleanup\n// ---------------------------------------------------------------------------\n\n/**\n * Close orphan sessions (started but never ended) older than maxAgeHours.\n * Returns the number of sessions closed.\n */\nexport function cleanOrphanSessions(store: MemoryStore, maxAgeHours = 24): number {\n\tconst d = drizzle(store.db, { schema });\n\tconst cutoff = new Date(Date.now() - maxAgeHours * 3600_000).toISOString();\n\tconst result = d\n\t\t.update(schema.sessions)\n\t\t.set({ ended_at: cutoff })\n\t\t.where(and(isNull(schema.sessions.ended_at), lt(schema.sessions.started_at, cutoff)))\n\t\t.run();\n\treturn result.changes;\n}\n\n// ---------------------------------------------------------------------------\n// CLI entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Read a JSON payload from stdin and run the ingest pipeline.\n *\n * This is the TypeScript equivalent of `codemem ingest` — receives events\n * from the plugin and processes them through the observer LLM.\n */\nexport async function main(store: MemoryStore, observer: ObserverClient): Promise<void> {\n\tconst chunks: string[] = [];\n\tfor await (const chunk of process.stdin) {\n\t\tchunks.push(String(chunk));\n\t}\n\tconst raw = chunks.join(\"\");\n\tif (!raw.trim()) return;\n\n\tlet payload: IngestPayload;\n\ttry {\n\t\tpayload = JSON.parse(raw) as IngestPayload;\n\t} catch (err) {\n\t\tthrow new Error(`codemem: invalid payload: ${err}`);\n\t}\n\n\tawait ingest(payload, store, { observer });\n}\n","const MAX_SAFE_BIGINT = BigInt(Number.MAX_SAFE_INTEGER);\nconst MIN_SAFE_BIGINT = BigInt(Number.MIN_SAFE_INTEGER);\n\n/**\n * Parse a strict integer string like Python's int(), but reject partial strings\n * like \"10abc\" that parseInt() would silently truncate.\n */\nexport function parseStrictInteger(value: string | undefined): number | null {\n\tif (value == null) return null;\n\tconst trimmed = value.trim();\n\tif (!/^[+-]?\\d+$/.test(trimmed)) return null;\n\ttry {\n\t\tconst parsed = BigInt(trimmed);\n\t\tif (parsed < MIN_SAFE_BIGINT || parsed > MAX_SAFE_BIGINT) return null;\n\t\treturn Number(parsed);\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/**\n * Parse a positive memory ID matching Python's stricter semantics:\n * - allow integer numbers\n * - allow digit-only strings\n * - reject bools, floats, scientific notation, whitespacey strings, unsafe ints\n */\nexport function parsePositiveMemoryId(value: unknown): number | null {\n\tif (typeof value === \"boolean\") return null;\n\tif (typeof value === \"number\") {\n\t\tif (!Number.isInteger(value) || !Number.isSafeInteger(value) || value <= 0) return null;\n\t\treturn value;\n\t}\n\tif (typeof value === \"string\") {\n\t\tif (!/^\\d+$/.test(value)) return null;\n\t\ttry {\n\t\t\tconst parsed = BigInt(value);\n\t\t\tif (parsed <= 0n || parsed > MAX_SAFE_BIGINT) return null;\n\t\t\treturn Number(parsed);\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\treturn null;\n}\n","/**\n * Observer authentication: credential resolution, caching, and header rendering.\n *\n * Mirrors codemem/observer_auth.py — resolves auth tokens from explicit values,\n * environment variables, OAuth caches, files, or external commands. Supports\n * template-based header rendering with `${auth.token}` / `${auth.type}` / `${auth.source}`.\n */\n\nimport { execFileSync } from \"node:child_process\";\nimport { existsSync, readFileSync, statSync } from \"node:fs\";\nimport { arch, homedir, platform, release } from \"node:os\";\nimport { join } from \"node:path\";\n\n// Inline version to avoid circular import (index.ts re-exports from this module).\n// Keep in sync with the VERSION constant in index.ts.\nconst PACKAGE_VERSION = \"0.0.1\";\n\n// ---------------------------------------------------------------------------\n// Redaction\n// ---------------------------------------------------------------------------\n\nconst REDACT_PATTERNS = [/sk-[A-Za-z0-9]{10,}/g, /Bearer\\s+[A-Za-z0-9._-]{10,}/g];\n\n/** Redact API keys and bearer tokens in text. Truncates at `limit` chars. */\nexport function redactText(text: string, limit = 400): string {\n\tlet redacted = text;\n\tfor (const pattern of REDACT_PATTERNS) {\n\t\tredacted = redacted.replace(new RegExp(pattern.source, pattern.flags), \"[redacted]\");\n\t}\n\treturn redacted.length > limit ? `${redacted.slice(0, limit)}…` : redacted;\n}\n\n// ---------------------------------------------------------------------------\n// Auth material\n// ---------------------------------------------------------------------------\n\nexport interface ObserverAuthMaterial {\n\ttoken: string | null;\n\tauthType: string;\n\tsource: string;\n}\n\nfunction noAuth(): ObserverAuthMaterial {\n\treturn { token: null, authType: \"none\", source: \"none\" };\n}\n\n// ---------------------------------------------------------------------------\n// OAuth cache (OpenCode's auth.json)\n// ---------------------------------------------------------------------------\n\nfunction getOpenCodeAuthPath(): string {\n\treturn join(homedir(), \".local\", \"share\", \"opencode\", \"auth.json\");\n}\n\n/** Load the OpenCode OAuth token cache from `~/.local/share/opencode/auth.json`. */\nexport function loadOpenCodeOAuthCache(): Record<string, unknown> {\n\tconst authPath = getOpenCodeAuthPath();\n\tif (!existsSync(authPath)) return {};\n\ttry {\n\t\tconst data = JSON.parse(readFileSync(authPath, \"utf-8\"));\n\t\treturn data != null && typeof data === \"object\" && !Array.isArray(data) ? data : {};\n\t} catch {\n\t\treturn {};\n\t}\n}\n\n/** Determine OAuth provider from configured provider name or model prefix. */\nexport function resolveOAuthProvider(configured: string | null | undefined, model: string): string {\n\tif (configured && [\"openai\", \"anthropic\"].includes(configured.toLowerCase())) {\n\t\treturn configured.toLowerCase();\n\t}\n\treturn model.toLowerCase().startsWith(\"claude\") ? \"anthropic\" : \"openai\";\n}\n\ntype OAuthEntry = Record<string, unknown>;\n\nfunction getOAuthEntry(cache: Record<string, unknown>, provider: string): OAuthEntry | null {\n\tconst entry = cache[provider];\n\treturn entry != null && typeof entry === \"object\" && !Array.isArray(entry)\n\t\t? (entry as OAuthEntry)\n\t\t: null;\n}\n\n/** Extract access token from OAuth cache for a given provider. */\nexport function extractOAuthAccess(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst access = entry.access;\n\treturn typeof access === \"string\" && access ? access : null;\n}\n\n/** Extract API key from auth cache for a given provider. */\nexport function extractProviderApiKey(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst key = entry.key;\n\treturn typeof key === \"string\" && key ? key : null;\n}\n\n/** Probe which credential sources are currently available per built-in provider. */\nexport function probeAvailableCredentials(): Record<\n\tstring,\n\t{ oauth: boolean; api_key: boolean; env_var: boolean }\n> {\n\tconst cache = loadOpenCodeOAuthCache();\n\tconst now = Date.now();\n\tconst explicitApiKey = Boolean(process.env.CODEMEM_OBSERVER_API_KEY);\n\tconst providers = [\n\t\t[\"openai\", \"OPENAI_API_KEY\"],\n\t\t[\"anthropic\", \"ANTHROPIC_API_KEY\"],\n\t\t[\"opencode\", null],\n\t] as const;\n\tconst result: Record<string, { oauth: boolean; api_key: boolean; env_var: boolean }> = {};\n\tfor (const [provider, envVar] of providers) {\n\t\tconst oauthAccess = extractOAuthAccess(cache, provider);\n\t\tconst oauthExpires = extractOAuthExpires(cache, provider);\n\t\tconst oauthValid =\n\t\t\tprovider === \"opencode\"\n\t\t\t\t? Boolean(extractProviderApiKey(cache, provider))\n\t\t\t\t: Boolean(oauthAccess) && (oauthExpires == null || oauthExpires > now);\n\t\tresult[provider] = {\n\t\t\toauth: oauthValid,\n\t\t\tapi_key: explicitApiKey,\n\t\t\tenv_var: envVar ? Boolean(process.env[envVar]) : false,\n\t\t};\n\t}\n\treturn result;\n}\n\n/** Extract account ID from OAuth cache for a given provider. */\nexport function extractOAuthAccountId(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): string | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst accountId = entry.accountId;\n\treturn typeof accountId === \"string\" && accountId ? accountId : null;\n}\n\n/** Extract expiry timestamp (ms) from OAuth cache for a given provider. */\nexport function extractOAuthExpires(\n\tcache: Record<string, unknown>,\n\tprovider: string,\n): number | null {\n\tconst entry = getOAuthEntry(cache, provider);\n\tif (!entry) return null;\n\tconst expires = entry.expires;\n\treturn typeof expires === \"number\" ? expires : null;\n}\n\n// ---------------------------------------------------------------------------\n// Codex headers\n// ---------------------------------------------------------------------------\n\n/** Build OpenAI Codex transport headers from an OAuth access token. */\nexport function buildCodexHeaders(\n\taccessToken: string,\n\taccountId: string | null,\n): Record<string, string> {\n\tconst originator = process.env.CODEMEM_CODEX_ORIGINATOR ?? \"opencode\";\n\tconst userAgent =\n\t\tprocess.env.CODEMEM_CODEX_USER_AGENT ??\n\t\t`codemem/${PACKAGE_VERSION} (${platform()} ${release()}; ${arch()})`;\n\n\tconst headers: Record<string, string> = {\n\t\tauthorization: `Bearer ${accessToken}`,\n\t\toriginator,\n\t\t\"User-Agent\": userAgent,\n\t\taccept: \"text/event-stream\",\n\t};\n\tif (accountId) {\n\t\theaders[\"ChatGPT-Account-Id\"] = accountId;\n\t}\n\treturn headers;\n}\n\n// ---------------------------------------------------------------------------\n// External auth: command execution & file reading\n// ---------------------------------------------------------------------------\n\n/** Execute an external auth command and return the token (stdout, trimmed). */\nexport function runAuthCommand(command: string[], timeoutMs: number): string | null {\n\tconst cmd = command[0];\n\tif (!cmd) return null;\n\t// execFileSync timeout is in milliseconds\n\tconst effectiveTimeoutMs = Math.max(100, timeoutMs);\n\ttry {\n\t\tconst stdout = execFileSync(cmd, command.slice(1), {\n\t\t\ttimeout: effectiveTimeoutMs,\n\t\t\tencoding: \"utf-8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t});\n\t\tconst token = (stdout ?? \"\").trim();\n\t\treturn token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n/** Read a token from a file path (supports `~` and `$ENV_VAR` expansion). */\nexport function readAuthFile(filePath: string | null): string | null {\n\tif (!filePath) return null;\n\t// Expand ~ and $ENV_VAR\n\tlet resolved = filePath.replace(/^~/, homedir());\n\tresolved = resolved.replace(\n\t\t/\\$\\{([^}]+)\\}|\\$([A-Za-z_][A-Za-z0-9_]*)/g,\n\t\t(match, braced, bare) => {\n\t\t\tconst name = braced ?? bare;\n\t\t\treturn process.env[name] ?? match;\n\t\t},\n\t);\n\ttry {\n\t\tif (!existsSync(resolved) || !statSync(resolved).isFile()) return null;\n\t\tconst token = readFileSync(resolved, \"utf-8\").trim();\n\t\treturn token || null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Source normalization\n// ---------------------------------------------------------------------------\n\nconst VALID_SOURCES = new Set([\"\", \"auto\", \"env\", \"file\", \"command\", \"none\"]);\n\nfunction normalizeAuthSource(value: string | null | undefined): string {\n\tconst normalized = (value ?? \"\").trim().toLowerCase();\n\treturn VALID_SOURCES.has(normalized) ? normalized || \"auto\" : \"auto\";\n}\n\n// ---------------------------------------------------------------------------\n// Auth adapter (credential cascade with caching)\n// ---------------------------------------------------------------------------\n\nexport interface ObserverAuthResolveOptions {\n\texplicitToken?: string | null;\n\tenvTokens?: string[];\n\toauthToken?: string | null;\n\tforceRefresh?: boolean;\n}\n\n/**\n * Resolves auth credentials through a configurable cascade:\n * explicit → env → oauth → file → command.\n *\n * Results from file/command sources are cached for `cacheTtlS` seconds.\n */\nexport class ObserverAuthAdapter {\n\treadonly source: string;\n\treadonly filePath: string | null;\n\treadonly command: string[];\n\treadonly timeoutMs: number;\n\treadonly cacheTtlS: number;\n\n\tprivate cached: ObserverAuthMaterial = noAuth();\n\tprivate cachedAtMs = 0;\n\n\tconstructor(opts?: {\n\t\tsource?: string;\n\t\tfilePath?: string | null;\n\t\tcommand?: string[];\n\t\ttimeoutMs?: number;\n\t\tcacheTtlS?: number;\n\t}) {\n\t\tthis.source = opts?.source ?? \"auto\";\n\t\tthis.filePath = opts?.filePath ?? null;\n\t\tthis.command = opts?.command ?? [];\n\t\tthis.timeoutMs = opts?.timeoutMs ?? 1500;\n\t\tthis.cacheTtlS = opts?.cacheTtlS ?? 300;\n\t}\n\n\t/** Resolve auth material through the credential cascade. */\n\tresolve(opts?: ObserverAuthResolveOptions): ObserverAuthMaterial {\n\t\tconst source = normalizeAuthSource(this.source);\n\t\tconst explicitToken = opts?.explicitToken ?? null;\n\t\tconst envTokens = opts?.envTokens ?? [];\n\t\tconst oauthToken = opts?.oauthToken ?? null;\n\t\tconst forceRefresh = opts?.forceRefresh ?? false;\n\n\t\tif (source === \"none\") return noAuth();\n\n\t\t// Check cache for file/command sources\n\t\tif (!forceRefresh && (source === \"command\" || source === \"file\") && this.cacheTtlS > 0) {\n\t\t\tconst ageMs = performance.now() - this.cachedAtMs;\n\t\t\tif (this.cachedAtMs > 0 && ageMs <= this.cacheTtlS * 1000) {\n\t\t\t\treturn this.cached;\n\t\t\t}\n\t\t}\n\n\t\tlet token: string | null = null;\n\t\tlet tokenSource = \"none\";\n\n\t\tif (source === \"auto\") {\n\t\t\tif (explicitToken) {\n\t\t\t\ttoken = explicitToken;\n\t\t\t\ttokenSource = \"explicit\";\n\t\t\t}\n\t\t\tif (!token) {\n\t\t\t\ttoken = envTokens.find((t) => !!t) ?? null;\n\t\t\t\tif (token) tokenSource = \"env\";\n\t\t\t}\n\t\t\tif (!token && oauthToken) {\n\t\t\t\ttoken = oauthToken;\n\t\t\t\ttokenSource = \"oauth\";\n\t\t\t}\n\t\t} else if (source === \"env\") {\n\t\t\ttoken = envTokens.find((t) => !!t) ?? null;\n\t\t\tif (token) tokenSource = \"env\";\n\t\t}\n\n\t\tif ((source === \"auto\" || source === \"file\") && !token) {\n\t\t\ttoken = readAuthFile(this.filePath);\n\t\t\tif (token) tokenSource = \"file\";\n\t\t}\n\n\t\tif ((source === \"auto\" || source === \"command\") && !token) {\n\t\t\ttoken = runAuthCommand(this.command, this.timeoutMs);\n\t\t\tif (token) tokenSource = \"command\";\n\t\t}\n\n\t\tconst resolved: ObserverAuthMaterial = token\n\t\t\t? { token, authType: \"bearer\", source: tokenSource }\n\t\t\t: noAuth();\n\n\t\tconst shouldCache = source === \"command\" || source === \"file\";\n\t\tif (shouldCache && resolved.token) {\n\t\t\tthis.cached = resolved;\n\t\t\tthis.cachedAtMs = performance.now();\n\t\t} else if (shouldCache) {\n\t\t\tthis.invalidateCache();\n\t\t}\n\n\t\treturn resolved;\n\t}\n\n\t/** Clear the cached auth material. */\n\tinvalidateCache(): void {\n\t\tthis.cached = noAuth();\n\t\tthis.cachedAtMs = 0;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Header rendering\n// ---------------------------------------------------------------------------\n\nconst AUTH_TOKEN_RE = /\\$\\{auth\\.token\\}/g;\nconst AUTH_TYPE_RE = /\\$\\{auth\\.type\\}/g;\nconst AUTH_SOURCE_RE = /\\$\\{auth\\.source\\}/g;\n\n/** Render observer headers with `${auth.token}`, `${auth.type}`, `${auth.source}` substitution. */\nexport function renderObserverHeaders(\n\theaders: Record<string, string>,\n\tauth: ObserverAuthMaterial,\n): Record<string, string> {\n\tconst token = auth.token ?? \"\";\n\tconst rendered: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(headers)) {\n\t\tif (typeof key !== \"string\" || typeof value !== \"string\") continue;\n\n\t\tlet candidate = value.replace(AUTH_TOKEN_RE, token);\n\t\tcandidate = candidate.replace(AUTH_TYPE_RE, auth.authType);\n\t\tcandidate = candidate.replace(AUTH_SOURCE_RE, auth.source);\n\n\t\t// Skip headers that reference auth.token when no token is available\n\t\t// biome-ignore lint/suspicious/noTemplateCurlyInString: intentional template pattern, not JS template literal\n\t\tif (value.includes(\"${auth.token}\") && !token) continue;\n\n\t\tconst cleaned = candidate.trim();\n\t\tif (!cleaned) continue;\n\t\trendered[key] = cleaned;\n\t}\n\treturn rendered;\n}\n","/**\n * Observer client: LLM caller for analyzing coding session transcripts.\n *\n * Mirrors codemem/observer.py — resolves provider config + auth, then calls\n * an LLM (Anthropic Messages or OpenAI Chat Completions) via fetch to extract\n * memories from session transcripts.\n *\n * Phase 1 scope: api_http runtime only (no claude_sidecar, no opencode_run).\n * Non-streaming responses via fetch (no SDK deps).\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport {\n\tbuildCodexHeaders,\n\textractOAuthAccess,\n\textractOAuthAccountId,\n\textractOAuthExpires,\n\textractProviderApiKey,\n\tloadOpenCodeOAuthCache,\n\tObserverAuthAdapter,\n\ttype ObserverAuthMaterial,\n\tredactText,\n\trenderObserverHeaders,\n\tresolveOAuthProvider,\n} from \"./observer-auth.js\";\nimport {\n\tgetOpenCodeProviderConfig,\n\tgetProviderApiKey,\n\tlistConfiguredOpenCodeProviders,\n\tresolveBuiltInProviderDefaultModel,\n\tresolveBuiltInProviderFromModel,\n\tresolveBuiltInProviderModel,\n\tresolveCustomProviderDefaultModel,\n\tresolveCustomProviderFromModel,\n\tresolveCustomProviderModel,\n\tstripJsonComments,\n\tstripTrailingCommas,\n} from \"./observer-config.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nconst DEFAULT_ANTHROPIC_MODEL = \"claude-sonnet-4-20250514\";\nconst DEFAULT_OPENAI_MODEL = \"gpt-5.1-codex-mini\";\n\nconst ANTHROPIC_MESSAGES_ENDPOINT = \"https://api.anthropic.com/v1/messages\";\nconst ANTHROPIC_VERSION = \"2023-06-01\";\n\nconst CODEX_API_ENDPOINT = \"https://chatgpt.com/backend-api/codex/responses\";\n\nconst FETCH_TIMEOUT_MS = 60_000;\n\n// Anthropic model name aliases (friendly → API id)\nconst ANTHROPIC_MODEL_ALIASES: Record<string, string> = {\n\t\"claude-4.5-haiku\": \"claude-haiku-4-5\",\n\t\"claude-4.5-sonnet\": \"claude-sonnet-4-5\",\n\t\"claude-4.5-opus\": \"claude-opus-4-5\",\n\t\"claude-4.6-sonnet\": \"claude-sonnet-4-6\",\n\t\"claude-4.6-opus\": \"claude-opus-4-6\",\n\t\"claude-4.1-opus\": \"claude-opus-4-1\",\n\t\"claude-4.0-sonnet\": \"claude-sonnet-4-0\",\n\t\"claude-4.0-opus\": \"claude-opus-4-0\",\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface ObserverConfig {\n\tobserverProvider: string | null;\n\tobserverModel: string | null;\n\tobserverRuntime: string | null;\n\tobserverApiKey: string | null;\n\tobserverBaseUrl: string | null;\n\tobserverMaxChars: number;\n\tobserverMaxTokens: number;\n\tobserverHeaders: Record<string, string>;\n\tobserverAuthSource: string;\n\tobserverAuthFile: string | null;\n\tobserverAuthCommand: string[];\n\tobserverAuthTimeoutMs: number;\n\tobserverAuthCacheTtlS: number;\n}\n\nexport interface ObserverResponse {\n\traw: string | null;\n\tparsed: Record<string, unknown> | null;\n\tprovider: string;\n\tmodel: string;\n}\n\nexport interface ObserverStatus {\n\tprovider: string;\n\tmodel: string;\n\truntime: string;\n\tauth: { source: string; type: string; hasToken: boolean };\n\tlastError?: { code: string; message: string } | null;\n}\n\n// ---------------------------------------------------------------------------\n// Config loading\n// ---------------------------------------------------------------------------\n\nfunction parseIntSafe(value: unknown, fallback: number): number {\n\tif (value == null) return fallback;\n\tconst n = typeof value === \"string\" ? Number.parseInt(value, 10) : Number(value);\n\treturn Number.isFinite(n) ? n : fallback;\n}\n\nfunction coerceStringMap(value: unknown): Record<string, string> | null {\n\tif (value == null) return null;\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed) return {};\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\tif (typeof parsed !== \"object\" || parsed == null || Array.isArray(parsed)) return null;\n\t\t\treturn parsed as Record<string, string>;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t}\n\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\treturn value as Record<string, string>;\n\t}\n\treturn null;\n}\n\nfunction coerceCommand(value: unknown): string[] | null {\n\tif (value == null) return null;\n\tif (Array.isArray(value)) {\n\t\treturn value.every((v) => typeof v === \"string\") ? (value as string[]) : null;\n\t}\n\tif (typeof value === \"string\") {\n\t\tconst trimmed = value.trim();\n\t\tif (!trimmed) return [];\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(trimmed);\n\t\t\tif (Array.isArray(parsed) && parsed.every((v: unknown) => typeof v === \"string\")) {\n\t\t\t\treturn parsed as string[];\n\t\t\t}\n\t\t} catch {\n\t\t\t/* not JSON — ignore */\n\t\t}\n\t\treturn null;\n\t}\n\treturn null;\n}\n\n/**\n * Load observer config from `~/.config/codemem/config.json{c}`.\n *\n * Reads the codemem config file (not OpenCode's) and extracts observer-related\n * fields with environment variable overrides.\n */\nexport function loadObserverConfig(): ObserverConfig {\n\tconst defaults: ObserverConfig = {\n\t\tobserverProvider: null,\n\t\tobserverModel: null,\n\t\tobserverRuntime: null,\n\t\tobserverApiKey: null,\n\t\tobserverBaseUrl: null,\n\t\tobserverMaxChars: 12_000,\n\t\tobserverMaxTokens: 4_000,\n\t\tobserverHeaders: {},\n\t\tobserverAuthSource: \"auto\",\n\t\tobserverAuthFile: null,\n\t\tobserverAuthCommand: [],\n\t\tobserverAuthTimeoutMs: 1_500,\n\t\tobserverAuthCacheTtlS: 300,\n\t};\n\n\t// Read config file\n\tconst configDir = join(homedir(), \".config\", \"codemem\");\n\tconst envPath = process.env.CODEMEM_CONFIG;\n\tlet configPath: string | null = null;\n\tif (envPath) {\n\t\tconfigPath = envPath.replace(/^~/, homedir());\n\t} else {\n\t\tconst candidates = [join(configDir, \"config.json\"), join(configDir, \"config.jsonc\")];\n\t\tconfigPath = candidates.find((p) => existsSync(p)) ?? null;\n\t}\n\n\tlet data: Record<string, unknown> = {};\n\tif (configPath && existsSync(configPath)) {\n\t\ttry {\n\t\t\tlet text = readFileSync(configPath, \"utf-8\");\n\t\t\tif (text.trim()) {\n\t\t\t\ttry {\n\t\t\t\t\tdata = JSON.parse(text) as Record<string, unknown>;\n\t\t\t\t} catch {\n\t\t\t\t\ttext = stripTrailingCommas(stripJsonComments(text));\n\t\t\t\t\tdata = JSON.parse(text) as Record<string, unknown>;\n\t\t\t\t}\n\t\t\t\tif (typeof data !== \"object\" || data == null || Array.isArray(data)) {\n\t\t\t\t\tdata = {};\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\tdata = {};\n\t\t}\n\t}\n\n\t// Apply config file values\n\tconst cfg = { ...defaults };\n\n\tif (typeof data.observer_provider === \"string\") cfg.observerProvider = data.observer_provider;\n\tif (typeof data.observer_model === \"string\") cfg.observerModel = data.observer_model;\n\tif (typeof data.observer_runtime === \"string\") cfg.observerRuntime = data.observer_runtime;\n\tif (typeof data.observer_api_key === \"string\") cfg.observerApiKey = data.observer_api_key;\n\tif (typeof data.observer_base_url === \"string\") cfg.observerBaseUrl = data.observer_base_url;\n\tcfg.observerMaxChars = parseIntSafe(data.observer_max_chars, cfg.observerMaxChars);\n\tcfg.observerMaxTokens = parseIntSafe(data.observer_max_tokens, cfg.observerMaxTokens);\n\tif (typeof data.observer_auth_source === \"string\")\n\t\tcfg.observerAuthSource = data.observer_auth_source;\n\tif (typeof data.observer_auth_file === \"string\") cfg.observerAuthFile = data.observer_auth_file;\n\tcfg.observerAuthTimeoutMs = parseIntSafe(\n\t\tdata.observer_auth_timeout_ms,\n\t\tcfg.observerAuthTimeoutMs,\n\t);\n\tcfg.observerAuthCacheTtlS = parseIntSafe(\n\t\tdata.observer_auth_cache_ttl_s,\n\t\tcfg.observerAuthCacheTtlS,\n\t);\n\n\tconst headers = coerceStringMap(data.observer_headers);\n\tif (headers) cfg.observerHeaders = headers;\n\n\tconst authCmd = coerceCommand(data.observer_auth_command);\n\tif (authCmd) cfg.observerAuthCommand = authCmd;\n\n\t// Apply env var overrides (take precedence over file)\n\tcfg.observerProvider = process.env.CODEMEM_OBSERVER_PROVIDER ?? cfg.observerProvider;\n\tcfg.observerModel = process.env.CODEMEM_OBSERVER_MODEL ?? cfg.observerModel;\n\tcfg.observerRuntime = process.env.CODEMEM_OBSERVER_RUNTIME ?? cfg.observerRuntime;\n\tcfg.observerApiKey = process.env.CODEMEM_OBSERVER_API_KEY ?? cfg.observerApiKey;\n\tcfg.observerBaseUrl = process.env.CODEMEM_OBSERVER_BASE_URL ?? cfg.observerBaseUrl;\n\tcfg.observerAuthSource = process.env.CODEMEM_OBSERVER_AUTH_SOURCE ?? cfg.observerAuthSource;\n\tcfg.observerAuthFile = process.env.CODEMEM_OBSERVER_AUTH_FILE ?? cfg.observerAuthFile;\n\tcfg.observerMaxChars = parseIntSafe(process.env.CODEMEM_OBSERVER_MAX_CHARS, cfg.observerMaxChars);\n\tcfg.observerMaxTokens = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_MAX_TOKENS,\n\t\tcfg.observerMaxTokens,\n\t);\n\tcfg.observerAuthTimeoutMs = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_AUTH_TIMEOUT_MS,\n\t\tcfg.observerAuthTimeoutMs,\n\t);\n\tcfg.observerAuthCacheTtlS = parseIntSafe(\n\t\tprocess.env.CODEMEM_OBSERVER_AUTH_CACHE_TTL_S,\n\t\tcfg.observerAuthCacheTtlS,\n\t);\n\n\tconst envHeaders = coerceStringMap(process.env.CODEMEM_OBSERVER_HEADERS);\n\tif (envHeaders) cfg.observerHeaders = envHeaders;\n\n\tconst envAuthCmd = coerceCommand(process.env.CODEMEM_OBSERVER_AUTH_COMMAND);\n\tif (envAuthCmd) cfg.observerAuthCommand = envAuthCmd;\n\n\treturn cfg;\n}\n\n// ---------------------------------------------------------------------------\n// Auth error\n// ---------------------------------------------------------------------------\n\nexport class ObserverAuthError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ObserverAuthError\";\n\t}\n}\n\nfunction isAuthStatus(status: number): boolean {\n\treturn status === 401 || status === 403;\n}\n\n// ---------------------------------------------------------------------------\n// Anthropic helpers\n// ---------------------------------------------------------------------------\n\nfunction normalizeAnthropicModel(model: string): string {\n\tconst normalized = model.trim();\n\tif (!normalized) return normalized;\n\treturn ANTHROPIC_MODEL_ALIASES[normalized.toLowerCase()] ?? normalized;\n}\n\nfunction resolveAnthropicEndpoint(): string {\n\treturn process.env.CODEMEM_ANTHROPIC_ENDPOINT ?? ANTHROPIC_MESSAGES_ENDPOINT;\n}\n\nfunction buildAnthropicHeaders(token: string, isOAuth: boolean): Record<string, string> {\n\tconst headers: Record<string, string> = {\n\t\t\"anthropic-version\": ANTHROPIC_VERSION,\n\t\t\"content-type\": \"application/json\",\n\t};\n\tif (isOAuth) {\n\t\theaders.authorization = `Bearer ${token}`;\n\t\theaders[\"anthropic-beta\"] = \"oauth-2025-04-20\";\n\t} else {\n\t\theaders[\"x-api-key\"] = token;\n\t}\n\treturn headers;\n}\n\nfunction buildAnthropicPayload(\n\tmodel: string,\n\tsystemPrompt: string,\n\tuserPrompt: string,\n\tmaxTokens: number,\n): Record<string, unknown> {\n\treturn {\n\t\tmodel: normalizeAnthropicModel(model),\n\t\tmax_tokens: maxTokens,\n\t\tsystem: systemPrompt,\n\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t};\n}\n\nfunction parseAnthropicResponse(body: Record<string, unknown>): string | null {\n\tconst content = body.content;\n\tif (!Array.isArray(content)) {\n\t\tconsole.warn(\n\t\t\t`[codemem] Anthropic response has no content array (stop_reason=${body.stop_reason ?? \"unknown\"}, ` +\n\t\t\t\t`keys=${Object.keys(body).join(\",\")})`,\n\t\t);\n\t\treturn null;\n\t}\n\tconst parts: string[] = [];\n\tfor (const block of content) {\n\t\tif (\n\t\t\ttypeof block === \"object\" &&\n\t\t\tblock != null &&\n\t\t\t(block as Record<string, unknown>).type === \"text\"\n\t\t) {\n\t\t\tconst text = (block as Record<string, unknown>).text;\n\t\t\tif (typeof text === \"string\") parts.push(text);\n\t\t}\n\t}\n\tif (parts.length === 0 && content.length > 0) {\n\t\tconst blockTypes = content\n\t\t\t.map((b) =>\n\t\t\t\ttypeof b === \"object\" && b != null ? (b as Record<string, unknown>).type : typeof b,\n\t\t\t)\n\t\t\t.join(\",\");\n\t\tconsole.warn(\n\t\t\t`[codemem] Anthropic response has ${content.length} content block(s) but no text blocks (types=${blockTypes}, ` +\n\t\t\t\t`stop_reason=${body.stop_reason ?? \"unknown\"})`,\n\t\t);\n\t}\n\treturn parts.length > 0 ? parts.join(\"\") : null;\n}\n\n// ---------------------------------------------------------------------------\n// OpenAI helpers\n// ---------------------------------------------------------------------------\n\nfunction buildOpenAIHeaders(token: string): Record<string, string> {\n\treturn {\n\t\tauthorization: `Bearer ${token}`,\n\t\t\"content-type\": \"application/json\",\n\t};\n}\n\nfunction buildOpenAIPayload(\n\tmodel: string,\n\tsystemPrompt: string,\n\tuserPrompt: string,\n\tmaxTokens: number,\n): Record<string, unknown> {\n\treturn {\n\t\tmodel,\n\t\tmax_tokens: maxTokens,\n\t\ttemperature: 0,\n\t\tmessages: [\n\t\t\t{ role: \"system\", content: systemPrompt },\n\t\t\t{ role: \"user\", content: userPrompt },\n\t\t],\n\t};\n}\n\nfunction parseOpenAIResponse(body: Record<string, unknown>): string | null {\n\tconst choices = body.choices;\n\tif (!Array.isArray(choices) || choices.length === 0) return null;\n\tconst first = choices[0] as Record<string, unknown> | undefined;\n\tif (!first) return null;\n\tconst message = first.message as Record<string, unknown> | undefined;\n\tif (!message) return null;\n\tconst content = message.content;\n\treturn typeof content === \"string\" ? content : null;\n}\n\n// ---------------------------------------------------------------------------\n// Codex consumer helpers\n// ---------------------------------------------------------------------------\n\nfunction resolveCodexEndpoint(): string {\n\treturn process.env.CODEMEM_CODEX_ENDPOINT ?? CODEX_API_ENDPOINT;\n}\n\nfunction buildCodexPayload(model: string, userPrompt: string): Record<string, unknown> {\n\treturn {\n\t\tmodel,\n\t\tinstructions: \"You are a memory observer.\",\n\t\tinput: [\n\t\t\t{\n\t\t\t\trole: \"user\",\n\t\t\t\tcontent: [{ type: \"input_text\", text: userPrompt }],\n\t\t\t},\n\t\t],\n\t\tstore: false,\n\t\tstream: true,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// SSE stream text extraction (shared for Codex and Anthropic OAuth)\n// ---------------------------------------------------------------------------\n\nfunction extractTextFromSSE(\n\trawText: string,\n\textractDelta: (event: Record<string, unknown>) => string | null,\n): string | null {\n\tconst parts: string[] = [];\n\tfor (const line of rawText.split(\"\\n\")) {\n\t\tif (!line.startsWith(\"data:\")) continue;\n\t\tconst payload = line.slice(5).trim();\n\t\tif (!payload || payload === \"[DONE]\") continue;\n\t\ttry {\n\t\t\tconst event = JSON.parse(payload) as Record<string, unknown>;\n\t\t\tconst delta = extractDelta(event);\n\t\t\tif (delta) parts.push(delta);\n\t\t} catch {\n\t\t\t// skip malformed events\n\t\t}\n\t}\n\treturn parts.length > 0 ? parts.join(\"\").trim() : null;\n}\n\nfunction extractCodexDelta(event: Record<string, unknown>): string | null {\n\tif (event.type === \"response.output_text.delta\") {\n\t\tconst delta = event.delta;\n\t\treturn typeof delta === \"string\" && delta ? delta : null;\n\t}\n\treturn null;\n}\n\nfunction extractAnthropicStreamDelta(event: Record<string, unknown>): string | null {\n\tif (event.type === \"content_block_delta\") {\n\t\tconst delta = event.delta as Record<string, unknown> | undefined;\n\t\tif (delta && delta.type === \"text_delta\") {\n\t\t\tconst text = delta.text;\n\t\t\treturn typeof text === \"string\" && text ? text : null;\n\t\t}\n\t}\n\treturn null;\n}\n\n// ---------------------------------------------------------------------------\n// nowMs helper\n// ---------------------------------------------------------------------------\n\nfunction nowMs(): number {\n\treturn Date.now();\n}\n\n// ---------------------------------------------------------------------------\n// ObserverClient\n// ---------------------------------------------------------------------------\n\n/**\n * LLM client for analyzing coding session transcripts and extracting memories.\n *\n * Resolves provider + auth from codemem config, then calls the LLM via fetch.\n * Supports Anthropic Messages API, OpenAI Chat Completions, Codex consumer\n * (OpenAI OAuth + SSE), and Anthropic OAuth consumer (SSE).\n */\nexport class ObserverClient {\n\treadonly provider: string;\n\tmodel: string;\n\treadonly runtime: string;\n\treadonly maxChars: number;\n\treadonly maxTokens: number;\n\n\t/** Resolved auth material — updated on refresh. */\n\tauth: ObserverAuthMaterial;\n\treadonly authAdapter: ObserverAuthAdapter;\n\n\tprivate _observerHeaders: Record<string, string>;\n\tprivate _customBaseUrl: string | null;\n\tprivate readonly _apiKey: string | null;\n\n\t// OAuth consumer state\n\tprivate _codexAccess: string | null = null;\n\tprivate _codexAccountId: string | null = null;\n\tprivate _anthropicOAuthAccess: string | null = null;\n\n\t// Error tracking\n\tprivate _lastErrorCode: string | null = null;\n\tprivate _lastErrorMessage: string | null = null;\n\n\tconstructor(config?: ObserverConfig) {\n\t\tconst cfg = config ?? loadObserverConfig();\n\n\t\tconst provider = (cfg.observerProvider ?? \"\").toLowerCase();\n\t\tconst model = (cfg.observerModel ?? \"\").trim();\n\n\t\t// Collect known custom providers\n\t\tconst customProviders = listConfiguredOpenCodeProviders();\n\t\tif (provider && provider !== \"openai\" && provider !== \"anthropic\") {\n\t\t\tcustomProviders.add(provider);\n\t\t}\n\n\t\t// Resolve provider\n\t\tlet resolved = provider;\n\t\tif (!resolved) {\n\t\t\tconst inferred = resolveCustomProviderFromModel(model, customProviders);\n\t\t\tif (inferred) resolved = inferred;\n\t\t}\n\t\tif (!resolved) {\n\t\t\tconst builtIn = resolveBuiltInProviderFromModel(model);\n\t\t\tif (builtIn) resolved = builtIn;\n\t\t}\n\t\tif (!resolved) {\n\t\t\tresolved = resolveOAuthProvider(null, model || DEFAULT_OPENAI_MODEL);\n\t\t}\n\t\tif (\n\t\t\tresolved !== \"openai\" &&\n\t\t\tresolved !== \"anthropic\" &&\n\t\t\tresolved !== \"opencode\" &&\n\t\t\t!customProviders.has(resolved)\n\t\t) {\n\t\t\tresolved = \"openai\";\n\t\t}\n\t\tthis.provider = resolved;\n\n\t\t// Resolve runtime (Phase 1: api_http only)\n\t\tconst runtimeRaw = cfg.observerRuntime;\n\t\tconst runtime = typeof runtimeRaw === \"string\" ? runtimeRaw.trim().toLowerCase() : \"api_http\";\n\t\tthis.runtime = runtime === \"api_http\" ? \"api_http\" : \"api_http\"; // only api_http supported\n\n\t\t// Resolve model\n\t\tif (model) {\n\t\t\tthis.model = model;\n\t\t} else if (resolved === \"anthropic\") {\n\t\t\tthis.model = DEFAULT_ANTHROPIC_MODEL;\n\t\t} else if (resolved === \"openai\") {\n\t\t\tthis.model = DEFAULT_OPENAI_MODEL;\n\t\t} else {\n\t\t\tthis.model =\n\t\t\t\tresolveBuiltInProviderDefaultModel(resolved) ??\n\t\t\t\tresolveCustomProviderDefaultModel(resolved) ??\n\t\t\t\t\"\";\n\t\t}\n\n\t\tthis.maxChars = cfg.observerMaxChars;\n\t\tthis.maxTokens = cfg.observerMaxTokens;\n\t\tthis._observerHeaders = { ...cfg.observerHeaders };\n\t\tthis._apiKey = cfg.observerApiKey ?? null;\n\n\t\tconst baseUrl = cfg.observerBaseUrl;\n\t\tthis._customBaseUrl = typeof baseUrl === \"string\" && baseUrl.trim() ? baseUrl.trim() : null;\n\n\t\t// Set up auth adapter\n\t\tthis.authAdapter = new ObserverAuthAdapter({\n\t\t\tsource: cfg.observerAuthSource,\n\t\t\tfilePath: cfg.observerAuthFile,\n\t\t\tcommand: cfg.observerAuthCommand,\n\t\t\ttimeoutMs: Math.max(100, cfg.observerAuthTimeoutMs),\n\t\t\tcacheTtlS: Math.max(0, cfg.observerAuthCacheTtlS),\n\t\t});\n\t\tthis.auth = { token: null, authType: \"none\", source: \"none\" };\n\n\t\t// Initialize provider client state\n\t\tthis._initProvider(false);\n\t}\n\n\t/** Return the resolved runtime state of this observer client. */\n\tgetStatus(): ObserverStatus {\n\t\tlet method = \"none\";\n\t\tif (this._anthropicOAuthAccess) {\n\t\t\tmethod = \"anthropic_consumer\";\n\t\t} else if (this._codexAccess) {\n\t\t\tmethod = \"codex_consumer\";\n\t\t} else if (this.provider === \"opencode\" && this.auth.token) {\n\t\t\tmethod = \"sdk_client\";\n\t\t} else if (this.auth.token) {\n\t\t\tmethod = \"api_direct\";\n\t\t}\n\n\t\tconst status: ObserverStatus = {\n\t\t\tprovider: this.provider,\n\t\t\tmodel: this.model,\n\t\t\truntime: this.runtime,\n\t\t\tauth: {\n\t\t\t\tsource: this.auth.source,\n\t\t\t\ttype: method,\n\t\t\t\thasToken: !!this.auth.token,\n\t\t\t},\n\t\t};\n\t\tif (this._lastErrorMessage) {\n\t\t\tstatus.lastError = {\n\t\t\t\tcode: this._lastErrorCode ?? \"observer_error\",\n\t\t\t\tmessage: this._lastErrorMessage,\n\t\t\t};\n\t\t}\n\t\treturn status;\n\t}\n\n\t/** Force-refresh auth credentials. */\n\trefreshAuth(force = true): void {\n\t\tthis.authAdapter.invalidateCache();\n\t\tthis._initProvider(force);\n\t}\n\n\t/**\n\t * Call the LLM with a system prompt and user prompt, return the response.\n\t *\n\t * This is the main entry point. On auth errors, attempts one refresh + retry.\n\t */\n\tasync observe(systemPrompt: string, userPrompt: string): Promise<ObserverResponse> {\n\t\t// Enforce configured prompt-length cap (matches Python behavior)\n\t\tconst maxChars = this.maxChars;\n\t\tconst minUserBudget = Math.floor(maxChars * 0.25);\n\t\tconst systemBudget = Math.max(0, maxChars - minUserBudget);\n\t\tconst clippedSystem =\n\t\t\tsystemPrompt.length > systemBudget ? systemPrompt.slice(0, systemBudget) : systemPrompt;\n\t\tconst userBudget = Math.max(minUserBudget, maxChars - clippedSystem.length);\n\t\tconst clippedUser =\n\t\t\tuserPrompt.length > userBudget ? userPrompt.slice(0, userBudget) : userPrompt;\n\n\t\ttry {\n\t\t\tconst raw = await this._callOnce(clippedSystem, clippedUser);\n\t\t\tif (raw) this._clearLastError();\n\t\t\treturn {\n\t\t\t\traw,\n\t\t\t\tparsed: raw ? tryParseJSON(raw) : null,\n\t\t\t\tprovider: this.provider,\n\t\t\t\tmodel: this.model,\n\t\t\t};\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) {\n\t\t\t\t// Attempt one auth refresh + retry\n\t\t\t\tthis.refreshAuth();\n\t\t\t\tif (!this.auth.token) throw err;\n\t\t\t\ttry {\n\t\t\t\t\tconst raw = await this._callOnce(clippedSystem, clippedUser);\n\t\t\t\t\tif (raw) this._clearLastError();\n\t\t\t\t\treturn {\n\t\t\t\t\t\traw,\n\t\t\t\t\t\tparsed: raw ? tryParseJSON(raw) : null,\n\t\t\t\t\t\tprovider: this.provider,\n\t\t\t\t\t\tmodel: this.model,\n\t\t\t\t\t};\n\t\t\t\t} catch {\n\t\t\t\t\tthrow err; // re-throw original\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Provider initialization\n\t// -----------------------------------------------------------------------\n\n\tprivate _initProvider(forceRefresh: boolean): void {\n\t\tthis._codexAccess = null;\n\t\tthis._codexAccountId = null;\n\t\tthis._anthropicOAuthAccess = null;\n\n\t\tconst oauthCache = loadOpenCodeOAuthCache();\n\t\tlet oauthAccess: string | null = null;\n\t\tlet oauthProvider: string | null = null;\n\n\t\tif (this.provider === \"openai\" || this.provider === \"anthropic\") {\n\t\t\toauthProvider = resolveOAuthProvider(this.provider, this.model);\n\t\t\toauthAccess = extractOAuthAccess(oauthCache, oauthProvider);\n\t\t\tconst oauthExpires = extractOAuthExpires(oauthCache, oauthProvider);\n\t\t\tif (oauthAccess && oauthExpires != null && oauthExpires <= nowMs()) {\n\t\t\t\toauthAccess = null;\n\t\t\t}\n\t\t}\n\n\t\tif (this.provider !== \"openai\" && this.provider !== \"anthropic\") {\n\t\t\t// Custom provider — resolve base URL, model ID, and headers from OpenCode config\n\t\t\tconst providerConfig = getOpenCodeProviderConfig(this.provider);\n\t\t\tconst hasExplicitProviderConfig = Object.keys(providerConfig).length > 0;\n\t\t\tconst [baseUrl, modelId, providerHeaders] = hasExplicitProviderConfig\n\t\t\t\t? resolveCustomProviderModel(this.provider, this.model)\n\t\t\t\t: resolveBuiltInProviderModel(this.provider, this.model);\n\n\t\t\t// Persist resolved values for use in _callOpenAIDirect\n\t\t\tif (baseUrl && !this._customBaseUrl) this._customBaseUrl = baseUrl;\n\t\t\tif (modelId) this.model = modelId;\n\t\t\tif (providerHeaders && Object.keys(providerHeaders).length > 0) {\n\t\t\t\tthis._observerHeaders = { ...this._observerHeaders, ...providerHeaders };\n\t\t\t}\n\n\t\t\tconst effectiveBaseUrl = this._customBaseUrl;\n\t\t\tif (!effectiveBaseUrl) return;\n\n\t\t\tconst cachedApiKey =\n\t\t\t\tthis.provider === \"opencode\" ? extractProviderApiKey(oauthCache, this.provider) : null;\n\t\t\tconst apiKey = getProviderApiKey(providerConfig) || this._apiKey || cachedApiKey;\n\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: apiKey,\n\t\t\t\tenvTokens: [process.env.CODEMEM_OBSERVER_API_KEY ?? \"\"],\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t} else if (this.provider === \"anthropic\") {\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: this._apiKey,\n\t\t\t\tenvTokens: [process.env.ANTHROPIC_API_KEY ?? \"\"],\n\t\t\t\toauthToken: oauthAccess,\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t\tif (this.auth.source === \"oauth\" && oauthAccess) {\n\t\t\t\tthis._anthropicOAuthAccess = oauthAccess;\n\t\t\t}\n\t\t} else {\n\t\t\t// OpenAI\n\t\t\tthis.auth = this.authAdapter.resolve({\n\t\t\t\texplicitToken: this._apiKey,\n\t\t\t\tenvTokens: [\n\t\t\t\t\tprocess.env.OPENCODE_API_KEY ?? \"\",\n\t\t\t\t\tprocess.env.OPENAI_API_KEY ?? \"\",\n\t\t\t\t\tprocess.env.CODEX_API_KEY ?? \"\",\n\t\t\t\t],\n\t\t\t\toauthToken: oauthAccess,\n\t\t\t\tforceRefresh,\n\t\t\t});\n\t\t\tif (this.auth.source === \"oauth\" && oauthAccess) {\n\t\t\t\tthis._codexAccess = oauthAccess;\n\t\t\t\tthis._codexAccountId = extractOAuthAccountId(oauthCache, oauthProvider ?? \"openai\");\n\t\t\t}\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// LLM call dispatch\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callOnce(systemPrompt: string, userPrompt: string): Promise<string | null> {\n\t\t// Codex consumer path (OpenAI OAuth)\n\t\tif (this._codexAccess) {\n\t\t\treturn this._callCodexConsumer(userPrompt);\n\t\t}\n\n\t\t// Anthropic OAuth consumer path\n\t\tif (this._anthropicOAuthAccess) {\n\t\t\treturn this._callAnthropicConsumer(systemPrompt, userPrompt);\n\t\t}\n\n\t\t// Refresh if we have no token\n\t\tif (!this.auth.token) {\n\t\t\tthis._initProvider(true);\n\t\t\tif (this._codexAccess) return this._callCodexConsumer(userPrompt);\n\t\t\tif (this._anthropicOAuthAccess) return this._callAnthropicConsumer(systemPrompt, userPrompt);\n\t\t\tif (!this.auth.token) {\n\t\t\t\tthis._setLastError(`${capitalize(this.provider)} credentials are missing.`, \"auth_missing\");\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// Direct API call via fetch\n\t\tif (this.provider === \"anthropic\") {\n\t\t\treturn this._callAnthropicDirect(systemPrompt, userPrompt);\n\t\t}\n\t\treturn this._callOpenAIDirect(systemPrompt, userPrompt);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Anthropic direct (API key)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callAnthropicDirect(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tconst url = resolveAnthropicEndpoint();\n\t\tconst token = this.auth.token ?? \"\";\n\t\tconst headers = buildAnthropicHeaders(token, false);\n\t\tconst mergedHeaders = {\n\t\t\t...headers,\n\t\t\t...renderObserverHeaders(this._observerHeaders, this.auth),\n\t\t};\n\t\tconst payload = buildAnthropicPayload(this.model, systemPrompt, userPrompt, this.maxTokens);\n\n\t\treturn this._fetchJSON(url, mergedHeaders, payload, {\n\t\t\tparseResponse: parseAnthropicResponse,\n\t\t\tproviderLabel: \"Anthropic\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// OpenAI direct (API key)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callOpenAIDirect(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tlet url: string;\n\t\tif (this._customBaseUrl) {\n\t\t\turl = `${this._customBaseUrl.replace(/\\/+$/, \"\")}/chat/completions`;\n\t\t} else {\n\t\t\turl = \"https://api.openai.com/v1/chat/completions\";\n\t\t}\n\n\t\tconst headers = buildOpenAIHeaders(this.auth.token ?? \"\");\n\t\tconst mergedHeaders = {\n\t\t\t...headers,\n\t\t\t...renderObserverHeaders(this._observerHeaders, this.auth),\n\t\t};\n\t\tconst payload = buildOpenAIPayload(this.model, systemPrompt, userPrompt, this.maxTokens);\n\n\t\treturn this._fetchJSON(url, mergedHeaders, payload, {\n\t\t\tparseResponse: parseOpenAIResponse,\n\t\t\tproviderLabel: capitalize(this.provider),\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Codex consumer (OpenAI OAuth + SSE streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callCodexConsumer(userPrompt: string): Promise<string | null> {\n\t\tif (!this._codexAccess) return null;\n\n\t\tconst headers = buildCodexHeaders(this._codexAccess, this._codexAccountId);\n\t\tif (Object.keys(this._observerHeaders).length > 0) {\n\t\t\tconst codexAuth: ObserverAuthMaterial = {\n\t\t\t\ttoken: this._codexAccess,\n\t\t\t\tauthType: \"bearer\",\n\t\t\t\tsource: this.auth.source,\n\t\t\t};\n\t\t\tObject.assign(headers, renderObserverHeaders(this._observerHeaders, codexAuth));\n\t\t}\n\t\theaders[\"content-type\"] = \"application/json\";\n\n\t\tconst payload = buildCodexPayload(this.model, userPrompt);\n\t\tconst url = resolveCodexEndpoint();\n\n\t\treturn this._fetchSSE(url, headers, payload, extractCodexDelta, {\n\t\t\tproviderLabel: \"OpenAI\",\n\t\t\tauthErrorMessage: \"OpenAI authentication failed. Refresh credentials and retry.\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Anthropic OAuth consumer (SSE streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _callAnthropicConsumer(\n\t\tsystemPrompt: string,\n\t\tuserPrompt: string,\n\t): Promise<string | null> {\n\t\tif (!this._anthropicOAuthAccess) return null;\n\n\t\tconst headers = buildAnthropicHeaders(this._anthropicOAuthAccess, true);\n\t\tif (Object.keys(this._observerHeaders).length > 0) {\n\t\t\tconst anthropicAuth: ObserverAuthMaterial = {\n\t\t\t\ttoken: this._anthropicOAuthAccess,\n\t\t\t\tauthType: \"bearer\",\n\t\t\t\tsource: this.auth.source,\n\t\t\t};\n\t\t\tObject.assign(headers, renderObserverHeaders(this._observerHeaders, anthropicAuth));\n\t\t}\n\n\t\t// Append ?beta=true to the endpoint\n\t\tconst baseEndpoint = resolveAnthropicEndpoint();\n\t\tconst endpointUrl = new URL(baseEndpoint);\n\t\tendpointUrl.searchParams.set(\"beta\", \"true\");\n\t\tconst url = endpointUrl.toString();\n\n\t\tconst payload: Record<string, unknown> = {\n\t\t\tmodel: normalizeAnthropicModel(this.model),\n\t\t\tmax_tokens: this.maxTokens,\n\t\t\tstream: true,\n\t\t\tmessages: [{ role: \"user\", content: userPrompt }],\n\t\t\tsystem: systemPrompt,\n\t\t};\n\n\t\treturn this._fetchSSE(url, headers, payload, extractAnthropicStreamDelta, {\n\t\t\tproviderLabel: \"Anthropic\",\n\t\t\tauthErrorMessage: \"Anthropic authentication failed. Refresh credentials and retry.\",\n\t\t});\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Shared fetch: JSON response (non-streaming)\n\t// -----------------------------------------------------------------------\n\n\tprivate async _fetchJSON(\n\t\turl: string,\n\t\theaders: Record<string, string>,\n\t\tpayload: Record<string, unknown>,\n\t\topts: {\n\t\t\tparseResponse: (body: Record<string, unknown>) => string | null;\n\t\t\tproviderLabel: string;\n\t\t},\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\tsignal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst errorText = await response.text().catch(() => \"\");\n\t\t\t\tthis._handleHttpError(response.status, errorText, opts.providerLabel);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tconst body = (await response.json()) as Record<string, unknown>;\n\t\t\tconst result = opts.parseResponse(body);\n\t\t\tif (result === null) {\n\t\t\t\tthis._setLastError(\n\t\t\t\t\t`${opts.providerLabel} returned 200 but response contained no extractable text.`,\n\t\t\t\t\t\"empty_response\",\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) throw err;\n\t\t\tthis._setLastError(\n\t\t\t\t`${opts.providerLabel} processing failed during observer inference.`,\n\t\t\t\t\"observer_call_failed\",\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Shared fetch: SSE streaming response\n\t// -----------------------------------------------------------------------\n\n\tprivate async _fetchSSE(\n\t\turl: string,\n\t\theaders: Record<string, string>,\n\t\tpayload: Record<string, unknown>,\n\t\textractDelta: (event: Record<string, unknown>) => string | null,\n\t\topts: { providerLabel: string; authErrorMessage: string },\n\t): Promise<string | null> {\n\t\ttry {\n\t\t\tconst response = await fetch(url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify(payload),\n\t\t\t\tsignal: AbortSignal.timeout(FETCH_TIMEOUT_MS),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\t// Consume body to avoid dangling connection\n\t\t\t\tawait response.text().catch(() => \"\");\n\t\t\t\tif (isAuthStatus(response.status)) {\n\t\t\t\t\tthis._setLastError(opts.authErrorMessage, \"auth_failed\");\n\t\t\t\t\tthrow new ObserverAuthError(`${opts.providerLabel} auth error: ${response.status}`);\n\t\t\t\t}\n\t\t\t\tthis._setLastError(\n\t\t\t\t\t`${opts.providerLabel} request failed during observer processing.`,\n\t\t\t\t\t\"provider_request_failed\",\n\t\t\t\t);\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\t// Read full response body as text and parse SSE events\n\t\t\tconst rawText = await response.text();\n\t\t\treturn extractTextFromSSE(rawText, extractDelta);\n\t\t} catch (err) {\n\t\t\tif (err instanceof ObserverAuthError) throw err;\n\t\t\tthis._setLastError(\n\t\t\t\t`${opts.providerLabel} processing failed during observer inference.`,\n\t\t\t\t\"observer_call_failed\",\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Error handling\n\t// -----------------------------------------------------------------------\n\n\tprivate _handleHttpError(status: number, errorText: string, providerLabel: string): void {\n\t\tconst summary = redactText(errorText);\n\n\t\tif (isAuthStatus(status)) {\n\t\t\tthis._setLastError(\n\t\t\t\t`${providerLabel} authentication failed. Refresh credentials and retry.`,\n\t\t\t\t\"auth_failed\",\n\t\t\t);\n\t\t\tthrow new ObserverAuthError(`${providerLabel} auth error: ${status}: ${summary}`);\n\t\t}\n\n\t\tif (status === 429) {\n\t\t\tthis._setLastError(`${providerLabel} rate limited. Retry later.`, \"rate_limited\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Check for model-not-found in Anthropic error responses\n\t\tif (errorText) {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(errorText) as Record<string, unknown>;\n\t\t\t\tconst error = parsed.error as Record<string, unknown> | undefined;\n\t\t\t\tif (error && typeof error === \"object\") {\n\t\t\t\t\tconst errorType = String(error.type ?? \"\").toLowerCase();\n\t\t\t\t\tconst message = String(error.message ?? \"\");\n\t\t\t\t\tif (errorType === \"not_found_error\" && message.toLowerCase().startsWith(\"model:\")) {\n\t\t\t\t\t\tthis._setLastError(\n\t\t\t\t\t\t\t`${providerLabel} model ID not found: ${message.split(\":\")[1]?.trim() ?? this.model}.`,\n\t\t\t\t\t\t\t\"invalid_model_id\",\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// not JSON — ignore\n\t\t\t}\n\t\t}\n\n\t\tthis._setLastError(`${providerLabel} request failed (${status}).`, \"provider_request_failed\");\n\t}\n\n\tprivate _setLastError(message: string, code?: string): void {\n\t\tconst text = message.trim();\n\t\tif (!text) return;\n\t\tthis._lastErrorMessage = text;\n\t\tthis._lastErrorCode = (code ?? \"observer_error\").trim() || \"observer_error\";\n\t}\n\n\tprivate _clearLastError(): void {\n\t\tthis._lastErrorCode = null;\n\t\tthis._lastErrorMessage = null;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Utilities\n// ---------------------------------------------------------------------------\n\nfunction capitalize(s: string): string {\n\treturn s.charAt(0).toUpperCase() + s.slice(1);\n}\n\nfunction tryParseJSON(text: string): Record<string, unknown> | null {\n\ttry {\n\t\tconst parsed = JSON.parse(text);\n\t\treturn typeof parsed === \"object\" && parsed != null && !Array.isArray(parsed)\n\t\t\t? (parsed as Record<string, unknown>)\n\t\t\t: null;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","/**\n * FTS5 full-text search, timeline, and explain for memory items.\n *\n * FTS5 MATCH queries and BM25 scoring use raw SQL (Drizzle has no FTS5 support).\n * Simple queries (anchor lookup, batch ID fetch, session projects) use Drizzle typed queries.\n * Dynamic filter queries use raw SQL since the filter builder returns SQL strings.\n */\n\nimport { and, eq, inArray } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { fromJson } from \"./db.js\";\nimport {\n\tbuildFilterClausesWithContext,\n\tnormalizeFilterStrings,\n\tnormalizeVisibilityValues,\n\tnormalizeWorkspaceKinds,\n} from \"./filters.js\";\nimport { parsePositiveMemoryId } from \"./integers.js\";\nimport { projectMatchesFilter } from \"./project.js\";\nimport * as schema from \"./schema.js\";\nimport type {\n\tExplainError,\n\tExplainItem,\n\tExplainResponse,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tTimelineItemResponse,\n} from \"./types.js\";\n\nexport interface ExplainOptions {\n\tincludePackContext?: boolean;\n}\n\n/** Lazily wrap a better-sqlite3 Database in a Drizzle ORM instance. */\nfunction getDrizzle(db: Database) {\n\treturn drizzle(db, { schema });\n}\n\n/** Structural type for the store parameter (avoids circular import). */\nexport interface StoreHandle {\n\treadonly db: import(\"better-sqlite3\").Database;\n\treadonly actorId: string;\n\treadonly deviceId: string;\n\tget(memoryId: number): MemoryItemResponse | null;\n\tmemoryOwnedBySelf(item: MemoryItem | MemoryResult | Record<string, unknown>): boolean;\n\trecent(limit?: number, filters?: MemoryFilters | null, offset?: number): MemoryItemResponse[];\n\trecentByKinds(\n\t\tkinds: string[],\n\t\tlimit?: number,\n\t\tfilters?: MemoryFilters | null,\n\t\toffset?: number,\n\t): MemoryItemResponse[];\n}\n\nconst MEMORY_KIND_BONUS: Record<string, number> = {\n\tsession_summary: 0.25,\n\tdecision: 0.2,\n\tfeature: 0.18,\n\tbugfix: 0.18,\n\trefactor: 0.17,\n\tnote: 0.15,\n\tchange: 0.12,\n\tdiscovery: 0.12,\n\tobservation: 0.1,\n\texploration: 0.1,\n\tentities: 0.05,\n};\n\nconst PERSONAL_FIRST_BONUS = 0.45;\nconst TRUST_BIAS_LEGACY_UNKNOWN_PENALTY = 0.18;\nconst TRUST_BIAS_UNREVIEWED_PENALTY = 0.12;\nconst WIDEN_SHARED_DEFAULT_MIN_PERSONAL_RESULTS = 3;\nconst WIDEN_SHARED_DEFAULT_MIN_PERSONAL_SCORE = 0.0;\nconst WIDEN_SHARED_MAX_SHARED_RESULTS = 2;\nconst PERSONAL_QUERY_PATTERNS = [\n\t/\\bwhat did i\\b/i,\n\t/\\bmy notes\\b/i,\n\t/\\bmy last session\\b/i,\n\t/\\bmy machine\\b/i,\n];\n\n/** FTS5 operators that must be stripped from user queries. */\nconst FTS5_OPERATORS = new Set([\"or\", \"and\", \"not\", \"near\", \"phrase\"]);\n\n/**\n * Expand a user query string into an FTS5 MATCH expression.\n *\n * Extracts alphanumeric tokens, filters out FTS5 operators,\n * and joins multiple tokens with OR for broader matching.\n */\nexport function expandQuery(query: string): string {\n\tconst rawTokens = query.match(/[A-Za-z0-9_]+/g);\n\tif (!rawTokens) return \"\";\n\tconst tokens = rawTokens.filter((t) => !FTS5_OPERATORS.has(t.toLowerCase()));\n\tif (tokens.length === 0) return \"\";\n\tif (tokens.length === 1) return tokens[0] as string;\n\treturn tokens.join(\" OR \");\n}\n\n/**\n * Compute a recency score for a memory based on its creation timestamp.\n *\n * Returns a value in (0, 1] where 1.0 means \"just created\" and the\n * score decays with a 7-day half-life: score = 1 / (1 + days_ago / 7).\n */\nexport function recencyScore(createdAt: string, now?: Date): number {\n\tconst parsed = Date.parse(createdAt);\n\tif (Number.isNaN(parsed)) return 0.0;\n\n\tconst referenceNow = now ?? new Date();\n\tconst ageDays = Math.max(0, Math.floor((referenceNow.getTime() - parsed) / 86_400_000));\n\treturn 1.0 / (1.0 + ageDays / 7.0);\n}\n\n/**\n * Return a scoring bonus for a memory kind.\n *\n * Higher-signal kinds (decisions, features) get a larger bonus.\n * Unknown or null kinds return 0.\n */\nexport function kindBonus(kind: string | null): number {\n\tif (!kind) return 0.0;\n\treturn MEMORY_KIND_BONUS[kind.trim().toLowerCase()] ?? 0.0;\n}\n\nfunction personalFirstEnabled(filters: MemoryFilters | undefined): boolean {\n\tif (!filters || filters.personal_first === undefined) return true;\n\tconst value = filters.personal_first;\n\tif (typeof value === \"string\") {\n\t\tconst lowered = value.trim().toLowerCase();\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(lowered)) return false;\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(lowered)) return true;\n\t}\n\treturn Boolean(value);\n}\n\nfunction trustBiasMode(filters: MemoryFilters | undefined): \"off\" | \"soft\" {\n\tconst value = String(filters?.trust_bias ?? \"off\")\n\t\t.trim()\n\t\t.toLowerCase();\n\treturn value === \"soft\" ? \"soft\" : \"off\";\n}\n\nfunction widenSharedWhenWeakEnabled(filters: MemoryFilters | undefined): boolean {\n\tif (!filters || filters.widen_shared_when_weak === undefined) return false;\n\tconst value = filters.widen_shared_when_weak;\n\tif (typeof value === \"string\") {\n\t\tconst lowered = value.trim().toLowerCase();\n\t\tif ([\"0\", \"false\", \"no\", \"off\"].includes(lowered)) return false;\n\t\tif ([\"1\", \"true\", \"yes\", \"on\"].includes(lowered)) return true;\n\t}\n\treturn Boolean(value);\n}\n\nfunction widenSharedMinPersonalResults(filters: MemoryFilters | undefined): number {\n\tconst value = filters?.widen_shared_min_personal_results;\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn WIDEN_SHARED_DEFAULT_MIN_PERSONAL_RESULTS;\n\t}\n\treturn Math.max(1, Math.trunc(value));\n}\n\nfunction widenSharedMinPersonalScore(filters: MemoryFilters | undefined): number {\n\tconst value = filters?.widen_shared_min_personal_score;\n\tif (typeof value !== \"number\" || !Number.isFinite(value)) {\n\t\treturn WIDEN_SHARED_DEFAULT_MIN_PERSONAL_SCORE;\n\t}\n\treturn Math.max(0, value);\n}\n\nfunction queryBlocksSharedWidening(query: string): boolean {\n\treturn PERSONAL_QUERY_PATTERNS.some((pattern) => pattern.test(query));\n}\n\nfunction filtersBlockSharedWidening(filters: MemoryFilters | undefined): boolean {\n\tif (!filters) return false;\n\tconst ownershipScope = String(filters.ownership_scope ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (ownershipScope === \"mine\" || ownershipScope === \"theirs\") return true;\n\tconst includeVisibility = normalizeVisibilityValues(\n\t\tfilters.include_visibility ?? filters.visibility,\n\t);\n\tconst excludeVisibility = normalizeVisibilityValues(filters.exclude_visibility);\n\tconst includeWorkspaceIds = normalizeFilterStrings(filters.include_workspace_ids);\n\tconst excludeWorkspaceIds = normalizeFilterStrings(filters.exclude_workspace_ids);\n\tconst includeWorkspaceKinds = normalizeWorkspaceKinds(filters.include_workspace_kinds);\n\tconst excludeWorkspaceKinds = normalizeWorkspaceKinds(filters.exclude_workspace_kinds);\n\tif (includeVisibility.length || includeWorkspaceIds.length || includeWorkspaceKinds.length)\n\t\treturn true;\n\tif (excludeVisibility.includes(\"private\") || excludeVisibility.includes(\"shared\")) return true;\n\tif (excludeWorkspaceKinds.includes(\"shared\") || excludeWorkspaceKinds.includes(\"personal\")) {\n\t\treturn true;\n\t}\n\treturn excludeWorkspaceIds.some(\n\t\t(value) => value.startsWith(\"personal:\") || value.startsWith(\"shared:\"),\n\t);\n}\n\nfunction sharedWideningFilters(filters: MemoryFilters | undefined): MemoryFilters {\n\treturn {\n\t\t...(filters ?? {}),\n\t\tvisibility: undefined,\n\t\townership_scope: undefined,\n\t\tinclude_visibility: [\"shared\"],\n\t\tinclude_workspace_kinds: [\"shared\"],\n\t\tpersonal_first: false,\n\t\twiden_shared_when_weak: false,\n\t};\n}\n\nfunction markWideningMetadata(items: MemoryResult[]): MemoryResult[] {\n\treturn items.map((item) => ({\n\t\t...item,\n\t\tmetadata: { ...(item.metadata ?? {}), widened_from_shared: true },\n\t}));\n}\n\nfunction personalBias(\n\tstore: StoreHandle,\n\titem: MemoryResult,\n\tfilters: MemoryFilters | undefined,\n): number {\n\tif (!personalFirstEnabled(filters)) return 0.0;\n\treturn store.memoryOwnedBySelf(item) ? PERSONAL_FIRST_BONUS : 0.0;\n}\n\nfunction sharedTrustPenalty(\n\tstore: StoreHandle,\n\titem: MemoryResult,\n\tfilters: MemoryFilters | undefined,\n): number {\n\tif (trustBiasMode(filters) !== \"soft\") return 0.0;\n\tif (store.memoryOwnedBySelf(item)) return 0.0;\n\tconst metadata = item.metadata ?? {};\n\tconst visibility = String(metadata.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tconst workspaceKind = String(metadata.workspace_kind ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (visibility !== \"shared\" && workspaceKind !== \"shared\") return 0.0;\n\tconst trustState = String(metadata.trust_state ?? \"trusted\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (trustState === \"legacy_unknown\") return TRUST_BIAS_LEGACY_UNKNOWN_PENALTY;\n\tif (trustState === \"unreviewed\") return TRUST_BIAS_UNREVIEWED_PENALTY;\n\treturn 0.0;\n}\n\nfunction canonicalPath(rawPath: string): string {\n\tlet path = rawPath.trim().replaceAll(\"\\\\\", \"/\");\n\tif (path.startsWith(\"./\")) {\n\t\tpath = path.slice(2);\n\t}\n\tconst parts = path.split(\"/\").filter((part) => part && part !== \".\");\n\tif (parts.length === 0) return \"\";\n\treturn parts.join(\"/\").toLowerCase();\n}\n\nfunction pathSegments(path: string): string[] {\n\tconst canonical = canonicalPath(path);\n\tif (!canonical) return [];\n\treturn canonical.split(\"/\");\n}\n\nfunction pathBasename(path: string): string {\n\tconst segments = pathSegments(path);\n\tif (segments.length === 0) return \"\";\n\treturn segments[segments.length - 1] ?? \"\";\n}\n\nfunction pathSegmentsOverlap(a: string[], b: string[]): boolean {\n\tif (a.length === 0 || b.length === 0) return false;\n\tif (a.length <= b.length) {\n\t\treturn b.slice(b.length - a.length).join(\"/\") === a.join(\"/\");\n\t}\n\treturn a.slice(a.length - b.length).join(\"/\") === b.join(\"/\");\n}\n\nfunction normalizeWorkingSetPaths(value: unknown): string[] {\n\tif (!Array.isArray(value)) return [];\n\tconst normalized: string[] = [];\n\tconst seen = new Set<string>();\n\tfor (const raw of value) {\n\t\tif (typeof raw !== \"string\") continue;\n\t\tconst path = canonicalPath(raw);\n\t\tif (!path || seen.has(path)) continue;\n\t\tseen.add(path);\n\t\tnormalized.push(path);\n\t}\n\treturn normalized;\n}\n\nfunction memoryFilesModified(item: MemoryResult): string[] {\n\tconst metadata = item.metadata ?? {};\n\tconst rawPaths = metadata.files_modified;\n\tif (!Array.isArray(rawPaths)) return [];\n\tconst normalized: string[] = [];\n\tfor (const raw of rawPaths) {\n\t\tif (typeof raw !== \"string\") continue;\n\t\tconst path = canonicalPath(raw);\n\t\tif (path) normalized.push(path);\n\t}\n\treturn normalized;\n}\n\nfunction workingSetOverlapBoost(item: MemoryResult, workingSetPaths: string[]): number {\n\tif (workingSetPaths.length === 0) return 0.0;\n\tconst itemPaths = memoryFilesModified(item);\n\tif (itemPaths.length === 0) return 0.0;\n\n\tconst itemPathSegments = [...new Set(itemPaths)].map((path) => pathSegments(path));\n\tconst workingSetSegments = [...new Set(workingSetPaths)].map((path) => pathSegments(path));\n\tconst itemBasenames = new Set(itemPaths.map((path) => pathBasename(path)).filter(Boolean));\n\tconst workingSetBasenames = new Set(\n\t\tworkingSetPaths.map((path) => pathBasename(path)).filter(Boolean),\n\t);\n\n\tlet directHits = 0;\n\tfor (const itemSegment of itemPathSegments) {\n\t\tif (workingSetSegments.some((wsSegment) => pathSegmentsOverlap(itemSegment, wsSegment))) {\n\t\t\tdirectHits += 1;\n\t\t}\n\t}\n\n\tlet basenameHits = 0;\n\tfor (const basename of itemBasenames) {\n\t\tif (workingSetBasenames.has(basename)) basenameHits += 1;\n\t}\n\n\tconst boost = directHits * 0.16 + basenameHits * 0.06;\n\treturn Math.min(0.32, boost);\n}\n\n/**\n * Re-rank search results by combining BM25 score, recency, kind bonus,\n * personal bias, and shared trust penalty.\n */\nexport function rerankResults(\n\tstore: StoreHandle,\n\tresults: MemoryResult[],\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst referenceNow = new Date();\n\tconst workingSetPaths = normalizeWorkingSetPaths(filters?.working_set_paths);\n\n\tconst scored = results.map((item) => ({\n\t\titem,\n\t\tcombinedScore:\n\t\t\titem.score * 1.5 +\n\t\t\trecencyScore(item.created_at, referenceNow) +\n\t\t\tkindBonus(item.kind) +\n\t\t\tworkingSetOverlapBoost(item, workingSetPaths) +\n\t\t\tpersonalBias(store, item, filters) -\n\t\t\tsharedTrustPenalty(store, item, filters),\n\t}));\n\n\tscored.sort((a, b) => b.combinedScore - a.combinedScore);\n\n\treturn scored.slice(0, limit).map((s) => s.item);\n}\n\n/**\n * Execute an FTS5 full-text search against memory_items.\n *\n * Uses expandQuery to prepare the MATCH expression, applies filters\n * via buildFilterClauses, and re-ranks results. Optionally widens to\n * shared workspaces when personal results are weak.\n */\nexport function search(\n\tstore: StoreHandle,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst primary = searchOnce(store, query, limit, filters);\n\tif (\n\t\t!widenSharedWhenWeakEnabled(filters) ||\n\t\t!query ||\n\t\tqueryBlocksSharedWidening(query) ||\n\t\tfiltersBlockSharedWidening(filters)\n\t) {\n\t\treturn primary;\n\t}\n\n\tconst personalResults = primary.filter((item) => store.memoryOwnedBySelf(item));\n\tconst strongestPersonalScore = personalResults[0]?.score ?? -Infinity;\n\tconst personalStrongEnough =\n\t\tpersonalResults.length >= widenSharedMinPersonalResults(filters) &&\n\t\tstrongestPersonalScore >= widenSharedMinPersonalScore(filters);\n\tif (personalStrongEnough) return primary;\n\n\tconst shared = markWideningMetadata(\n\t\tsearchOnce(\n\t\t\tstore,\n\t\t\tquery,\n\t\t\tWIDEN_SHARED_MAX_SHARED_RESULTS,\n\t\t\tsharedWideningFilters(filters),\n\t\t).filter((item) => !store.memoryOwnedBySelf(item)),\n\t);\n\tconst seen = new Set(primary.map((item) => item.id));\n\tconst combined = [...primary];\n\tlet addedShared = 0;\n\tfor (const item of shared) {\n\t\tif (seen.has(item.id)) continue;\n\t\tseen.add(item.id);\n\t\tcombined.push(item);\n\t\taddedShared += 1;\n\t\tif (addedShared >= WIDEN_SHARED_MAX_SHARED_RESULTS) break;\n\t}\n\treturn combined;\n}\n\nfunction searchOnce(\n\tstore: StoreHandle,\n\tquery: string,\n\tlimit = 10,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst effectiveLimit = Math.max(1, Math.trunc(limit));\n\tconst expanded = expandQuery(query);\n\tif (!expanded) return [];\n\n\t// Widen the SQL candidate set before reranking — kindBonus can promote items\n\t// that SQL ordering missed, so we fetch more than the final limit.\n\tconst queryLimit = Math.min(Math.max(effectiveLimit * 4, effectiveLimit + 8), 200);\n\n\tconst params: unknown[] = [expanded];\n\tconst whereClauses = [\"memory_items.active = 1\", \"memory_fts MATCH ?\"];\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\twhereClauses.push(...filterResult.clauses);\n\tparams.push(...filterResult.params);\n\n\tconst where = whereClauses.join(\" AND \");\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\n\tconst sql = `\n\t\tSELECT memory_items.*,\n\t\t\t-bm25(memory_fts, 1.0, 1.0, 0.25) AS score,\n\t\t\t(1.0 / (1.0 + ((julianday('now') - julianday(memory_items.created_at)) / 7.0))) AS recency\n\t\tFROM memory_fts\n\t\tJOIN memory_items ON memory_items.id = memory_fts.rowid\n\t\t${joinClause}\n\t\tWHERE ${where}\n\t\tORDER BY (score * 1.5 + recency) DESC, memory_items.created_at DESC, memory_items.id DESC\n\t\tLIMIT ?\n\t`;\n\tparams.push(queryLimit);\n\n\tconst rows = store.db.prepare(sql).all(...params) as Record<string, unknown>[];\n\n\tconst results: MemoryResult[] = rows.map((row) => {\n\t\tconst metadata: Record<string, unknown> = {\n\t\t\t...fromJson(row.metadata_json as string | null),\n\t\t};\n\t\tfor (const key of [\n\t\t\t\"actor_id\",\n\t\t\t\"actor_display_name\",\n\t\t\t\"visibility\",\n\t\t\t\"workspace_id\",\n\t\t\t\"workspace_kind\",\n\t\t\t\"origin_device_id\",\n\t\t\t\"origin_source\",\n\t\t\t\"trust_state\",\n\t\t] as const) {\n\t\t\tconst value = row[key];\n\t\t\tif (value != null) metadata[key] = value;\n\t\t}\n\n\t\t// Propagate files_modified into metadata when stored as a JSON array\n\t\tif (row.files_modified && typeof row.files_modified === \"string\") {\n\t\t\ttry {\n\t\t\t\tconst parsed: unknown = JSON.parse(row.files_modified as string);\n\t\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\t\tmetadata.files_modified ??= parsed;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// not valid JSON — ignore\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tid: row.id as number,\n\t\t\tkind: row.kind as string,\n\t\t\ttitle: row.title as string,\n\t\t\tbody_text: row.body_text as string,\n\t\t\tconfidence: row.confidence as number,\n\t\t\tcreated_at: row.created_at as string,\n\t\t\tupdated_at: row.updated_at as string,\n\t\t\ttags_text: (row.tags_text as string) ?? \"\",\n\t\t\tscore: Number(row.score),\n\t\t\tsession_id: row.session_id as number,\n\t\t\tmetadata,\n\t\t};\n\t});\n\n\treturn rerankResults(store, results, effectiveLimit, filters);\n}\n\n/**\n * Return a chronological window of memories around an anchor.\n *\n * Finds an anchor by memoryId or query, then fetches neighbors in the\n * same session ordered by created_at.\n */\nexport function timeline(\n\tstore: StoreHandle,\n\tquery?: string | null,\n\tmemoryId?: number | null,\n\tdepthBefore = 3,\n\tdepthAfter = 3,\n\tfilters?: MemoryFilters | null,\n): TimelineItemResponse[] {\n\t// Find anchor: prefer explicit memoryId, fall back to search\n\tlet anchorRef: { id: number; session_id: number; created_at: string } | null = null;\n\tif (memoryId != null) {\n\t\tconst row = store.get(memoryId);\n\t\tif (row) {\n\t\t\tanchorRef = { id: row.id, session_id: row.session_id, created_at: row.created_at };\n\t\t}\n\t}\n\tif (anchorRef == null && query) {\n\t\tconst matches = search(store, query, 1, filters ?? undefined);\n\t\tif (matches.length > 0) {\n\t\t\tconst m = matches[0] as MemoryResult;\n\t\t\tanchorRef = {\n\t\t\t\tid: m.id,\n\t\t\t\tsession_id: m.session_id,\n\t\t\t\tcreated_at: m.created_at,\n\t\t\t};\n\t\t}\n\t}\n\tif (anchorRef == null) {\n\t\treturn [];\n\t}\n\n\treturn timelineAround(store, anchorRef, depthBefore, depthAfter, filters);\n}\n\n/** Fetch memories before/after an anchor within the same session. */\nfunction timelineAround(\n\tstore: StoreHandle,\n\tanchor: { id: number; session_id: number; created_at: string },\n\tdepthBefore: number,\n\tdepthAfter: number,\n\tfilters?: MemoryFilters | null,\n): TimelineItemResponse[] {\n\tconst anchorId = anchor.id;\n\tconst anchorCreatedAt = anchor.created_at;\n\tconst anchorSessionId = anchor.session_id;\n\n\tif (!anchorId || !anchorCreatedAt) {\n\t\treturn [];\n\t}\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\tconst whereParts = [\"memory_items.active = 1\", ...filterResult.clauses];\n\tconst baseParams = [...filterResult.params];\n\n\tif (anchorSessionId) {\n\t\twhereParts.push(\"memory_items.session_id = ?\");\n\t\tbaseParams.push(anchorSessionId);\n\t}\n\n\tconst whereClause = whereParts.join(\" AND \");\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\n\t// Before: older memories, descending (we'll reverse later)\n\tconst beforeRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${whereClause} AND memory_items.created_at < ?\n\t\t\t ORDER BY memory_items.created_at DESC\n\t\t\t LIMIT ?`,\n\t\t)\n\t\t.all(...baseParams, anchorCreatedAt, depthBefore) as MemoryItem[];\n\n\t// After: newer memories, ascending\n\tconst afterRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t\t FROM memory_items\n\t\t\t ${joinClause}\n\t\t\t WHERE ${whereClause} AND memory_items.created_at > ?\n\t\t\t ORDER BY memory_items.created_at ASC\n\t\t\t LIMIT ?`,\n\t\t)\n\t\t.all(...baseParams, anchorCreatedAt, depthAfter) as MemoryItem[];\n\n\t// Re-fetch anchor row to get full columns (anchor from search may be partial)\n\tconst d = getDrizzle(store.db);\n\tconst anchorRow = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(and(eq(schema.memoryItems.id, anchorId), eq(schema.memoryItems.active, 1)))\n\t\t.get() as MemoryItem | undefined;\n\n\t// Combine: reversed(before) + anchor + after\n\tconst rows: MemoryItem[] = [...beforeRows.reverse()];\n\tif (anchorRow) {\n\t\trows.push(anchorRow);\n\t}\n\trows.push(...afterRows);\n\n\treturn rows.map((row) => {\n\t\tconst { metadata_json, ...rest } = row;\n\t\treturn { ...rest, metadata_json: fromJson(metadata_json), linked_prompt: null };\n\t});\n}\n\n/**\n * Deduplicate an array of IDs, preserving order. Returns valid int IDs\n * and a list of values that could not be parsed as integers.\n */\nexport function dedupeOrderedIds(ids: unknown[]): { ordered: number[]; invalid: string[] } {\n\tconst seen = new Set<number>();\n\tconst ordered: number[] = [];\n\tconst invalid: string[] = [];\n\n\tfor (const rawId of ids) {\n\t\tconst parsed = parsePositiveMemoryId(rawId);\n\t\tif (parsed == null) {\n\t\t\tinvalid.push(String(rawId));\n\t\t\tcontinue;\n\t\t}\n\t\tif (seen.has(parsed)) continue;\n\t\tseen.add(parsed);\n\t\tordered.push(parsed);\n\t}\n\n\treturn { ordered, invalid };\n}\n\n/** Build an explain payload for a single memory item. */\nfunction explainItem(\n\titem: MemoryResult,\n\tsource: string,\n\trank: number | null,\n\tqueryTokens: string[],\n\tprojectFilter: string | null | undefined,\n\tprojectValue: string | null | undefined,\n\tincludePackContext: boolean,\n\treferenceNow: Date,\n): ExplainItem {\n\tconst text = `${item.title} ${item.body_text} ${item.tags_text}`.toLowerCase();\n\tconst matchedTerms = queryTokens.filter((token) => text.includes(token));\n\n\tconst baseScore: number | null =\n\t\tsource === \"query\" || source === \"query+id_lookup\" ? item.score : null;\n\tconst recencyComponent = recencyScore(item.created_at, referenceNow);\n\tconst kindComponent = kindBonus(item.kind);\n\n\tlet totalScore: number | null = null;\n\tif (baseScore != null) {\n\t\ttotalScore = baseScore * 1.5 + recencyComponent + kindComponent;\n\t}\n\n\treturn {\n\t\tid: item.id,\n\t\tkind: item.kind,\n\t\ttitle: item.title,\n\t\tcreated_at: item.created_at,\n\t\tproject: projectValue ?? null,\n\t\tretrieval: {\n\t\t\tsource,\n\t\t\trank,\n\t\t},\n\t\tscore: {\n\t\t\ttotal: totalScore,\n\t\t\tcomponents: {\n\t\t\t\tbase: baseScore,\n\t\t\t\trecency: recencyComponent,\n\t\t\t\tkind_bonus: kindComponent,\n\t\t\t\tpersonal_bias: 0.0,\n\t\t\t\tsemantic_boost: null,\n\t\t\t},\n\t\t},\n\t\tmatches: {\n\t\t\tquery_terms: matchedTerms,\n\t\t\tproject_match: projectMatchesFilter(projectFilter, projectValue),\n\t\t},\n\t\tpack_context: includePackContext ? { included: null, section: null } : null,\n\t};\n}\n\nfunction loadItemsByIdsForExplain(\n\tstore: StoreHandle,\n\tids: number[],\n\tfilters: MemoryFilters,\n): {\n\titems: MemoryResult[];\n\tmissingNotFound: number[];\n\tmissingProjectMismatch: number[];\n\tmissingFilterMismatch: number[];\n} {\n\tif (ids.length === 0) {\n\t\treturn {\n\t\t\titems: [],\n\t\t\tmissingNotFound: [],\n\t\t\tmissingProjectMismatch: [],\n\t\t\tmissingFilterMismatch: [],\n\t\t};\n\t}\n\n\tconst d = getDrizzle(store.db);\n\tconst allRows = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(and(eq(schema.memoryItems.active, 1), inArray(schema.memoryItems.id, ids)))\n\t\t.all() as MemoryItem[];\n\tconst allFoundIds = new Set(allRows.map((item) => item.id));\n\n\t// Placeholders for the dynamic-filter raw SQL queries below\n\tconst placeholders = ids.map(() => \"?\").join(\", \");\n\n\tlet projectScopedRows = allRows;\n\tlet projectScopedIds = new Set(allFoundIds);\n\tif (filters.project) {\n\t\tconst projectFiltersOnly: MemoryFilters = { project: filters.project };\n\t\tconst projectFilterResult = buildFilterClausesWithContext(projectFiltersOnly, {\n\t\t\tactorId: store.actorId,\n\t\t\tdeviceId: store.deviceId,\n\t\t});\n\t\tif (projectFilterResult.clauses.length > 0) {\n\t\t\tconst projectJoin = projectFilterResult.joinSessions\n\t\t\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t\t\t: \"\";\n\t\t\tprojectScopedRows = store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT memory_items.*\n\t\t\t\t FROM memory_items\n\t\t\t\t ${projectJoin}\n\t\t\t\t WHERE memory_items.active = 1\n\t\t\t\t AND memory_items.id IN (${placeholders})\n\t\t\t\t AND ${projectFilterResult.clauses.join(\" AND \")}`,\n\t\t\t\t)\n\t\t\t\t.all(...ids, ...projectFilterResult.params) as MemoryItem[];\n\t\t\tprojectScopedIds = new Set(projectScopedRows.map((item) => item.id));\n\t\t}\n\t}\n\n\tconst filterResult = buildFilterClausesWithContext(filters, {\n\t\tactorId: store.actorId,\n\t\tdeviceId: store.deviceId,\n\t});\n\tconst joinClause = filterResult.joinSessions\n\t\t? \"JOIN sessions ON sessions.id = memory_items.session_id\"\n\t\t: \"\";\n\tconst scopedRows = store.db\n\t\t.prepare(\n\t\t\t`SELECT memory_items.*\n\t\t FROM memory_items\n\t\t ${joinClause}\n\t\t WHERE ${[\"memory_items.active = 1\", `memory_items.id IN (${placeholders})`, ...filterResult.clauses].join(\" AND \")}`,\n\t\t)\n\t\t.all(...ids, ...filterResult.params) as MemoryItem[];\n\tconst scopedIds = new Set(scopedRows.map((item) => item.id));\n\n\tconst missingNotFound = ids.filter((memoryId) => !allFoundIds.has(memoryId));\n\tconst missingProjectMismatch = ids.filter(\n\t\t(memoryId) => allFoundIds.has(memoryId) && !projectScopedIds.has(memoryId),\n\t);\n\tconst missingFilterMismatch = ids.filter(\n\t\t(memoryId) => projectScopedIds.has(memoryId) && !scopedIds.has(memoryId),\n\t);\n\n\tconst items = scopedRows.map((row) => ({\n\t\tid: row.id,\n\t\tkind: row.kind,\n\t\ttitle: row.title,\n\t\tbody_text: row.body_text,\n\t\tconfidence: row.confidence ?? 0,\n\t\tcreated_at: row.created_at,\n\t\tupdated_at: row.updated_at,\n\t\ttags_text: row.tags_text ?? \"\",\n\t\tscore: 0,\n\t\tsession_id: row.session_id,\n\t\tmetadata: fromJson(row.metadata_json),\n\t}));\n\n\treturn { items, missingNotFound, missingProjectMismatch, missingFilterMismatch };\n}\n\nfunction loadSessionProjects(\n\tstore: StoreHandle,\n\tsessionIds: Set<number>,\n): Map<number, string | null> {\n\tif (sessionIds.size === 0) return new Map();\n\tconst orderedIds = [...sessionIds].sort((a, b) => a - b);\n\tconst d = getDrizzle(store.db);\n\tconst rows = d\n\t\t.select({ id: schema.sessions.id, project: schema.sessions.project })\n\t\t.from(schema.sessions)\n\t\t.where(inArray(schema.sessions.id, orderedIds))\n\t\t.all();\n\treturn new Map(rows.map((row) => [row.id, row.project]));\n}\n\n/**\n * Explain search results with scoring breakdown.\n *\n * Accepts a query and/or explicit IDs, merges results, and returns a\n * detailed scoring payload for each item including retrieval source,\n * score components, and term matches.\n */\nexport function explain(\n\tstore: StoreHandle,\n\tquery?: string | null,\n\tids?: unknown[] | null,\n\tlimit = 10,\n\tfilters?: MemoryFilters | null,\n\toptions?: ExplainOptions,\n): ExplainResponse {\n\tconst includePackContext = options?.includePackContext ?? false;\n\tconst normalizedQuery = (query ?? \"\").trim();\n\tconst { ordered: orderedIds, invalid: invalidIds } = dedupeOrderedIds(ids ?? []);\n\n\tconst errors: ExplainError[] = [];\n\tif (invalidIds.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"INVALID_ARGUMENT\",\n\t\t\tfield: \"ids\",\n\t\t\tmessage: \"some ids are not valid integers\",\n\t\t\tids: invalidIds,\n\t\t});\n\t}\n\n\t// Require at least one of query or ids\n\tif (!normalizedQuery && orderedIds.length === 0) {\n\t\terrors.push({\n\t\t\tcode: \"INVALID_ARGUMENT\",\n\t\t\tfield: \"query\",\n\t\t\tmessage: \"at least one of query or ids is required\",\n\t\t});\n\t\treturn {\n\t\t\titems: [],\n\t\t\tmissing_ids: [],\n\t\t\terrors,\n\t\t\tmetadata: {\n\t\t\t\tquery: null,\n\t\t\t\tproject: null,\n\t\t\t\trequested_ids_count: orderedIds.length,\n\t\t\t\treturned_items_count: 0,\n\t\t\t\tinclude_pack_context: includePackContext,\n\t\t\t},\n\t\t};\n\t}\n\n\t// Query-based results\n\tlet queryResults: MemoryResult[] = [];\n\tif (normalizedQuery) {\n\t\tqueryResults = search(\n\t\t\tstore,\n\t\t\tnormalizedQuery,\n\t\t\tMath.max(1, Math.trunc(limit)),\n\t\t\tfilters ?? undefined,\n\t\t);\n\t}\n\n\t// Build rank map for query results\n\tconst queryRank = new Map<number, number>();\n\tfor (let i = 0; i < queryResults.length; i++) {\n\t\tqueryRank.set((queryResults[i] as MemoryResult).id, i + 1);\n\t}\n\n\tconst {\n\t\titems: idRows,\n\t\tmissingNotFound,\n\t\tmissingProjectMismatch,\n\t\tmissingFilterMismatch,\n\t} = loadItemsByIdsForExplain(store, orderedIds, filters ?? {});\n\tconst idLookup = new Map(idRows.map((item) => [item.id, item]));\n\n\t// Merge: query results first, then id-lookup results not already seen\n\tconst explicitIdSet = new Set(orderedIds);\n\tconst selectedIds = new Set<number>();\n\tconst orderedItems: Array<{ item: MemoryResult; source: string; rank: number | null }> = [];\n\n\tfor (const item of queryResults) {\n\t\tselectedIds.add(item.id);\n\t\tconst source = explicitIdSet.has(item.id) ? \"query+id_lookup\" : \"query\";\n\t\torderedItems.push({ item, source, rank: queryRank.get(item.id) ?? null });\n\t}\n\n\tfor (const memId of orderedIds) {\n\t\tif (selectedIds.has(memId)) continue;\n\t\tconst item = idLookup.get(memId);\n\t\tif (!item) continue;\n\t\torderedItems.push({ item, source: \"id_lookup\", rank: null });\n\t\tselectedIds.add(memId);\n\t}\n\n\t// Tokenize query for term matching\n\tconst queryTokens = normalizedQuery\n\t\t? (normalizedQuery.match(/[A-Za-z0-9_]+/g) ?? []).map((t) => t.toLowerCase())\n\t\t: [];\n\n\tconst sessionProjects = loadSessionProjects(\n\t\tstore,\n\t\tnew Set(orderedItems.map(({ item }) => item.session_id).filter((sessionId) => sessionId > 0)),\n\t);\n\tconst referenceNow = new Date();\n\tconst itemsPayload = orderedItems.map(({ item, source, rank }) =>\n\t\texplainItem(\n\t\t\titem,\n\t\t\tsource,\n\t\t\trank,\n\t\t\tqueryTokens,\n\t\t\tfilters?.project ?? null,\n\t\t\tsessionProjects.get(item.session_id) ?? null,\n\t\t\tincludePackContext,\n\t\t\treferenceNow,\n\t\t),\n\t);\n\n\t// Collect all missing IDs (requested but not returned)\n\tconst missingIds = orderedIds.filter((id) => !selectedIds.has(id));\n\n\tif (missingNotFound.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"NOT_FOUND\",\n\t\t\tfield: \"ids\",\n\t\t\tmessage: \"some requested ids were not found\",\n\t\t\tids: missingNotFound,\n\t\t});\n\t}\n\tif (missingProjectMismatch.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"PROJECT_MISMATCH\",\n\t\t\tfield: \"project\",\n\t\t\tmessage: \"some requested ids are outside the requested project scope\",\n\t\t\tids: missingProjectMismatch,\n\t\t});\n\t}\n\tif (missingFilterMismatch.length > 0) {\n\t\terrors.push({\n\t\t\tcode: \"FILTER_MISMATCH\",\n\t\t\tfield: \"filters\",\n\t\t\tmessage: \"some requested ids do not match the provided filters\",\n\t\t\tids: missingFilterMismatch,\n\t\t});\n\t}\n\n\treturn {\n\t\titems: itemsPayload,\n\t\tmissing_ids: missingIds,\n\t\terrors,\n\t\tmetadata: {\n\t\t\tquery: normalizedQuery || null,\n\t\t\tproject: filters?.project ?? null,\n\t\t\trequested_ids_count: orderedIds.length,\n\t\t\treturned_items_count: itemsPayload.length,\n\t\t\tinclude_pack_context: includePackContext,\n\t\t},\n\t};\n}\n","/**\n * Memory pack builder — port of codemem/store/packs.py.\n *\n * Builds a formatted \"memory pack\" from search results, organized into\n * sections (summary, timeline, observations) with token budgeting.\n *\n * Ported: exact dedup, tag-overlap sorting, summary/observation fallback,\n * support_count, separate section dedup, semantic candidate merging.\n *\n * Semantic candidate merging is supported via `buildMemoryPackAsync` or\n * by passing pre-computed semantic results to `buildMemoryPack`.\n *\n * NOT ported: fuzzy search, pack delta\n * tracking, discovery-token work estimation.\n */\n\nimport type { Database } from \"./db.js\";\nimport { projectBasename } from \"./project.js\";\nimport type { StoreHandle } from \"./search.js\";\nimport { rerankResults, search, timeline } from \"./search.js\";\nimport type {\n\tMemoryFilters,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tPackItem,\n\tPackResponse,\n\tTimelineItemResponse,\n} from \"./types.js\";\nimport { semanticSearch } from \"./vectors.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Observation kind priority — higher-signal kinds sort first. */\nconst OBSERVATION_KIND_PRIORITY: Record<string, number> = {\n\tdecision: 0,\n\tfeature: 1,\n\tbugfix: 2,\n\trefactor: 3,\n\tchange: 4,\n\tdiscovery: 5,\n\texploration: 6,\n\tnote: 7,\n};\n\nconst TASK_RECENCY_DAYS = 365;\n\nconst TASK_HINT_QUERY =\n\t\"todo todos task tasks pending follow up follow-up next resume continue backlog pick up pick-up\";\n\nconst RECALL_HINT_QUERY = \"session summary recap remember last time previous work\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Rough token estimate: ~4 chars per token. */\nexport function estimateTokens(text: string): number {\n\treturn Math.ceil(text.length / 4);\n}\n\n/** Format a single memory item as a pack line. */\nfunction formatItem(item: MemoryResult): string {\n\tconst parts = [`[${item.id}]`, `(${item.kind})`, item.title];\n\tif (item.body_text) {\n\t\tparts.push(\"-\", item.body_text);\n\t}\n\treturn parts.join(\" \");\n}\n\n/** Build a formatted section with header and items. */\nfunction formatSection(header: string, items: MemoryResult[]): string {\n\tconst heading = `## ${header}`;\n\tif (items.length === 0) return `${heading}\\n`;\n\treturn [heading, ...items.map(formatItem)].join(\"\\n\");\n}\n\n// ---------------------------------------------------------------------------\n// Pack item shape (what goes into items array)\n// ---------------------------------------------------------------------------\n\nfunction toPackItem(result: MemoryResult, dedupeState?: DedupeState): PackItem {\n\tconst dupes = dedupeState?.duplicateIds.get(result.id);\n\tconst item: PackItem = {\n\t\tid: result.id,\n\t\tkind: result.kind,\n\t\ttitle: result.title,\n\t\tbody: result.body_text,\n\t\tconfidence: result.confidence,\n\t\ttags: result.tags_text,\n\t\tmetadata: result.metadata,\n\t};\n\tif (dupes && dupes.size > 0) {\n\t\titem.support_count = 1 + dupes.size;\n\t\titem.duplicate_ids = [...dupes].sort((a, b) => a - b);\n\t}\n\treturn item;\n}\n\n// ---------------------------------------------------------------------------\n// Exact dedup (ports Python's _collapse_exact_duplicates)\n// ---------------------------------------------------------------------------\n\n/** Normalize text for dedup comparison: lowercase, trim, collapse whitespace. */\nfunction normalizeDedupe(text: string): string {\n\treturn text.trim().toLowerCase().split(/\\s+/).join(\" \");\n}\n\n/**\n * Build a collision-free dedup key for non-summary items.\n * Uses length-prefixed fields so pipe characters in content can't\n * cause collisions between distinct (kind, title, body) tuples.\n */\nfunction exactDedupeKey(item: MemoryResult): string | null {\n\tif (item.kind === \"session_summary\") return null;\n\tconst title = normalizeDedupe(item.title);\n\tconst body = normalizeDedupe(item.body_text);\n\tif (!title && !body) return null;\n\treturn `${item.kind.length}:${item.kind}|${title.length}:${title}|${body.length}:${body}`;\n}\n\ninterface DedupeState {\n\tcanonicalByKey: Map<string, number>;\n\tduplicateIds: Map<number, Set<number>>;\n}\n\n/**\n * Collapse exact duplicates: same kind+title+body → keep first (canonical).\n * Tracks duplicate IDs so support_count can report how many were collapsed.\n */\nfunction collapseExactDuplicates(items: MemoryResult[], state: DedupeState): MemoryResult[] {\n\tconst collapsed: MemoryResult[] = [];\n\tfor (const item of items) {\n\t\tconst key = exactDedupeKey(item);\n\t\tif (key === null) {\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tconst canonicalId = state.canonicalByKey.get(key);\n\t\tif (canonicalId === undefined) {\n\t\t\tstate.canonicalByKey.set(key, item.id);\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\tif (canonicalId === item.id) {\n\t\t\tcollapsed.push(item);\n\t\t\tcontinue;\n\t\t}\n\t\t// Track as duplicate of the canonical\n\t\tconst existing = state.duplicateIds.get(canonicalId);\n\t\tif (existing) existing.add(item.id);\n\t\telse state.duplicateIds.set(canonicalId, new Set([item.id]));\n\t}\n\treturn collapsed;\n}\n\n// ---------------------------------------------------------------------------\n// Tag-overlap sorting (ports Python's _sort_by_tag_overlap)\n// ---------------------------------------------------------------------------\n\n/** Sort items by tag overlap with the query, then by recency. */\nfunction sortByTagOverlap(items: MemoryResult[], query: string): MemoryResult[] {\n\tconst queryTokens = new Set((query.toLowerCase().match(/[a-z0-9_]+/g) ?? []).filter(Boolean));\n\tif (queryTokens.size === 0) return items;\n\n\treturn [...items].sort((a, b) => {\n\t\tconst aOverlap = countOverlap(a.tags_text, queryTokens);\n\t\tconst bOverlap = countOverlap(b.tags_text, queryTokens);\n\t\tif (bOverlap !== aOverlap) return bOverlap - aOverlap;\n\t\t// Tiebreak by recency (newest first)\n\t\treturn (b.created_at ?? \"\").localeCompare(a.created_at ?? \"\");\n\t});\n}\n\nfunction countOverlap(tags: string, tokens: Set<string>): number {\n\tconst tagSet = new Set(tags.split(/\\s+/).filter(Boolean));\n\tlet count = 0;\n\tfor (const t of tokens) {\n\t\tif (tagSet.has(t)) count++;\n\t}\n\treturn count;\n}\n\nfunction queryLooksLikeTasks(query: string): boolean {\n\tconst lowered = query.toLowerCase();\n\tfor (const token of [\n\t\t\"todo\",\n\t\t\"todos\",\n\t\t\"pending\",\n\t\t\"task\",\n\t\t\"tasks\",\n\t\t\"next\",\n\t\t\"resume\",\n\t\t\"continue\",\n\t\t\"backlog\",\n\t]) {\n\t\tif (lowered.includes(token)) return true;\n\t}\n\tfor (const phrase of [\n\t\t\"follow up\",\n\t\t\"follow-up\",\n\t\t\"followups\",\n\t\t\"pick up\",\n\t\t\"pick-up\",\n\t\t\"left off\",\n\t\t\"where we left off\",\n\t\t\"work on next\",\n\t\t\"what's next\",\n\t\t\"what was next\",\n\t]) {\n\t\tif (lowered.includes(phrase)) return true;\n\t}\n\treturn false;\n}\n\nfunction queryLooksLikeRecall(query: string): boolean {\n\tconst lowered = query.toLowerCase();\n\tfor (const token of [\"remember\", \"remind\", \"recall\", \"recap\", \"summary\", \"summarize\"]) {\n\t\tif (lowered.includes(token)) return true;\n\t}\n\tfor (const phrase of [\n\t\t\"what did we do\",\n\t\t\"what did we work on\",\n\t\t\"what did we decide\",\n\t\t\"what happened\",\n\t\t\"last time\",\n\t\t\"previous session\",\n\t\t\"previous work\",\n\t\t\"where were we\",\n\t\t\"catch me up\",\n\t\t\"catch up\",\n\t]) {\n\t\tif (lowered.includes(phrase)) return true;\n\t}\n\treturn false;\n}\n\nfunction toMemoryResult(row: MemoryItemResponse | TimelineItemResponse): MemoryResult {\n\treturn {\n\t\tid: row.id,\n\t\tkind: row.kind,\n\t\ttitle: row.title,\n\t\tbody_text: row.body_text,\n\t\tconfidence: row.confidence ?? 0,\n\t\tcreated_at: row.created_at,\n\t\tupdated_at: row.updated_at,\n\t\ttags_text: row.tags_text ?? \"\",\n\t\tscore: 0,\n\t\tsession_id: row.session_id,\n\t\tmetadata: row.metadata_json,\n\t};\n}\n\nfunction parseCreatedAt(value: string): number {\n\tconst parsed = Date.parse(value);\n\tif (Number.isNaN(parsed)) return Number.NEGATIVE_INFINITY;\n\treturn parsed;\n}\n\nfunction filterRecentResults(results: MemoryResult[], days: number): MemoryResult[] {\n\tconst cutoff = Date.now() - days * 86_400_000;\n\treturn results.filter((item) => parseCreatedAt(item.created_at) >= cutoff);\n}\n\nfunction prioritizeTaskResults(results: MemoryResult[], limit: number): MemoryResult[] {\n\tconst ordered = [...results].sort((a, b) =>\n\t\t(b.created_at ?? \"\").localeCompare(a.created_at ?? \"\"),\n\t);\n\tordered.sort((a, b) => {\n\t\tconst rank = (kind: string): number => {\n\t\t\tif (kind === \"note\") return 0;\n\t\t\tif (kind === \"decision\") return 1;\n\t\t\tif (kind === \"observation\") return 2;\n\t\t\treturn 3;\n\t\t};\n\t\treturn rank(a.kind) - rank(b.kind);\n\t});\n\treturn ordered.slice(0, limit);\n}\n\nfunction prioritizeRecallResults(results: MemoryResult[], limit: number): MemoryResult[] {\n\tconst ordered = [...results].sort((a, b) =>\n\t\t(b.created_at ?? \"\").localeCompare(a.created_at ?? \"\"),\n\t);\n\tordered.sort((a, b) => {\n\t\tconst rank = (kind: string): number => {\n\t\t\tif (kind === \"session_summary\") return 0;\n\t\t\tif (kind === \"decision\") return 1;\n\t\t\tif (kind === \"note\") return 2;\n\t\t\tif (kind === \"observation\") return 3;\n\t\t\tif (kind === \"entities\") return 4;\n\t\t\treturn 5;\n\t\t};\n\t\treturn rank(a.kind) - rank(b.kind);\n\t});\n\treturn ordered.slice(0, limit);\n}\n\nfunction taskFallbackRecent(\n\tstore: StoreHandle,\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst expandedLimit = Math.max(limit * 3, limit);\n\tconst recentRows = store.recent(expandedLimit, filters ?? null);\n\treturn prioritizeTaskResults(recentRows.map(toMemoryResult), limit);\n}\n\nfunction recallFallbackRecent(\n\tstore: StoreHandle,\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): MemoryResult[] {\n\tconst summaryFilters = { ...(filters ?? {}), kind: \"session_summary\" };\n\tconst summaries = store.recent(limit, summaryFilters).map(toMemoryResult);\n\tif (summaries.length >= limit) return summaries.slice(0, limit);\n\n\tconst expandedLimit = Math.max(limit * 3, limit);\n\tconst recentAll = store.recent(expandedLimit, filters ?? null).map(toMemoryResult);\n\tconst summaryIds = new Set(summaries.map((item) => item.id));\n\tconst remainder = recentAll.filter((item) => !summaryIds.has(item.id));\n\tconst prioritized = prioritizeTaskResults(remainder, limit - summaries.length);\n\treturn [...summaries, ...prioritized];\n}\nfunction parseNonNegativeInt(value: unknown): number | null {\n\tif (value == null || typeof value === \"boolean\") return null;\n\tconst parsed = Number(value);\n\tif (!Number.isFinite(parsed)) return null;\n\tconst intValue = Math.trunc(parsed);\n\tif (intValue < 0) return null;\n\treturn intValue;\n}\n\nfunction dedupePositiveIds(values: unknown[]): number[] {\n\tconst deduped: number[] = [];\n\tconst seen = new Set<number>();\n\tfor (const raw of values) {\n\t\tconst parsed = parseNonNegativeInt(raw);\n\t\tif (parsed == null || parsed <= 0 || seen.has(parsed)) continue;\n\t\tseen.add(parsed);\n\t\tdeduped.push(parsed);\n\t}\n\treturn deduped;\n}\n\nfunction coercePackItemIds(value: unknown): { ids: number[]; valid: boolean } {\n\tif (!Array.isArray(value)) return { ids: [], valid: false };\n\tfor (const raw of value) {\n\t\tif (raw == null || typeof raw === \"boolean\") return { ids: [], valid: false };\n\t}\n\treturn { ids: dedupePositiveIds(value), valid: true };\n}\n\nfunction parseMetadataObject(value: unknown): Record<string, unknown> {\n\tif (!value) return {};\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(value) as unknown;\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\treturn parsed as Record<string, unknown>;\n\t\t\t}\n\t\t\treturn {};\n\t\t} catch {\n\t\t\treturn {};\n\t\t}\n\t}\n\tif (typeof value === \"object\" && !Array.isArray(value)) {\n\t\treturn value as Record<string, unknown>;\n\t}\n\treturn {};\n}\n\nfunction getPackDeltaBaseline(\n\tstore: StoreHandle,\n\tproject: string | null,\n): { previousPackIds: number[] | null; previousPackTokens: number | null } {\n\tconst projectBase = project ? projectBasename(project) : null;\n\tconst metaProjectExpr =\n\t\t\"CASE WHEN json_valid(metadata_json) = 1 THEN json_extract(metadata_json, '$.project') ELSE NULL END\";\n\tconst rows = project\n\t\t? (store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT metadata_json, tokens_read\n\t\t\t\t\t FROM usage_events\n\t\t\t\t\t WHERE event = 'pack'\n\t\t\t\t\t AND (${metaProjectExpr} = ? OR ${metaProjectExpr} = ?)\n\t\t\t\t\t ORDER BY created_at DESC\n\t\t\t\t\t LIMIT 25`,\n\t\t\t\t)\n\t\t\t\t.all(project, projectBase ?? project) as Array<{\n\t\t\t\tmetadata_json: string | null;\n\t\t\t\ttokens_read: number | null;\n\t\t\t}>)\n\t\t: (store.db\n\t\t\t\t.prepare(\n\t\t\t\t\t`SELECT metadata_json, tokens_read\n\t\t\t\t\t FROM usage_events\n\t\t\t\t\t WHERE event = 'pack'\n\t\t\t\t\t ORDER BY created_at DESC\n\t\t\t\t\t LIMIT 25`,\n\t\t\t\t)\n\t\t\t\t.all() as Array<{ metadata_json: string | null; tokens_read: number | null }>);\n\n\tfor (const row of rows) {\n\t\tconst metadata = parseMetadataObject(row.metadata_json);\n\t\tif (project != null) {\n\t\t\tconst rowProject = typeof metadata.project === \"string\" ? metadata.project : null;\n\t\t\tif (rowProject !== project && rowProject !== projectBase) continue;\n\t\t}\n\t\tif (!(\"pack_item_ids\" in metadata)) continue;\n\n\t\tconst { ids, valid } = coercePackItemIds(metadata.pack_item_ids);\n\t\tif (!valid) continue;\n\n\t\tconst previousTokens =\n\t\t\tparseNonNegativeInt(metadata.pack_tokens) ?? parseNonNegativeInt(row.tokens_read);\n\t\tif (previousTokens == null) continue;\n\n\t\treturn { previousPackIds: ids, previousPackTokens: previousTokens };\n\t}\n\n\treturn { previousPackIds: null, previousPackTokens: null };\n}\n\nfunction resolveUsageSessionId(store: StoreHandle, project: string | null): number | null {\n\tif (!project) return null;\n\tconst projectBase = projectBasename(project);\n\tconst row = store.db\n\t\t.prepare(\n\t\t\t`SELECT id\n\t\t\t FROM sessions\n\t\t\t WHERE project = ? OR project = ?\n\t\t\t ORDER BY started_at DESC, id DESC\n\t\t\t LIMIT 1`,\n\t\t)\n\t\t.get(project, projectBase) as { id: number } | undefined;\n\treturn row?.id ?? null;\n}\n\nfunction estimateWorkTokens(item: MemoryResult): number {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst known = parseNonNegativeInt(metadata.discovery_tokens);\n\tif (known != null) return known;\n\treturn Math.max(2000, estimateTokens(`${item.title} ${item.body_text}`.trim()));\n}\n\nfunction discoveryGroup(item: MemoryResult): string {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst group = metadata.discovery_group;\n\tif (typeof group === \"string\" && group.trim().length > 0) return group.trim();\n\treturn `memory:${item.id}`;\n}\n\nfunction avoidedWorkTokens(item: MemoryResult): { tokens: number; source: string } {\n\tconst metadata = parseMetadataObject(item.metadata);\n\tconst tokens = parseNonNegativeInt(metadata.discovery_tokens);\n\tif (tokens != null && tokens > 0) {\n\t\tconst source =\n\t\t\ttypeof metadata.discovery_source === \"string\" && metadata.discovery_source\n\t\t\t\t? metadata.discovery_source\n\t\t\t\t: \"known\";\n\t\treturn { tokens, source };\n\t}\n\treturn { tokens: 0, source: \"unknown\" };\n}\n\nfunction workSource(item: MemoryResult): \"usage\" | \"estimate\" {\n\tconst metadata = parseMetadataObject(item.metadata);\n\treturn metadata.discovery_source === \"usage\" ? \"usage\" : \"estimate\";\n}\n\nfunction recordPackUsage(store: StoreHandle, metrics: Record<string, unknown>): void {\n\tconst now = new Date().toISOString();\n\tconst tokensRead = parseNonNegativeInt(metrics.pack_tokens) ?? 0;\n\tconst tokensSaved = parseNonNegativeInt(metrics.tokens_saved) ?? 0;\n\tconst project = typeof metrics.project === \"string\" ? metrics.project : null;\n\tconst sessionId = resolveUsageSessionId(store, project);\n\ttry {\n\t\tstore.db\n\t\t\t.prepare(\n\t\t\t\t`INSERT INTO usage_events(session_id, event, tokens_read, tokens_written, tokens_saved, created_at, metadata_json)\n\t\t\t\t VALUES (?, 'pack', ?, 0, ?, ?, ?)`,\n\t\t\t)\n\t\t\t.run(sessionId, tokensRead, tokensSaved, now, JSON.stringify(metrics));\n\t} catch {\n\t\t// Non-fatal for pack building path\n\t}\n}\n// ---------------------------------------------------------------------------\n// buildMemoryPack\n// ---------------------------------------------------------------------------\n\n/**\n * Build a memory pack: a formatted, categorized summary of memories\n * matching a given context string.\n *\n * Flow:\n * 1. Search for memories matching `context`\n * 2. Separate into summary / timeline / observations sections\n * 3. Fall back to recent() if search returns nothing\n * 4. Apply token budget (truncate items if budget exceeded)\n * 5. Format sections into pack_text\n */\n/**\n * Merge FTS and semantic results by ID, keeping the higher score for dupes.\n * Matches Python's always-merge behavior when semantic results are available.\n */\nfunction mergeResults(\n\tstore: StoreHandle,\n\tftsResults: MemoryResult[],\n\tsemanticResults: MemoryResult[],\n\tlimit: number,\n\tfilters?: MemoryFilters,\n): { merged: MemoryResult[]; ftsCount: number; semanticCount: number } {\n\tconst seen = new Map<number, MemoryResult>();\n\tfor (const r of ftsResults) {\n\t\tconst existing = seen.get(r.id);\n\t\tif (!existing || r.score > existing.score) seen.set(r.id, r);\n\t}\n\tlet semanticCount = 0;\n\tfor (const r of semanticResults) {\n\t\tif (!seen.has(r.id)) semanticCount++;\n\t\tconst existing = seen.get(r.id);\n\t\tif (!existing || r.score > existing.score) seen.set(r.id, r);\n\t}\n\tconst merged = rerankResults(store, [...seen.values()], limit, filters);\n\treturn { merged, ftsCount: ftsResults.length, semanticCount };\n}\n\nexport function buildMemoryPack(\n\tstore: StoreHandle,\n\tcontext: string,\n\tlimit = 10,\n\ttokenBudget: number | null = null,\n\tfilters?: MemoryFilters,\n\tsemanticResults?: MemoryResult[],\n): PackResponse {\n\tconst effectiveLimit = Math.max(1, Math.trunc(limit));\n\tlet fallbackUsed = false;\n\tlet ftsCount = 0;\n\tlet semanticCount = 0;\n\tlet results: MemoryResult[];\n\tconst taskMode = queryLooksLikeTasks(context);\n\tconst recallMode = !taskMode && queryLooksLikeRecall(context);\n\n\tif (taskMode) {\n\t\tconst taskQuery = `${context} ${TASK_HINT_QUERY}`.trim();\n\t\tlet taskResults = search(store, taskQuery, effectiveLimit, filters);\n\t\tftsCount = taskResults.length;\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, taskResults, semanticResults, effectiveLimit, filters);\n\t\t\ttaskResults = merge.merged;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t}\n\t\tif (taskResults.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = taskFallbackRecent(store, effectiveLimit, filters);\n\t\t} else {\n\t\t\tconst recentTaskResults = filterRecentResults(taskResults, TASK_RECENCY_DAYS);\n\t\t\tresults = prioritizeTaskResults(\n\t\t\t\trecentTaskResults.length > 0 ? recentTaskResults : taskResults,\n\t\t\t\teffectiveLimit,\n\t\t\t);\n\t\t}\n\t} else if (recallMode) {\n\t\tconst recallQuery = context.trim().length > 0 ? context : RECALL_HINT_QUERY;\n\t\tlet recallResults = search(store, recallQuery, effectiveLimit, filters);\n\t\tftsCount = recallResults.length;\n\t\tif (recallResults.length === 0) {\n\t\t\tconst recallFilters = { ...(filters ?? {}), kind: \"session_summary\" };\n\t\t\trecallResults = search(store, RECALL_HINT_QUERY, effectiveLimit, recallFilters);\n\t\t\tftsCount = recallResults.length;\n\t\t}\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, recallResults, semanticResults, effectiveLimit, filters);\n\t\t\trecallResults = merge.merged;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t}\n\t\tresults = prioritizeRecallResults(recallResults, effectiveLimit);\n\t\tif (results.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = recallFallbackRecent(store, effectiveLimit, filters);\n\t\t}\n\t\tconst anchorId = results[0]?.id;\n\t\tif (anchorId != null) {\n\t\t\tconst depthBefore = Math.max(0, Math.floor(effectiveLimit / 2));\n\t\t\tconst depthAfter = Math.max(0, effectiveLimit - depthBefore - 1);\n\t\t\tconst timelineRows = timeline(\n\t\t\t\tstore,\n\t\t\t\tundefined,\n\t\t\t\tanchorId,\n\t\t\t\tdepthBefore,\n\t\t\t\tdepthAfter,\n\t\t\t\tfilters ?? null,\n\t\t\t);\n\t\t\tif (timelineRows.length > 0) {\n\t\t\t\tresults = timelineRows.map(toMemoryResult);\n\t\t\t}\n\t\t}\n\t} else {\n\t\tconst ftsResults = search(store, context, effectiveLimit, filters);\n\t\tif (semanticResults && semanticResults.length > 0) {\n\t\t\tconst merge = mergeResults(store, ftsResults, semanticResults, effectiveLimit, filters);\n\t\t\tresults = merge.merged;\n\t\t\tftsCount = merge.ftsCount;\n\t\t\tsemanticCount = merge.semanticCount;\n\t\t} else {\n\t\t\tresults = ftsResults;\n\t\t\tftsCount = results.length;\n\t\t}\n\t\tif (results.length === 0) {\n\t\t\tfallbackUsed = true;\n\t\t\tresults = store.recent(effectiveLimit, filters ?? null).map(toMemoryResult);\n\t\t}\n\t}\n\n\t// Step 2: categorize results\n\n\t// Summary: prefer search match, fall back to most recent session_summary\n\tlet summaryItems = results.filter((r) => r.kind === \"session_summary\").slice(0, 1);\n\tif (summaryItems.length === 0) {\n\t\tconst recentSummary = store.recent(1, { ...(filters ?? {}), kind: \"session_summary\" });\n\t\tif (recentSummary.length > 0) {\n\t\t\tconst s = recentSummary[0]!;\n\t\t\tsummaryItems = [\n\t\t\t\t{\n\t\t\t\t\tid: s.id,\n\t\t\t\t\tkind: s.kind,\n\t\t\t\t\ttitle: s.title,\n\t\t\t\t\tbody_text: s.body_text,\n\t\t\t\t\tconfidence: s.confidence ?? 0,\n\t\t\t\t\tcreated_at: s.created_at,\n\t\t\t\t\tupdated_at: s.updated_at,\n\t\t\t\t\ttags_text: s.tags_text ?? \"\",\n\t\t\t\t\tscore: 0,\n\t\t\t\t\tsession_id: s.session_id,\n\t\t\t\t\tmetadata: s.metadata_json,\n\t\t\t\t},\n\t\t\t];\n\t\t}\n\t}\n\n\tlet timelineItems = results.filter((r) => r.kind !== \"session_summary\").slice(0, 3);\n\tconst timelineIds = new Set(timelineItems.map((r) => r.id));\n\n\t// Observations: from search results, then fall back to recent by observation kinds\n\tconst OBSERVATION_KINDS = Object.keys(OBSERVATION_KIND_PRIORITY);\n\tlet observationItems = [...results]\n\t\t.filter((r) => r.kind !== \"session_summary\" && !timelineIds.has(r.id))\n\t\t.sort((a, b) => {\n\t\t\tconst pa = OBSERVATION_KIND_PRIORITY[a.kind] ?? 99;\n\t\t\tconst pb = OBSERVATION_KIND_PRIORITY[b.kind] ?? 99;\n\t\t\treturn pa - pb;\n\t\t});\n\n\tif (recallMode && observationItems.length === 0) {\n\t\tobservationItems = results.filter((r) => r.kind !== \"session_summary\");\n\t}\n\n\tif (observationItems.length === 0) {\n\t\tconst recentObs = store.recentByKinds(\n\t\t\tOBSERVATION_KINDS,\n\t\t\tMath.max(effectiveLimit * 3, 10),\n\t\t\tfilters ?? null,\n\t\t);\n\t\tobservationItems = recentObs.map((row) => ({\n\t\t\tid: row.id,\n\t\t\tkind: row.kind,\n\t\t\ttitle: row.title,\n\t\t\tbody_text: row.body_text,\n\t\t\tconfidence: row.confidence ?? 0,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\ttags_text: row.tags_text ?? \"\",\n\t\t\tscore: 0,\n\t\t\tsession_id: row.session_id,\n\t\t\tmetadata: row.metadata_json,\n\t\t}));\n\t}\n\n\tif (observationItems.length === 0) {\n\t\tobservationItems = [...timelineItems];\n\t}\n\n\t// Sort observations by tag overlap with context, then by kind priority\n\tobservationItems = sortByTagOverlap(observationItems, context);\n\n\t// Exact dedup across all sections\n\tconst dedupeState: DedupeState = {\n\t\tcanonicalByKey: new Map(),\n\t\tduplicateIds: new Map(),\n\t};\n\tsummaryItems = collapseExactDuplicates(summaryItems, dedupeState);\n\ttimelineItems = collapseExactDuplicates(timelineItems, dedupeState);\n\tobservationItems = collapseExactDuplicates(observationItems, dedupeState);\n\n\t// Step 4: apply token budget\n\tlet budgetedSummary = summaryItems;\n\tlet budgetedTimeline = timelineItems;\n\tlet budgetedObservations = observationItems;\n\n\tif (tokenBudget != null && tokenBudget > 0) {\n\t\tlet tokensUsed = 0;\n\n\t\tbudgetedSummary = [];\n\t\tfor (const item of summaryItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedSummary.push(item);\n\t\t}\n\n\t\tbudgetedTimeline = [];\n\t\tfor (const item of timelineItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedTimeline.push(item);\n\t\t}\n\n\t\tbudgetedObservations = [];\n\t\tfor (const item of observationItems) {\n\t\t\tconst cost = estimateTokens(formatItem(item));\n\t\t\tif (tokensUsed + cost > tokenBudget) break;\n\t\t\ttokensUsed += cost;\n\t\t\tbudgetedObservations.push(item);\n\t\t}\n\t}\n\n\t// Step 5: format sections\n\tconst sections = [\n\t\tformatSection(\"Summary\", budgetedSummary),\n\t\tformatSection(\"Timeline\", budgetedTimeline),\n\t\tformatSection(\"Observations\", budgetedObservations),\n\t];\n\n\tconst packText = sections.join(\"\\n\\n\");\n\tconst packTokens = estimateTokens(packText);\n\n\t// Collect all unique items across sections\n\tconst seenIds = new Set<number>();\n\tconst allItems: PackItem[] = [];\n\tconst selectedItems: MemoryResult[] = [];\n\tconst allItemIds: number[] = [];\n\tfor (const item of [...budgetedSummary, ...budgetedTimeline, ...budgetedObservations]) {\n\t\tif (seenIds.has(item.id)) continue;\n\t\tseenIds.add(item.id);\n\t\tselectedItems.push(item);\n\t\tallItems.push(toPackItem(item, dedupeState));\n\t\tallItemIds.push(item.id);\n\t}\n\n\tconst { previousPackIds, previousPackTokens } = getPackDeltaBaseline(\n\t\tstore,\n\t\tfilters?.project ?? null,\n\t);\n\tconst packDeltaAvailable = previousPackIds != null && previousPackTokens != null;\n\tconst previousSet = new Set(previousPackIds ?? []);\n\tconst currentSet = new Set(allItemIds);\n\tconst addedIds = packDeltaAvailable ? allItemIds.filter((id) => !previousSet.has(id)) : [];\n\tconst removedIds = packDeltaAvailable\n\t\t? (previousPackIds ?? []).filter((id) => !currentSet.has(id))\n\t\t: [];\n\tconst retainedIds = packDeltaAvailable ? allItemIds.filter((id) => previousSet.has(id)) : [];\n\tconst packTokenDelta = packDeltaAvailable ? packTokens - (previousPackTokens ?? 0) : 0;\n\n\tconst workTokens = selectedItems.reduce((sum, item) => sum + estimateWorkTokens(item), 0);\n\tconst groupedWork = new Map<string, number>();\n\tfor (const item of selectedItems) {\n\t\tconst key = discoveryGroup(item);\n\t\tconst estimate = estimateWorkTokens(item);\n\t\tconst existing = groupedWork.get(key) ?? 0;\n\t\tif (estimate > existing) groupedWork.set(key, estimate);\n\t}\n\tconst workTokensUnique = [...groupedWork.values()].reduce((sum, value) => sum + value, 0);\n\tconst tokensSaved = Math.max(0, workTokensUnique - packTokens);\n\n\tlet avoidedWorkTokensTotal = 0;\n\tlet avoidedKnownItems = 0;\n\tlet avoidedUnknownItems = 0;\n\tconst avoidedWorkSources: Record<string, number> = {};\n\tfor (const item of selectedItems) {\n\t\tconst avoided = avoidedWorkTokens(item);\n\t\tif (avoided.tokens > 0) {\n\t\t\tavoidedWorkTokensTotal += avoided.tokens;\n\t\t\tavoidedKnownItems += 1;\n\t\t\tavoidedWorkSources[avoided.source] = (avoidedWorkSources[avoided.source] ?? 0) + 1;\n\t\t} else {\n\t\t\tavoidedUnknownItems += 1;\n\t\t}\n\t}\n\tconst avoidedWorkSaved = Math.max(0, avoidedWorkTokensTotal - packTokens);\n\tconst avoidedWorkRatio =\n\t\tavoidedWorkTokensTotal > 0 ? avoidedWorkTokensTotal / Math.max(packTokens, 1) : null;\n\n\tconst workSources = selectedItems.map(workSource);\n\tconst workUsageItems = workSources.filter((source) => source === \"usage\").length;\n\tconst workEstimateItems = workSources.length - workUsageItems;\n\tconst workSourceLabel: \"estimate\" | \"usage\" | \"mixed\" =\n\t\tworkUsageItems > 0 && workEstimateItems > 0\n\t\t\t? \"mixed\"\n\t\t\t: workUsageItems > 0\n\t\t\t\t? \"usage\"\n\t\t\t\t: \"estimate\";\n\n\tconst compressionRatio = workTokensUnique > 0 ? packTokens / workTokensUnique : null;\n\tconst overheadTokens = workTokensUnique > 0 ? packTokens - workTokensUnique : null;\n\tconst fallbackLabel: \"recent\" | null = fallbackUsed ? \"recent\" : null;\n\tconst modeLabel: \"default\" | \"task\" | \"recall\" = taskMode\n\t\t? \"task\"\n\t\t: recallMode\n\t\t\t? \"recall\"\n\t\t\t: \"default\";\n\n\tconst metrics = {\n\t\ttotal_items: allItems.length,\n\t\tpack_tokens: packTokens,\n\t\tfallback_used: fallbackUsed,\n\t\tfallback: fallbackLabel,\n\t\tlimit: effectiveLimit,\n\t\ttoken_budget: tokenBudget,\n\t\tproject: filters?.project ?? null,\n\t\tpack_item_ids: allItemIds,\n\t\tmode: modeLabel,\n\t\tadded_ids: addedIds,\n\t\tremoved_ids: removedIds,\n\t\tretained_ids: retainedIds,\n\t\tpack_token_delta: packTokenDelta,\n\t\tpack_delta_available: packDeltaAvailable,\n\t\twork_tokens: workTokens,\n\t\twork_tokens_unique: workTokensUnique,\n\t\ttokens_saved: tokensSaved,\n\t\tcompression_ratio: compressionRatio,\n\t\toverhead_tokens: overheadTokens,\n\t\tavoided_work_tokens: avoidedWorkTokensTotal,\n\t\tavoided_work_saved: avoidedWorkSaved,\n\t\tavoided_work_ratio: avoidedWorkRatio,\n\t\tavoided_work_known_items: avoidedKnownItems,\n\t\tavoided_work_unknown_items: avoidedUnknownItems,\n\t\tavoided_work_sources: avoidedWorkSources,\n\t\twork_source: workSourceLabel,\n\t\twork_usage_items: workUsageItems,\n\t\twork_estimate_items: workEstimateItems,\n\t\tsavings_reliable:\n\t\t\tavoidedKnownItems + avoidedUnknownItems > 0 ? avoidedKnownItems >= avoidedUnknownItems : true,\n\t\tsources: { fts: ftsCount, semantic: semanticCount, fuzzy: 0 },\n\t};\n\n\trecordPackUsage(store, metrics);\n\n\treturn {\n\t\tcontext,\n\t\titems: allItems,\n\t\titem_ids: allItemIds,\n\t\tpack_text: packText,\n\t\tmetrics,\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Async pack builder (with semantic search)\n// ---------------------------------------------------------------------------\n\n/**\n * Build a memory pack with semantic candidate merging.\n *\n * This is the async version that runs `semanticSearch` against the\n * sqlite-vec `memory_vectors` table, then merges those candidates\n * with FTS results via the sync `buildMemoryPack`.\n *\n * Callers that don't want/need async can still use the sync\n * `buildMemoryPack` directly — semantic candidates simply won't\n * be included.\n */\nexport async function buildMemoryPackAsync(\n\tstore: StoreHandle & { db: Database },\n\tcontext: string,\n\tlimit = 10,\n\ttokenBudget: number | null = null,\n\tfilters?: MemoryFilters,\n): Promise<PackResponse> {\n\t// Run semantic search (returns [] when embeddings unavailable)\n\tlet semResults: MemoryResult[] = [];\n\ttry {\n\t\tconst raw = await semanticSearch(store.db, context, limit, {\n\t\t\tproject: filters?.project,\n\t\t});\n\t\tsemResults = raw.map((r) => {\n\t\t\t// Parse metadata_json if present, matching FTS result shape\n\t\t\tlet metadata: Record<string, unknown> = {};\n\t\t\tif (r.metadata_json) {\n\t\t\t\ttry {\n\t\t\t\t\tconst parsed = JSON.parse(r.metadata_json) as unknown;\n\t\t\t\t\tif (parsed != null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n\t\t\t\t\t\tmetadata = parsed as Record<string, unknown>;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Invalid JSON metadata — use empty object\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {\n\t\t\t\tid: r.id,\n\t\t\t\tkind: r.kind,\n\t\t\t\ttitle: r.title,\n\t\t\t\tbody_text: r.body_text,\n\t\t\t\tconfidence: r.confidence,\n\t\t\t\tcreated_at: r.created_at,\n\t\t\t\tupdated_at: r.updated_at,\n\t\t\t\ttags_text: r.tags_text,\n\t\t\t\tscore: r.score,\n\t\t\t\tsession_id: r.session_id,\n\t\t\t\tmetadata,\n\t\t\t};\n\t\t});\n\t} catch {\n\t\t// Semantic search failure is non-fatal — fall through to FTS-only\n\t}\n\n\treturn buildMemoryPack(store, context, limit, tokenBudget, filters, semResults);\n}\n","/**\n * Raw event flush orchestration — bridge between the sweeper and the ingest pipeline.\n *\n * Ports the core flush logic from codemem/raw_event_flush.py.\n *\n * Reads unflushed raw events for a session, creates a batch record,\n * builds an IngestPayload, runs it through the ingest pipeline,\n * and updates flush state on success (or records failure details).\n */\n\nimport { type IngestOptions, ingest } from \"./ingest-pipeline.js\";\nimport type { IngestPayload, SessionContext } from \"./ingest-types.js\";\nimport { ObserverAuthError } from \"./observer-client.js\";\nimport type { MemoryStore } from \"./store.js\";\n\nconst EXTRACTOR_VERSION = \"raw_events_v1\";\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction truncateErrorMessage(message: string, limit = 280): string {\n\tconst text = message.replace(/\\s+/g, \" \").trim();\n\tif (text.length <= limit) return text;\n\treturn `${text.slice(0, limit - 3).trimEnd()}...`;\n}\n\nfunction providerDisplayName(provider: string | null | undefined): string {\n\tconst normalized = (provider ?? \"\").trim().toLowerCase();\n\tif (normalized === \"openai\") return \"OpenAI\";\n\tif (normalized === \"anthropic\") return \"Anthropic\";\n\tif (normalized) return normalized.charAt(0).toUpperCase() + normalized.slice(1);\n\treturn \"Observer\";\n}\n\nfunction summarizeFlushFailure(exc: Error, provider: string | null | undefined): string {\n\tconst providerTitle = providerDisplayName(provider);\n\tconst rawMessage = String(exc.message ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\n\tif (exc instanceof ObserverAuthError) {\n\t\treturn `${providerTitle} authentication failed. Refresh credentials and retry.`;\n\t}\n\tif (exc.name === \"TimeoutError\" || rawMessage.includes(\"timeout\")) {\n\t\treturn `${providerTitle} request timed out during raw-event processing.`;\n\t}\n\tif (\n\t\trawMessage === \"observer failed during raw-event flush\" ||\n\t\trawMessage === \"observer produced no storable output for raw-event flush\"\n\t) {\n\t\treturn `${providerTitle} returned no usable output for raw-event processing.`;\n\t}\n\tif (/parse|xml|json/i.test(rawMessage)) {\n\t\treturn `${providerTitle} response could not be processed.`;\n\t}\n\treturn `${providerTitle} processing failed during raw-event ingestion.`;\n}\n\n// ---------------------------------------------------------------------------\n// Session context builder\n// ---------------------------------------------------------------------------\n\n/**\n * Build session context from raw events — extracts prompt count, tool count,\n * duration, files modified/read, and first user prompt.\n *\n * Port of build_session_context() from raw_event_flush.py.\n */\nexport function buildSessionContext(events: Record<string, unknown>[]): SessionContext {\n\tlet promptCount = 0;\n\tlet toolCount = 0;\n\n\tfor (const e of events) {\n\t\tif (e.type === \"user_prompt\") promptCount++;\n\t\tif (e.type === \"tool.execute.after\") toolCount++;\n\t}\n\n\tconst tsValues: number[] = [];\n\tfor (const e of events) {\n\t\tconst ts = e.timestamp_wall_ms;\n\t\tif (ts == null) continue;\n\t\tconst num = Number(ts);\n\t\tif (!Number.isFinite(num)) continue;\n\t\ttsValues.push(num);\n\t}\n\tlet durationMs = 0;\n\tif (tsValues.length > 0) {\n\t\tlet minTs = tsValues[0]!;\n\t\tlet maxTs = tsValues[0]!;\n\t\tfor (let i = 1; i < tsValues.length; i++) {\n\t\t\tconst v = tsValues[i]!;\n\t\t\tif (v < minTs) minTs = v;\n\t\t\tif (v > maxTs) maxTs = v;\n\t\t}\n\t\tdurationMs = Math.max(0, maxTs - minTs);\n\t}\n\n\tconst filesModified = new Set<string>();\n\tconst filesRead = new Set<string>();\n\tfor (const e of events) {\n\t\tif (e.type !== \"tool.execute.after\") continue;\n\t\tconst tool = String(e.tool ?? \"\").toLowerCase();\n\t\tconst args = e.args;\n\t\tif (args == null || typeof args !== \"object\") continue;\n\t\tconst filePath =\n\t\t\t(args as Record<string, unknown>).filePath ?? (args as Record<string, unknown>).path;\n\t\tif (typeof filePath !== \"string\" || !filePath) continue;\n\t\tif (tool === \"write\" || tool === \"edit\") filesModified.add(filePath);\n\t\tif (tool === \"read\") filesRead.add(filePath);\n\t}\n\n\tlet firstPrompt: string | undefined;\n\tfor (const e of events) {\n\t\tif (e.type !== \"user_prompt\") continue;\n\t\tconst text = e.prompt_text;\n\t\tif (typeof text === \"string\" && text.trim()) {\n\t\t\tfirstPrompt = text.trim();\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn {\n\t\tfirstPrompt,\n\t\tpromptCount,\n\t\ttoolCount,\n\t\tdurationMs,\n\t\tfilesModified: [...filesModified].sort(),\n\t\tfilesRead: [...filesRead].sort(),\n\t};\n}\n\n// ---------------------------------------------------------------------------\n// Main flush function\n// ---------------------------------------------------------------------------\n\nexport interface FlushRawEventsOptions {\n\topencodeSessionId: string;\n\tsource?: string;\n\tcwd?: string | null;\n\tproject?: string | null;\n\tstartedAt?: string | null;\n\tmaxEvents?: number | null;\n\tallowEmptyFlush?: boolean;\n}\n\n/**\n * Flush raw events for a single session through the ingest pipeline.\n *\n * 1. Reads unflushed raw events from the store\n * 2. Creates/claims a flush batch for idempotency\n * 3. Builds session context and IngestPayload\n * 4. Calls ingest()\n * 5. Updates flush state on success; records failure details on error\n *\n * Port of flush_raw_events() from raw_event_flush.py.\n */\nexport async function flushRawEvents(\n\tstore: MemoryStore,\n\tingestOpts: IngestOptions,\n\topts: FlushRawEventsOptions,\n): Promise<{ flushed: number; updatedState: number }> {\n\tlet { source = \"opencode\", cwd, project, startedAt } = opts;\n\tconst { opencodeSessionId, maxEvents } = opts;\n\tconst allowEmptyFlush = opts.allowEmptyFlush ?? false;\n\n\tsource = (source ?? \"\").trim().toLowerCase() || \"opencode\";\n\n\t// Resolve session metadata for missing fields\n\tconst meta = store.rawEventSessionMeta(opencodeSessionId, source);\n\tif (cwd == null) cwd = (meta.cwd as string) ?? process.cwd();\n\tif (project == null) project = (meta.project as string) ?? null;\n\tif (startedAt == null) startedAt = (meta.started_at as string) ?? null;\n\n\t// Read unflushed events\n\tconst lastFlushed = store.rawEventFlushState(opencodeSessionId, source);\n\tconst events = store.rawEventsSinceBySeq(opencodeSessionId, source, lastFlushed, maxEvents);\n\tif (events.length === 0) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Extract event sequence range\n\tconst eventSeqs: number[] = [];\n\tfor (const e of events) {\n\t\tconst seq = e.event_seq;\n\t\tif (seq == null) continue;\n\t\tconst num = Number(seq);\n\t\tif (Number.isFinite(num)) eventSeqs.push(num);\n\t}\n\tif (eventSeqs.length === 0) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\tlet startEventSeq = eventSeqs[0]!;\n\tlet lastEventSeq = eventSeqs[0]!;\n\tfor (let i = 1; i < eventSeqs.length; i++) {\n\t\tconst v = eventSeqs[i]!;\n\t\tif (v < startEventSeq) startEventSeq = v;\n\t\tif (v > lastEventSeq) lastEventSeq = v;\n\t}\n\tif (lastEventSeq < startEventSeq) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Get or create flush batch (idempotency guard)\n\tconst { batchId, status } = store.getOrCreateRawEventFlushBatch(\n\t\topencodeSessionId,\n\t\tsource,\n\t\tstartEventSeq,\n\t\tlastEventSeq,\n\t\tEXTRACTOR_VERSION,\n\t);\n\n\t// If already completed, just advance flush state\n\tif (status === \"completed\") {\n\t\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\t\treturn { flushed: 0, updatedState: 1 };\n\t}\n\n\t// Claim the batch (atomic lock)\n\tif (!store.claimRawEventFlushBatch(batchId)) {\n\t\treturn { flushed: 0, updatedState: 0 };\n\t}\n\n\t// Build session context\n\tconst sessionContext: SessionContext = buildSessionContext(events);\n\tsessionContext.opencodeSessionId = opencodeSessionId;\n\tsessionContext.source = source;\n\tsessionContext.streamId = opencodeSessionId;\n\tsessionContext.flusher = \"raw_events\";\n\tsessionContext.flushBatch = {\n\t\tbatch_id: batchId,\n\t\tstart_event_seq: startEventSeq,\n\t\tend_event_seq: lastEventSeq,\n\t};\n\n\t// Build ingest payload\n\tconst payload: IngestPayload = {\n\t\tcwd: cwd ?? undefined,\n\t\tproject: project ?? undefined,\n\t\tstartedAt: startedAt ?? new Date().toISOString(),\n\t\tevents,\n\t\tsessionContext,\n\t};\n\n\t// Run ingest pipeline\n\ttry {\n\t\tawait ingest(payload, store, ingestOpts);\n\t} catch (exc) {\n\t\t// Record failure details on the batch\n\t\tconst err = exc instanceof Error ? exc : new Error(String(exc));\n\t\tif (\n\t\t\tallowEmptyFlush &&\n\t\t\terr.message === \"observer produced no storable output for raw-event flush\"\n\t\t) {\n\t\t\tstore.updateRawEventFlushBatchStatus(batchId, \"completed\");\n\t\t\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\t\t\treturn { flushed: events.length, updatedState: 1 };\n\t\t}\n\t\tconst provider = ingestOpts.observer?.getStatus?.()?.provider as string | undefined;\n\t\tconst message = truncateErrorMessage(summarizeFlushFailure(err, provider));\n\t\tstore.recordRawEventFlushBatchFailure(batchId, {\n\t\t\tmessage,\n\t\t\terrorType: err instanceof ObserverAuthError ? \"ObserverAuthError\" : err.name,\n\t\t\tobserverProvider: provider ?? null,\n\t\t\tobserverModel: (ingestOpts.observer?.getStatus?.()?.model as string) ?? null,\n\t\t\tobserverRuntime: null,\n\t\t});\n\t\tthrow exc;\n\t}\n\n\t// Success — mark batch completed and advance flush state\n\tstore.updateRawEventFlushBatchStatus(batchId, \"completed\");\n\tstore.updateRawEventFlushState(opencodeSessionId, lastEventSeq, source);\n\treturn { flushed: events.length, updatedState: 1 };\n}\n","/**\n * RawEventSweeper — periodically processes raw events into memories.\n *\n * Ports codemem/viewer_raw_events.py RawEventSweeper class.\n *\n * Uses setInterval (Node single-threaded) instead of Python threads.\n * The sweeper takes a shared MemoryStore and an IngestOptions provider.\n *\n * Each tick():\n * 1. Check if enabled\n * 2. Check auth backoff\n * 3. Purge old events (if retention configured)\n * 4. Mark stuck batches as error\n * 5. Flush sessions with pending queue entries\n * 6. Flush idle sessions with unflushed events\n * 7. Handle auth errors by setting backoff\n */\n\nimport type { IngestOptions } from \"./ingest-pipeline.js\";\nimport { ObserverAuthError } from \"./observer-client.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport { flushRawEvents } from \"./raw-event-flush.js\";\nimport type { MemoryStore } from \"./store.js\";\n\n/** Back off after an auth error. 60s gives OpenCode time to refresh its\n * OAuth token while staying longer than the default 30s sweep interval. */\nconst AUTH_BACKOFF_S = 60;\n\n// ---------------------------------------------------------------------------\n// Env helpers — read config from env vars matching Python exactly\n// ---------------------------------------------------------------------------\n\nfunction envInt(name: string, fallback: number): number {\n\tconst value = process.env[name];\n\tif (value == null) return fallback;\n\tconst parsed = Number.parseInt(value, 10);\n\treturn Number.isFinite(parsed) ? parsed : fallback;\n}\n\nfunction envBoolDisabled(name: string): boolean {\n\tconst value = (process.env[name] ?? \"1\").trim().toLowerCase();\n\treturn value === \"0\" || value === \"false\" || value === \"off\";\n}\n\n// ---------------------------------------------------------------------------\n// RawEventSweeper\n// ---------------------------------------------------------------------------\n\nexport class RawEventSweeper {\n\tprivate store: MemoryStore;\n\tprivate ingestOpts: IngestOptions;\n\tprivate active = false;\n\tprivate running = false; // reentrancy guard — prevents overlapping ticks\n\tprivate currentTick: Promise<void> | null = null;\n\tprivate wakeHandle: ReturnType<typeof setTimeout> | null = null;\n\tprivate loopHandle: ReturnType<typeof setTimeout> | null = null;\n\tprivate autoFlushTimers = new Map<string, ReturnType<typeof setTimeout>>();\n\tprivate sessionFlushing = new Set<string>();\n\tprivate autoFlushPending = new Set<string>();\n\tprivate autoFlushPromises = new Set<Promise<void>>();\n\tprivate authBackoffUntil = 0; // epoch seconds\n\tprivate authErrorLogged = false;\n\n\tconstructor(store: MemoryStore, ingestOpts: IngestOptions) {\n\t\tthis.store = store;\n\t\tthis.ingestOpts = ingestOpts;\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Config readers (from env vars, matching Python)\n\t// -----------------------------------------------------------------------\n\n\tprivate enabled(): boolean {\n\t\treturn !envBoolDisabled(\"CODEMEM_RAW_EVENTS_SWEEPER\");\n\t}\n\n\tprivate intervalMs(): number {\n\t\tconst envValue = process.env.CODEMEM_RAW_EVENTS_SWEEPER_INTERVAL_MS;\n\t\tif (envValue != null) {\n\t\t\tconst parsed = Number.parseInt(envValue, 10);\n\t\t\treturn Number.isFinite(parsed) ? Math.max(1000, parsed) : 30_000;\n\t\t}\n\t\tconst configValue = readCodememConfigFile().raw_events_sweeper_interval_s;\n\t\tconst configSeconds =\n\t\t\ttypeof configValue === \"number\"\n\t\t\t\t? configValue\n\t\t\t\t: typeof configValue === \"string\"\n\t\t\t\t\t? Number.parseInt(configValue, 10)\n\t\t\t\t\t: Number.NaN;\n\t\tif (Number.isFinite(configSeconds) && configSeconds > 0) {\n\t\t\treturn Math.max(1000, configSeconds * 1000);\n\t\t}\n\t\treturn 30_000;\n\t}\n\n\tprivate idleMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_SWEEPER_IDLE_MS\", 120_000);\n\t}\n\n\tprivate limit(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_SWEEPER_LIMIT\", 25);\n\t}\n\n\tprivate workerMaxEvents(): number | null {\n\t\tconst parsed = envInt(\"CODEMEM_RAW_EVENTS_WORKER_MAX_EVENTS\", 250);\n\t\treturn parsed <= 0 ? null : parsed;\n\t}\n\n\tprivate retentionMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_RETENTION_MS\", 0);\n\t}\n\n\tprivate autoFlushEnabled(): boolean {\n\t\treturn (process.env.CODEMEM_RAW_EVENTS_AUTO_FLUSH ?? \"\").trim() === \"1\";\n\t}\n\n\tprivate debounceMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_DEBOUNCE_MS\", 60_000);\n\t}\n\n\tprivate stuckBatchMs(): number {\n\t\treturn envInt(\"CODEMEM_RAW_EVENTS_STUCK_BATCH_MS\", 300_000);\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Auth backoff\n\t// -----------------------------------------------------------------------\n\n\tprivate handleAuthError(exc: ObserverAuthError): void {\n\t\tthis.authBackoffUntil = Date.now() / 1000 + AUTH_BACKOFF_S;\n\t\tif (!this.authErrorLogged) {\n\t\t\tthis.authErrorLogged = true;\n\t\t\tconst msg =\n\t\t\t\t`codemem: observer auth error — backing off for ${AUTH_BACKOFF_S}s. ` +\n\t\t\t\t`Refresh your provider credentials or update observer_provider in settings. ` +\n\t\t\t\t`(${exc.message})`;\n\t\t\tconsole.error(msg);\n\t\t}\n\t}\n\n\t/**\n\t * Reset the auth backoff and wake the worker.\n\t * Call this after credentials are refreshed.\n\t */\n\tresetAuthBackoff(): void {\n\t\tthis.authBackoffUntil = 0;\n\t\tthis.authErrorLogged = false;\n\t\tthis.wake();\n\t}\n\n\t/**\n\t * Return the current auth backoff status.\n\t */\n\tauthBackoffStatus(): { active: boolean; remainingS: number } {\n\t\tconst now = Date.now() / 1000;\n\t\tconst remaining = Math.max(0, Math.round(this.authBackoffUntil - now));\n\t\treturn { active: remaining > 0, remainingS: remaining };\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Lifecycle\n\t// -----------------------------------------------------------------------\n\n\t/**\n\t * Start the sweeper loop.\n\t * Uses a self-scheduling async loop (sleep → tick → sleep) to prevent\n\t * overlapping ticks. This mirrors the Python threading pattern where\n\t * the thread sleeps, runs tick() synchronously, then sleeps again.\n\t * No-op if sweeper is disabled or already running.\n\t */\n\tstart(): void {\n\t\tif (!this.enabled()) return;\n\t\tif (this.active) return;\n\t\tthis.active = true;\n\t\tthis.scheduleNext();\n\t}\n\n\t/**\n\t * Stop the sweeper. Cancels the next scheduled tick and waits for any\n\t * in-progress tick to finish before returning.\n\t */\n\tasync stop(): Promise<void> {\n\t\tthis.active = false;\n\t\tif (this.loopHandle != null) {\n\t\t\tclearTimeout(this.loopHandle);\n\t\t\tthis.loopHandle = null;\n\t\t}\n\t\tif (this.wakeHandle != null) {\n\t\t\tclearTimeout(this.wakeHandle);\n\t\t\tthis.wakeHandle = null;\n\t\t}\n\t\tfor (const timer of this.autoFlushTimers.values()) clearTimeout(timer);\n\t\tthis.autoFlushTimers.clear();\n\t\tif (this.currentTick != null) {\n\t\t\tawait this.currentTick;\n\t\t}\n\t\tif (this.autoFlushPromises.size > 0) {\n\t\t\tawait Promise.allSettled([...this.autoFlushPromises]);\n\t\t}\n\t}\n\n\t/**\n\t * Notify the sweeper that config changed.\n\t * Schedules an extra tick after a short delay.\n\t */\n\tnotifyConfigChanged(): void {\n\t\tthis.wake();\n\t}\n\n\t/**\n\t * Notify the debounced auto-flush path that new events arrived.\n\t * Mirrors Python's RawEventAutoFlusher.note_activity() behavior.\n\t */\n\tnudge(opencodeSessionId: string, source = \"opencode\"): void {\n\t\tif (!this.autoFlushEnabled()) return;\n\t\tif (Date.now() / 1000 < this.authBackoffUntil) return;\n\t\tconst streamId = opencodeSessionId.trim();\n\t\tif (!streamId) return;\n\t\tconst sourceNorm = source.trim().toLowerCase() || \"opencode\";\n\t\tconst key = `${sourceNorm}:${streamId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = this.debounceMs();\n\t\tif (delayMs <= 0) {\n\t\t\tthis.trackAutoFlush(this.flushNow(streamId, sourceNorm));\n\t\t\treturn;\n\t\t}\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) clearTimeout(existing);\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t\tthis.trackAutoFlush(this.flushNow(streamId, sourceNorm));\n\t\t}, delayMs);\n\t\tif (typeof timer === \"object\" && \"unref\" in timer) timer.unref();\n\t\tthis.autoFlushTimers.set(key, timer);\n\t}\n\n\tprivate trackAutoFlush(promise: Promise<void>): void {\n\t\tthis.autoFlushPromises.add(promise);\n\t\tvoid promise.finally(() => {\n\t\t\tthis.autoFlushPromises.delete(promise);\n\t\t});\n\t}\n\n\tprivate scheduleAutoFlush(opencodeSessionId: string, source: string): void {\n\t\tconst key = `${source}:${opencodeSessionId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tconst delayMs = this.debounceMs();\n\t\tif (delayMs <= 0) {\n\t\t\tthis.trackAutoFlush(this.flushNow(opencodeSessionId, source));\n\t\t\treturn;\n\t\t}\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) clearTimeout(existing);\n\t\tconst timer = setTimeout(() => {\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t\tthis.trackAutoFlush(this.flushNow(opencodeSessionId, source));\n\t\t}, delayMs);\n\t\tif (typeof timer === \"object\" && \"unref\" in timer) timer.unref();\n\t\tthis.autoFlushTimers.set(key, timer);\n\t}\n\n\tprivate async flushNow(opencodeSessionId: string, source: string): Promise<void> {\n\t\tif (Date.now() / 1000 < this.authBackoffUntil) return;\n\t\tconst key = `${source}:${opencodeSessionId}`;\n\t\tif (this.sessionFlushing.has(key)) {\n\t\t\tthis.autoFlushPending.add(key);\n\t\t\treturn;\n\t\t}\n\t\tthis.sessionFlushing.add(key);\n\t\tconst existing = this.autoFlushTimers.get(key);\n\t\tif (existing) {\n\t\t\tclearTimeout(existing);\n\t\t\tthis.autoFlushTimers.delete(key);\n\t\t}\n\t\ttry {\n\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\topencodeSessionId,\n\t\t\t\tsource,\n\t\t\t\tcwd: null,\n\t\t\t\tproject: null,\n\t\t\t\tstartedAt: null,\n\t\t\t\tmaxEvents: null,\n\t\t\t\tallowEmptyFlush: true,\n\t\t\t});\n\t\t} catch (exc) {\n\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconsole.error(\n\t\t\t\t`codemem: raw event auto flush failed for ${opencodeSessionId}:`,\n\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t);\n\t\t} finally {\n\t\t\tthis.sessionFlushing.delete(key);\n\t\t\tif (this.autoFlushPending.delete(key)) {\n\t\t\t\tthis.scheduleAutoFlush(opencodeSessionId, source);\n\t\t\t}\n\t\t}\n\t}\n\n\t/** Schedule the next tick after the configured interval. */\n\tprivate scheduleNext(): void {\n\t\tif (!this.active) return;\n\t\tthis.loopHandle = setTimeout(async () => {\n\t\t\tthis.loopHandle = null;\n\t\t\tawait this.runTick();\n\t\t\tthis.scheduleNext();\n\t\t}, this.intervalMs());\n\t\tif (typeof this.loopHandle === \"object\" && \"unref\" in this.loopHandle) {\n\t\t\tthis.loopHandle.unref();\n\t\t}\n\t}\n\n\t/** Execute a tick with reentrancy protection. */\n\tprivate async runTick(): Promise<void> {\n\t\tif (this.running) return;\n\t\tthis.running = true;\n\t\tthis.currentTick = (async () => {\n\t\t\ttry {\n\t\t\t\tawait this.tick();\n\t\t\t} catch (err) {\n\t\t\t\tconsole.error(\"codemem: sweeper tick failed unexpectedly:\", err);\n\t\t\t} finally {\n\t\t\t\tthis.running = false;\n\t\t\t\tthis.currentTick = null;\n\t\t\t}\n\t\t})();\n\t\tawait this.currentTick;\n\t}\n\n\tprivate wake(): void {\n\t\tif (!this.active) return;\n\t\t// Schedule a near-immediate extra tick (with reentrancy guard)\n\t\tif (this.wakeHandle != null) {\n\t\t\tclearTimeout(this.wakeHandle);\n\t\t}\n\t\tthis.wakeHandle = setTimeout(async () => {\n\t\t\tthis.wakeHandle = null;\n\t\t\tawait this.runTick();\n\t\t}, 100);\n\t\tif (typeof this.wakeHandle === \"object\" && \"unref\" in this.wakeHandle) {\n\t\t\tthis.wakeHandle.unref();\n\t\t}\n\t}\n\n\t// -----------------------------------------------------------------------\n\t// Tick — one sweep cycle\n\t// -----------------------------------------------------------------------\n\n\t/**\n\t * Execute one sweep cycle. Public for testing.\n\t *\n\t * 1. Check enabled / auth backoff\n\t * 2. Purge old events\n\t * 3. Mark stuck batches\n\t * 4. Flush pending queue sessions\n\t * 5. Flush idle sessions\n\t */\n\tasync tick(): Promise<void> {\n\t\tif (!this.enabled()) return;\n\n\t\t// Skip while backing off from auth error\n\t\tconst now = Date.now() / 1000;\n\t\tif (now < this.authBackoffUntil) return;\n\n\t\t// Backoff expired — reset so next auth error gets logged again\n\t\tif (this.authErrorLogged) {\n\t\t\tthis.authErrorLogged = false;\n\t\t}\n\n\t\tconst nowMs = Date.now();\n\t\tconst idleBefore = nowMs - this.idleMs();\n\n\t\t// Purge old events if retention configured\n\t\tconst retentionMs = this.retentionMs();\n\t\tif (retentionMs > 0) {\n\t\t\tthis.store.purgeRawEvents(retentionMs);\n\t\t}\n\n\t\t// Mark stuck batches as error\n\t\tconst stuckMs = this.stuckBatchMs();\n\t\tif (stuckMs > 0) {\n\t\t\tconst cutoff = new Date(nowMs - stuckMs).toISOString();\n\t\t\tthis.store.markStuckRawEventBatchesAsError(cutoff, 100);\n\t\t}\n\n\t\tconst maxEvents = this.workerMaxEvents();\n\t\tconst sessionLimit = this.limit();\n\t\tconst drained = new Set<string>();\n\n\t\t// Phase 1: Flush sessions with pending queue entries\n\t\tconst queueSessions = this.store.rawEventSessionsWithPendingQueue(sessionLimit);\n\t\tfor (const item of queueSessions) {\n\t\t\tconst { source, streamId } = item;\n\t\t\tif (!streamId) continue;\n\t\t\tconst key = `${source}:${streamId}`;\n\t\t\tif (this.sessionFlushing.has(key)) continue;\n\t\t\tthis.sessionFlushing.add(key);\n\n\t\t\ttry {\n\t\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\t\topencodeSessionId: streamId,\n\t\t\t\t\tsource,\n\t\t\t\t\tcwd: null,\n\t\t\t\t\tproject: null,\n\t\t\t\t\tstartedAt: null,\n\t\t\t\t\tmaxEvents,\n\t\t\t\t\tallowEmptyFlush: true,\n\t\t\t\t});\n\t\t\t\tdrained.add(`${source}:${streamId}`);\n\t\t\t} catch (exc) {\n\t\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\t\treturn; // Stop all flush work during auth backoff\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t`codemem: raw event queue worker flush failed for ${streamId}:`,\n\t\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis.sessionFlushing.delete(key);\n\t\t\t}\n\t\t}\n\n\t\t// Phase 2: Flush idle sessions with unflushed events\n\t\tconst idleSessions = this.store.rawEventSessionsPendingIdleFlush(idleBefore, sessionLimit);\n\t\tfor (const item of idleSessions) {\n\t\t\tconst { source, streamId } = item;\n\t\t\tif (!streamId) continue;\n\t\t\tif (drained.has(`${source}:${streamId}`)) continue;\n\t\t\tconst key = `${source}:${streamId}`;\n\t\t\tif (this.sessionFlushing.has(key)) continue;\n\t\t\tthis.sessionFlushing.add(key);\n\n\t\t\ttry {\n\t\t\t\tawait flushRawEvents(this.store, this.ingestOpts, {\n\t\t\t\t\topencodeSessionId: streamId,\n\t\t\t\t\tsource,\n\t\t\t\t\tcwd: null,\n\t\t\t\t\tproject: null,\n\t\t\t\t\tstartedAt: null,\n\t\t\t\t\tmaxEvents,\n\t\t\t\t\tallowEmptyFlush: true,\n\t\t\t\t});\n\t\t\t} catch (exc) {\n\t\t\t\tif (exc instanceof ObserverAuthError) {\n\t\t\t\t\tthis.handleAuthError(exc);\n\t\t\t\t\treturn; // Stop all flush work during auth backoff\n\t\t\t\t}\n\t\t\t\tconsole.error(\n\t\t\t\t\t`codemem: raw event sweeper flush failed for ${streamId}:`,\n\t\t\t\t\texc instanceof Error ? exc.message : exc,\n\t\t\t\t);\n\t\t\t} finally {\n\t\t\t\tthis.sessionFlushing.delete(key);\n\t\t\t}\n\t\t}\n\t}\n}\n","/**\n * Sync replication: cursor tracking, op chunking, and payload extraction.\n *\n * Uses Drizzle ORM for all SQL queries so that column mismatches are\n * compile-time errors instead of silent data-loss bugs at runtime.\n *\n * Keeps replication functions decoupled from MemoryStore by accepting\n * a raw Database handle. Ported from codemem/sync/replication.py.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { and, eq, gt, isNotNull, isNull, like, or, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { fromJson, fromJsonStrict, toJson, toJsonNullable } from \"./db.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport * as schema from \"./schema.js\";\nimport type { ReplicationOp } from \"./types.js\";\n\ntype MemoryItemRow = typeof schema.memoryItems.$inferSelect;\n\ninterface MemoryPayload {\n\tsession_id: number | null;\n\tkind: string | null;\n\ttitle: string | null;\n\tsubtitle: string | null;\n\tbody_text: string | null;\n\tconfidence: number | null;\n\ttags_text: string | null;\n\tactive: number | null;\n\tcreated_at: string | null;\n\tupdated_at: string | null;\n\tmetadata_json: Record<string, unknown>;\n\tactor_id: string | null;\n\tactor_display_name: string | null;\n\tvisibility: string | null;\n\tworkspace_id: string | null;\n\tworkspace_kind: string | null;\n\torigin_device_id: string | null;\n\torigin_source: string | null;\n\ttrust_state: string | null;\n\tnarrative: string | null;\n\tfacts: unknown;\n\tconcepts: unknown;\n\tfiles_read: unknown;\n\tfiles_modified: unknown;\n\tuser_prompt_id: number | null;\n\tprompt_number: number | null;\n\tdeleted_at: string | null;\n\trev: number;\n\timport_key: string | null;\n}\n\nfunction asNumberOrNull(value: unknown): number | null {\n\tif (value == null) return null;\n\tif (typeof value === \"number\" && Number.isFinite(value)) return value;\n\tif (typeof value === \"string\" && value.trim().length > 0) {\n\t\tconst parsed = Number(value);\n\t\tif (Number.isFinite(parsed)) return parsed;\n\t}\n\treturn null;\n}\n\nfunction asStringOrNull(value: unknown): string | null {\n\tif (value == null) return null;\n\treturn String(value);\n}\n\nfunction parseMemoryPayload(op: ReplicationOp, errors: string[]): MemoryPayload | null {\n\tif (!op.payload_json) return null;\n\tlet raw: Record<string, unknown>;\n\ttry {\n\t\traw = fromJsonStrict(op.payload_json);\n\t} catch (err) {\n\t\terrors.push(\n\t\t\t`op ${op.op_id}: skipped — payload_json is not a valid object: ${err instanceof Error ? err.message : String(err)}`,\n\t\t);\n\t\treturn null;\n\t}\n\tconst metadataRaw = raw.metadata_json;\n\tconst metadata_json =\n\t\tmetadataRaw && typeof metadataRaw === \"object\" && !Array.isArray(metadataRaw)\n\t\t\t? { ...(metadataRaw as Record<string, unknown>) }\n\t\t\t: {};\n\treturn {\n\t\tsession_id: asNumberOrNull(raw.session_id),\n\t\tkind: asStringOrNull(raw.kind),\n\t\ttitle: asStringOrNull(raw.title),\n\t\tsubtitle: asStringOrNull(raw.subtitle),\n\t\tbody_text: asStringOrNull(raw.body_text),\n\t\tconfidence: asNumberOrNull(raw.confidence),\n\t\ttags_text: asStringOrNull(raw.tags_text),\n\t\tactive: asNumberOrNull(raw.active),\n\t\tcreated_at: asStringOrNull(raw.created_at),\n\t\tupdated_at: asStringOrNull(raw.updated_at),\n\t\tmetadata_json,\n\t\tactor_id: asStringOrNull(raw.actor_id),\n\t\tactor_display_name: asStringOrNull(raw.actor_display_name),\n\t\tvisibility: asStringOrNull(raw.visibility),\n\t\tworkspace_id: asStringOrNull(raw.workspace_id),\n\t\tworkspace_kind: asStringOrNull(raw.workspace_kind),\n\t\torigin_device_id: asStringOrNull(raw.origin_device_id),\n\t\torigin_source: asStringOrNull(raw.origin_source),\n\t\ttrust_state: asStringOrNull(raw.trust_state),\n\t\tnarrative: asStringOrNull(raw.narrative),\n\t\tfacts: raw.facts ?? null,\n\t\tconcepts: raw.concepts ?? null,\n\t\tfiles_read: raw.files_read ?? null,\n\t\tfiles_modified: raw.files_modified ?? null,\n\t\tuser_prompt_id: asNumberOrNull(raw.user_prompt_id),\n\t\tprompt_number: asNumberOrNull(raw.prompt_number),\n\t\tdeleted_at: asStringOrNull(raw.deleted_at),\n\t\trev: asNumberOrNull(raw.rev) ?? 0,\n\t\timport_key: asStringOrNull(raw.import_key),\n\t};\n}\n\nfunction mergePayloadMetadata(\n\tmetadata: Record<string, unknown>,\n\tclockDeviceId: string,\n): Record<string, unknown> {\n\treturn {\n\t\t...metadata,\n\t\tclock_device_id: clockDeviceId,\n\t};\n}\n\nfunction clockDeviceIdFromMetadataJson(raw: string | null): string {\n\tconst metadata = fromJson(raw);\n\treturn typeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id : \"\";\n}\n\n// Op chunking\n\n/**\n * Split replication ops into batches that fit within a byte-size limit.\n *\n * Each batch is serialized as `{\"ops\": [...]}` and must not exceed maxBytes\n * when UTF-8 encoded. Throws if a single op exceeds the limit.\n */\nexport function chunkOpsBySize(ops: ReplicationOp[], maxBytes: number): ReplicationOp[][] {\n\tconst encoder = new TextEncoder();\n\t// Overhead: {\"ops\":[]} = 9 bytes, comma between elements = 1 byte\n\tconst WRAPPER_OVERHEAD = 9;\n\tconst COMMA_OVERHEAD = 1;\n\n\tconst batches: ReplicationOp[][] = [];\n\tlet current: ReplicationOp[] = [];\n\tlet currentBytes = WRAPPER_OVERHEAD;\n\n\tfor (const op of ops) {\n\t\tconst opBytes = encoder.encode(JSON.stringify(op)).byteLength;\n\t\tconst addedBytes = current.length === 0 ? opBytes : opBytes + COMMA_OVERHEAD;\n\n\t\tif (currentBytes + addedBytes <= maxBytes) {\n\t\t\tcurrent.push(op);\n\t\t\tcurrentBytes += addedBytes;\n\t\t\tcontinue;\n\t\t}\n\t\tif (current.length === 0) {\n\t\t\tthrow new Error(\"single op exceeds size limit\");\n\t\t}\n\t\tbatches.push(current);\n\t\tcurrent = [op];\n\t\tcurrentBytes = WRAPPER_OVERHEAD + opBytes;\n\t\tif (currentBytes > maxBytes) {\n\t\t\tthrow new Error(\"single op exceeds size limit\");\n\t\t}\n\t}\n\tif (current.length > 0) {\n\t\tbatches.push(current);\n\t}\n\treturn batches;\n}\n\n// Cursor read/write\n\n/**\n * Read the replication cursor for a peer device.\n *\n * Returns `[lastApplied, lastAcked]` — both null when no cursor exists.\n */\nexport function getReplicationCursor(\n\tdb: Database,\n\tpeerDeviceId: string,\n): [lastApplied: string | null, lastAcked: string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({\n\t\t\tlast_applied_cursor: schema.replicationCursors.last_applied_cursor,\n\t\t\tlast_acked_cursor: schema.replicationCursors.last_acked_cursor,\n\t\t})\n\t\t.from(schema.replicationCursors)\n\t\t.where(eq(schema.replicationCursors.peer_device_id, peerDeviceId))\n\t\t.get();\n\n\tif (!row) return [null, null];\n\treturn [row.last_applied_cursor, row.last_acked_cursor];\n}\n\n/**\n * Insert or update the replication cursor for a peer device.\n *\n * Uses COALESCE on update so callers can set only one of the two cursors\n * without clobbering the other.\n */\nexport function setReplicationCursor(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: { lastApplied?: string | null; lastAcked?: string | null } = {},\n): void {\n\tconst now = new Date().toISOString();\n\tconst lastApplied = options.lastApplied ?? null;\n\tconst lastAcked = options.lastAcked ?? null;\n\n\tconst d = drizzle(db, { schema });\n\t// Atomic UPSERT — avoids TOCTOU race with concurrent sync workers.\n\t// Drizzle's onConflictDoUpdate doesn't support COALESCE(excluded.col, col)\n\t// natively, so we use raw SQL for the SET expressions.\n\td.insert(schema.replicationCursors)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tlast_applied_cursor: lastApplied,\n\t\t\tlast_acked_cursor: lastAcked,\n\t\t\tupdated_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.replicationCursors.peer_device_id,\n\t\t\tset: {\n\t\t\t\tlast_applied_cursor: sql`COALESCE(excluded.last_applied_cursor, ${schema.replicationCursors.last_applied_cursor})`,\n\t\t\t\tlast_acked_cursor: sql`COALESCE(excluded.last_acked_cursor, ${schema.replicationCursors.last_acked_cursor})`,\n\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n}\n\n// Payload extraction\n\n/**\n * Extract replication ops from a parsed JSON payload.\n *\n * Returns an empty array if the payload is not an object or lacks an `ops` array.\n */\n/** Required fields for a valid replication op. */\nconst REQUIRED_OP_FIELDS = [\n\t\"op_id\",\n\t\"entity_type\",\n\t\"entity_id\",\n\t\"op_type\",\n\t\"clock_rev\",\n\t\"clock_updated_at\",\n\t\"clock_device_id\",\n\t\"device_id\",\n\t\"created_at\",\n] as const;\n\n/**\n * Extract and validate replication ops from a parsed JSON payload.\n *\n * Returns an empty array if the payload is not an object or lacks an `ops` array.\n * Silently filters out ops missing required fields to prevent garbage data\n * from entering the replication pipeline.\n */\nexport function extractReplicationOps(payload: unknown): ReplicationOp[] {\n\tif (typeof payload !== \"object\" || payload === null) return [];\n\tconst obj = payload as Record<string, unknown>;\n\tconst ops = obj.ops;\n\tif (!Array.isArray(ops)) return [];\n\n\treturn ops.filter((op): op is ReplicationOp => {\n\t\tif (typeof op !== \"object\" || op === null) return false;\n\t\tconst record = op as Record<string, unknown>;\n\t\treturn REQUIRED_OP_FIELDS.every(\n\t\t\t(field) => record[field] !== undefined && record[field] !== null,\n\t\t);\n\t});\n}\n\n// Clock comparison\n\n/**\n * Build a clock tuple from individual fields.\n *\n * Clock tuples enable lexicographic comparison for last-writer-wins\n * conflict resolution: higher rev wins, tiebreak on updated_at, then device_id.\n */\nexport function clockTuple(\n\trev: number,\n\tupdatedAt: string,\n\tdeviceId: string,\n): [number, string, string] {\n\treturn [rev, updatedAt, deviceId];\n}\n\n/**\n * Return true if `candidate` clock is strictly newer than `existing`.\n *\n * Comparison order: rev (higher wins) → updated_at → device_id.\n * Mirrors Python's `_is_newer_clock` which relies on tuple ordering.\n */\nexport function isNewerClock(\n\tcandidate: [number, string, string],\n\texisting: [number, string, string],\n): boolean {\n\tif (candidate[0] !== existing[0]) return candidate[0] > existing[0];\n\tif (candidate[1] !== existing[1]) return candidate[1] > existing[1];\n\treturn candidate[2] > existing[2];\n}\n\n// Record a replication op\n\n/**\n * Generate and INSERT a single replication op for a memory item.\n *\n * Reads the memory item's clock fields (rev, updated_at, metadata_json)\n * from the DB, builds the op row, and inserts into `replication_ops`.\n * Returns the generated op_id (UUID).\n *\n * Uses Drizzle's typed select so all memory_items columns are captured\n * automatically — no hand-maintained column list to go out of sync.\n */\nexport function recordReplicationOp(\n\tdb: Database,\n\topts: {\n\t\tmemoryId: number;\n\t\topType: \"upsert\" | \"delete\";\n\t\tdeviceId: string;\n\t\tpayload?: Record<string, unknown>;\n\t},\n): string {\n\tconst d = drizzle(db, { schema });\n\tconst opId = randomUUID();\n\tconst now = new Date().toISOString();\n\n\t// Read the full memory row — typed via Drizzle schema\n\tconst row = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(eq(schema.memoryItems.id, opts.memoryId))\n\t\t.get();\n\n\tconst rev = Number(row?.rev ?? 0);\n\tconst updatedAt = row?.updated_at ?? now;\n\tconst entityId = row?.import_key ?? String(opts.memoryId);\n\tconst metadata = fromJson(row?.metadata_json);\n\tconst clockDeviceId =\n\t\ttypeof metadata.clock_device_id === \"string\" && metadata.clock_device_id.trim().length > 0\n\t\t\t? metadata.clock_device_id\n\t\t\t: opts.deviceId;\n\n\t// Build payload from the memory row so peers can reconstruct the full item.\n\t// Explicit payload override takes precedence (used by tests).\n\tlet payloadJson: string | null;\n\tif (opts.payload) {\n\t\tpayloadJson = toJson(opts.payload);\n\t} else if (row && opts.opType === \"upsert\") {\n\t\t// Parse JSON-string columns so they round-trip as objects, not double-encoded strings\n\t\tconst parseSqliteJson = (val: string | null | undefined): unknown => {\n\t\t\tif (typeof val !== \"string\") return val ?? null;\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(val);\n\t\t\t} catch {\n\t\t\t\treturn val;\n\t\t\t}\n\t\t};\n\n\t\tpayloadJson = toJson({\n\t\t\tsession_id: row.session_id,\n\t\t\tkind: row.kind,\n\t\t\ttitle: row.title,\n\t\t\tsubtitle: row.subtitle,\n\t\t\tbody_text: row.body_text,\n\t\t\tconfidence: row.confidence,\n\t\t\ttags_text: row.tags_text,\n\t\t\tactive: row.active,\n\t\t\tcreated_at: row.created_at,\n\t\t\tupdated_at: row.updated_at,\n\t\t\tmetadata_json: parseSqliteJson(row.metadata_json),\n\t\t\tactor_id: row.actor_id,\n\t\t\tactor_display_name: row.actor_display_name,\n\t\t\tvisibility: row.visibility,\n\t\t\tworkspace_id: row.workspace_id,\n\t\t\tworkspace_kind: row.workspace_kind,\n\t\t\torigin_device_id: row.origin_device_id,\n\t\t\torigin_source: row.origin_source,\n\t\t\ttrust_state: row.trust_state,\n\t\t\tfacts: parseSqliteJson(row.facts),\n\t\t\tnarrative: row.narrative,\n\t\t\tconcepts: parseSqliteJson(row.concepts),\n\t\t\tfiles_read: parseSqliteJson(row.files_read),\n\t\t\tfiles_modified: parseSqliteJson(row.files_modified),\n\t\t\tuser_prompt_id: row.user_prompt_id,\n\t\t\tprompt_number: row.prompt_number,\n\t\t\tdeleted_at: row.deleted_at,\n\t\t});\n\t} else {\n\t\tpayloadJson = null;\n\t}\n\n\td.insert(schema.replicationOps)\n\t\t.values({\n\t\t\top_id: opId,\n\t\t\tentity_type: \"memory_item\",\n\t\t\tentity_id: entityId,\n\t\t\top_type: opts.opType,\n\t\t\tpayload_json: payloadJson,\n\t\t\tclock_rev: rev,\n\t\t\tclock_updated_at: updatedAt,\n\t\t\tclock_device_id: clockDeviceId,\n\t\t\tdevice_id: opts.deviceId,\n\t\t\tcreated_at: now,\n\t\t})\n\t\t.run();\n\n\treturn opId;\n}\n\n// Load replication ops with cursor pagination\n\n/** Parse a `created_at|op_id` cursor into its two components. */\nfunction parseCursor(cursor: string): [createdAt: string, opId: string] | null {\n\tconst idx = cursor.indexOf(\"|\");\n\tif (idx < 0) return null;\n\treturn [cursor.slice(0, idx), cursor.slice(idx + 1)];\n}\n\n/** Build a `created_at|op_id` cursor string. */\nfunction computeCursor(createdAt: string, opId: string): string {\n\treturn `${createdAt}|${opId}`;\n}\n\n/**\n * Load replication ops created after `cursor`, ordered by (created_at, op_id).\n *\n * Returns `[ops, nextCursor]` where nextCursor is the cursor for the last\n * returned row (or null if no rows matched). The cursor format is\n * `created_at|op_id`.\n */\nexport function loadReplicationOpsSince(\n\tdb: Database,\n\tcursor: string | null,\n\tlimit = 100,\n\tdeviceId?: string,\n): [ReplicationOp[], string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst t = schema.replicationOps;\n\tconst conditions = [];\n\n\tif (cursor) {\n\t\tconst parsed = parseCursor(cursor);\n\t\tif (parsed) {\n\t\t\tconst [createdAt, opId] = parsed;\n\t\t\tconditions.push(\n\t\t\t\tor(gt(t.created_at, createdAt), and(eq(t.created_at, createdAt), gt(t.op_id, opId))),\n\t\t\t);\n\t\t}\n\t}\n\n\tif (deviceId) {\n\t\tconditions.push(or(eq(t.device_id, deviceId), eq(t.device_id, \"local\")));\n\t}\n\n\tconst whereClause = conditions.length > 0 ? and(...conditions) : undefined;\n\n\tconst rows = d\n\t\t.select()\n\t\t.from(t)\n\t\t.where(whereClause)\n\t\t.orderBy(t.created_at, t.op_id)\n\t\t.limit(limit)\n\t\t.all();\n\n\tconst ops: ReplicationOp[] = rows.map((r) => ({\n\t\top_id: r.op_id,\n\t\tentity_type: r.entity_type,\n\t\tentity_id: r.entity_id,\n\t\top_type: r.op_type,\n\t\tpayload_json: r.payload_json,\n\t\tclock_rev: r.clock_rev,\n\t\tclock_updated_at: r.clock_updated_at,\n\t\tclock_device_id: r.clock_device_id,\n\t\tdevice_id: r.device_id,\n\t\tcreated_at: r.created_at,\n\t}));\n\n\tlet nextCursor: string | null = null;\n\tif (rows.length > 0) {\n\t\tconst last = rows.at(-1);\n\t\tif (last) {\n\t\t\tnextCursor = computeCursor(last.created_at, last.op_id);\n\t\t}\n\t}\n\n\treturn [ops, nextCursor];\n}\n\nfunction cleanList(values: unknown): string[] {\n\tif (!Array.isArray(values)) return [];\n\tconst out: string[] = [];\n\tfor (const raw of values) {\n\t\tconst value = String(raw ?? \"\").trim();\n\t\tif (value) out.push(value);\n\t}\n\treturn out;\n}\n\nfunction parseStringList(value: unknown): string[] {\n\tif (Array.isArray(value)) return cleanList(value);\n\tif (typeof value === \"string\") {\n\t\treturn value\n\t\t\t.split(\",\")\n\t\t\t.map((part) => part.trim())\n\t\t\t.filter(Boolean);\n\t}\n\treturn [];\n}\n\nfunction parseJsonList(valuesJson: string | null | undefined): string[] {\n\tif (!valuesJson) return [];\n\ttry {\n\t\treturn cleanList(JSON.parse(valuesJson));\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction projectBasename(value: string | null | undefined): string {\n\tconst raw = String(value ?? \"\")\n\t\t.trim()\n\t\t.replaceAll(\"\\\\\", \"/\");\n\tif (!raw) return \"\";\n\tconst parts = raw.split(\"/\").filter(Boolean);\n\treturn parts.length > 0 ? (parts[parts.length - 1] ?? \"\") : \"\";\n}\n\nfunction effectiveSyncProjectFilters(\n\tdb: Database,\n\tpeerDeviceId: string | null,\n): { include: string[]; exclude: string[] } {\n\tconst d = drizzle(db, { schema });\n\tconst config = readCodememConfigFile();\n\tconst includeOverride = process.env.CODEMEM_SYNC_PROJECTS_INCLUDE;\n\tconst excludeOverride = process.env.CODEMEM_SYNC_PROJECTS_EXCLUDE;\n\tconst globalInclude =\n\t\tincludeOverride !== undefined\n\t\t\t? parseStringList(includeOverride)\n\t\t\t: parseStringList(config.sync_projects_include);\n\tconst globalExclude =\n\t\texcludeOverride !== undefined\n\t\t\t? parseStringList(excludeOverride)\n\t\t\t: parseStringList(config.sync_projects_exclude);\n\tif (!peerDeviceId) return { include: globalInclude, exclude: globalExclude };\n\n\tconst row = d\n\t\t.select({\n\t\t\tprojects_include_json: schema.syncPeers.projects_include_json,\n\t\t\tprojects_exclude_json: schema.syncPeers.projects_exclude_json,\n\t\t})\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.limit(1)\n\t\t.get();\n\tif (!row) return { include: globalInclude, exclude: globalExclude };\n\n\tconst hasOverride = row.projects_include_json != null || row.projects_exclude_json != null;\n\tif (!hasOverride) {\n\t\treturn { include: globalInclude, exclude: globalExclude };\n\t}\n\n\treturn {\n\t\tinclude: parseJsonList(row.projects_include_json),\n\t\texclude: parseJsonList(row.projects_exclude_json),\n\t};\n}\n\nfunction syncProjectAllowed(\n\tdb: Database,\n\tproject: string | null,\n\tpeerDeviceId: string | null,\n): boolean {\n\tconst { include, exclude } = effectiveSyncProjectFilters(db, peerDeviceId);\n\tconst projectName = String(project ?? \"\").trim();\n\tconst basename = projectBasename(projectName);\n\n\tif (exclude.some((item) => item === projectName || item === basename)) return false;\n\tif (include.length === 0) return true;\n\treturn include.some((item) => item === projectName || item === basename);\n}\n\nfunction syncVisibilityAllowed(payload: Record<string, unknown> | null): boolean {\n\tif (!payload) return false;\n\tlet visibility = String(payload.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tconst metadata =\n\t\tpayload.metadata_json &&\n\t\ttypeof payload.metadata_json === \"object\" &&\n\t\t!Array.isArray(payload.metadata_json)\n\t\t\t? (payload.metadata_json as Record<string, unknown>)\n\t\t\t: {};\n\tconst metadataVisibility = String(metadata.visibility ?? \"\")\n\t\t.trim()\n\t\t.toLowerCase();\n\tif (!visibility && metadataVisibility) {\n\t\tvisibility = metadataVisibility;\n\t}\n\n\tif (!visibility) {\n\t\tlet workspaceKind = String(payload.workspace_kind ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tlet workspaceId = String(payload.workspace_id ?? \"\")\n\t\t\t.trim()\n\t\t\t.toLowerCase();\n\t\tif (!workspaceKind)\n\t\t\tworkspaceKind = String(metadata.workspace_kind ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\tif (!workspaceId)\n\t\t\tworkspaceId = String(metadata.workspace_id ?? \"\")\n\t\t\t\t.trim()\n\t\t\t\t.toLowerCase();\n\t\tif (workspaceKind === \"shared\" || workspaceId.startsWith(\"shared:\")) {\n\t\t\tvisibility = \"shared\";\n\t\t} else {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn visibility === \"shared\";\n}\n\nfunction peerClaimedLocalActor(db: Database, peerDeviceId: string | null): boolean {\n\tif (!peerDeviceId) return false;\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ claimed_local_actor: schema.syncPeers.claimed_local_actor })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.limit(1)\n\t\t.get();\n\treturn Boolean(row?.claimed_local_actor);\n}\n\nfunction parsePayload(payloadJson: string | null): Record<string, unknown> | null {\n\tif (!payloadJson || !payloadJson.trim()) return null;\n\ttry {\n\t\tconst parsed = JSON.parse(payloadJson) as unknown;\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return null;\n\t\treturn parsed as Record<string, unknown>;\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport interface FilterReplicationSkipped {\n\treason: \"visibility_filter\" | \"project_filter\";\n\top_id: string;\n\tcreated_at: string;\n\tentity_type: string;\n\tentity_id: string;\n\tskipped_count: number;\n\tproject?: string | null;\n\tvisibility?: string | null;\n}\n\n/**\n * Filter replication ops for peer sync scopes.\n *\n * Returns ops that pass project/visibility rules, a cursor for the last\n * processed op (including skipped ops), and skipped metadata when filtering\n * removed one or more ops.\n */\nexport function filterReplicationOpsForSyncWithStatus(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tpeerDeviceId: string | null,\n): [ReplicationOp[], string | null, FilterReplicationSkipped | null] {\n\tconst allowed: ReplicationOp[] = [];\n\tlet nextCursor: string | null = null;\n\tlet skippedCount = 0;\n\tlet firstSkipped: FilterReplicationSkipped | null = null;\n\tfor (const op of ops) {\n\t\tif (op.entity_type === \"memory_item\") {\n\t\t\tconst payload = parsePayload(op.payload_json);\n\t\t\tif (op.op_type === \"delete\" && payload == null) {\n\t\t\t\tallowed.push(op);\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!syncVisibilityAllowed(payload) && !peerClaimedLocalActor(db, peerDeviceId)) {\n\t\t\t\tskippedCount += 1;\n\t\t\t\tif (!firstSkipped) {\n\t\t\t\t\tfirstSkipped = {\n\t\t\t\t\t\treason: \"visibility_filter\",\n\t\t\t\t\t\top_id: op.op_id,\n\t\t\t\t\t\tcreated_at: op.created_at,\n\t\t\t\t\t\tentity_type: op.entity_type,\n\t\t\t\t\t\tentity_id: op.entity_id,\n\t\t\t\t\t\tvisibility: typeof payload?.visibility === \"string\" ? String(payload.visibility) : null,\n\t\t\t\t\t\tskipped_count: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst project =\n\t\t\t\ttypeof payload?.project === \"string\" && payload.project.trim()\n\t\t\t\t\t? payload.project.trim()\n\t\t\t\t\t: null;\n\t\t\tif (!syncProjectAllowed(db, project, peerDeviceId)) {\n\t\t\t\tskippedCount += 1;\n\t\t\t\tif (!firstSkipped) {\n\t\t\t\t\tfirstSkipped = {\n\t\t\t\t\t\treason: \"project_filter\",\n\t\t\t\t\t\top_id: op.op_id,\n\t\t\t\t\t\tcreated_at: op.created_at,\n\t\t\t\t\t\tentity_type: op.entity_type,\n\t\t\t\t\t\tentity_id: op.entity_id,\n\t\t\t\t\t\tproject,\n\t\t\t\t\t\tskipped_count: 0,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tallowed.push(op);\n\t\tnextCursor = computeCursor(op.created_at, op.op_id);\n\t}\n\n\tif (firstSkipped) {\n\t\tfirstSkipped.skipped_count = skippedCount;\n\t}\n\n\treturn [allowed, nextCursor, firstSkipped];\n}\n\nexport function filterReplicationOpsForSync(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tpeerDeviceId: string | null,\n): [ReplicationOp[], string | null] {\n\tconst [allowed, nextCursor] = filterReplicationOpsForSyncWithStatus(db, ops, peerDeviceId);\n\treturn [allowed, nextCursor];\n}\n\n// Apply inbound replication ops\n\nexport interface ApplyResult {\n\tapplied: number;\n\tskipped: number;\n\tconflicts: number;\n\terrors: string[];\n}\n\nconst LEGACY_IMPORT_KEY_OLD_RE = /^legacy:memory_item:(.+)$/;\n\n/**\n * Rewrite legacy import keys into globally unique, device-scoped keys.\n *\n * Older databases may contain keys like `legacy:memory_item:<id>`, which can\n * collide across peers. This rewrites them to\n * `legacy:<device_id>:memory_item:<id>`.\n */\nexport function migrateLegacyImportKeys(db: Database, limit = 2000): number {\n\tconst d = drizzle(db, { schema });\n\tconst deviceRow = d\n\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst localDeviceId = String(deviceRow?.device_id ?? \"\").trim();\n\tif (!localDeviceId) return 0;\n\n\tconst rows = d\n\t\t.select({\n\t\t\tid: schema.memoryItems.id,\n\t\t\timport_key: schema.memoryItems.import_key,\n\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t})\n\t\t.from(schema.memoryItems)\n\t\t.where(\n\t\t\tor(\n\t\t\t\tisNull(schema.memoryItems.import_key),\n\t\t\t\tsql`TRIM(${schema.memoryItems.import_key}) = ''`,\n\t\t\t\tlike(schema.memoryItems.import_key, \"legacy:memory_item:%\"),\n\t\t\t),\n\t\t)\n\t\t.orderBy(schema.memoryItems.id)\n\t\t.limit(limit)\n\t\t.all();\n\n\tif (rows.length === 0) return 0;\n\n\tlet updated = 0;\n\tfor (const row of rows) {\n\t\tconst memoryId = Number(row.id);\n\t\tconst current = String(row.import_key ?? \"\").trim();\n\t\tconst metadata = fromJson(row.metadata_json);\n\t\tconst clockDeviceId =\n\t\t\ttypeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id.trim() : \"\";\n\n\t\tlet canonical = \"\";\n\t\tif (!current) {\n\t\t\tcanonical = `legacy:${localDeviceId}:memory_item:${memoryId}`;\n\t\t} else {\n\t\t\tconst match = current.match(LEGACY_IMPORT_KEY_OLD_RE);\n\t\t\tif (!match) continue;\n\t\t\tconst suffix = match[1] ?? \"\";\n\t\t\tconst origin = clockDeviceId && clockDeviceId !== \"local\" ? clockDeviceId : localDeviceId;\n\t\t\tcanonical = origin ? `legacy:${origin}:memory_item:${suffix}` : \"\";\n\t\t}\n\n\t\tif (!canonical || canonical === current) continue;\n\n\t\tconst existing = d\n\t\t\t.select({ id: schema.memoryItems.id })\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(eq(schema.memoryItems.import_key, canonical))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (existing && Number(existing.id) !== memoryId) {\n\t\t\tcontinue;\n\t\t}\n\n\t\td.update(schema.memoryItems)\n\t\t\t.set({ import_key: canonical })\n\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t.run();\n\t\tupdated++;\n\t}\n\n\treturn updated;\n}\n\n/**\n * Generate replication ops for rows that predate replication.\n *\n * Prioritizes delete/tombstone ops first, then active upserts, and only\n * generates ops when a matching op for the same entity/rev/op_type is missing.\n */\nexport function backfillReplicationOps(db: Database, limit = 200): number {\n\tif (limit <= 0) return 0;\n\n\tmigrateLegacyImportKeys(db, 2000);\n\n\tconst d = drizzle(db, { schema });\n\tconst deviceRow = d\n\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t.from(schema.syncDevice)\n\t\t.limit(1)\n\t\t.get();\n\tconst localDeviceId = String(deviceRow?.device_id ?? \"\").trim();\n\n\tconst deletedRows = d\n\t\t.select()\n\t\t.from(schema.memoryItems)\n\t\t.where(\n\t\t\tand(\n\t\t\t\tor(isNotNull(schema.memoryItems.deleted_at), eq(schema.memoryItems.active, 0)),\n\t\t\t\tsql`NOT EXISTS (\n\t\t\t\t\tSELECT 1\n\t\t\t\t\tFROM replication_ops ro\n\t\t\t\t\tWHERE ro.entity_type = 'memory_item'\n\t\t\t\t\t AND ro.entity_id = ${schema.memoryItems.import_key}\n\t\t\t\t\t AND ro.op_type = 'delete'\n\t\t\t\t\t AND ro.clock_rev = COALESCE(${schema.memoryItems.rev}, 0)\n\t\t\t\t)`,\n\t\t\t),\n\t\t)\n\t\t.orderBy(schema.memoryItems.updated_at)\n\t\t.limit(limit)\n\t\t.all();\n\n\tconst remaining = Math.max(0, limit - deletedRows.length);\n\tlet rows = deletedRows;\n\tif (remaining > 0) {\n\t\tconst upsertRows = d\n\t\t\t.select()\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNull(schema.memoryItems.deleted_at),\n\t\t\t\t\teq(schema.memoryItems.active, 1),\n\t\t\t\t\tsql`NOT EXISTS (\n\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\tFROM replication_ops ro\n\t\t\t\t\t\tWHERE ro.entity_type = 'memory_item'\n\t\t\t\t\t\t AND ro.entity_id = ${schema.memoryItems.import_key}\n\t\t\t\t\t\t AND ro.op_type = 'upsert'\n\t\t\t\t\t\t AND ro.clock_rev = COALESCE(${schema.memoryItems.rev}, 0)\n\t\t\t\t\t)`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.memoryItems.updated_at)\n\t\t\t.limit(remaining)\n\t\t\t.all();\n\t\trows = [...rows, ...upsertRows];\n\t}\n\n\tif (rows.length === 0) return 0;\n\n\tconst now = new Date().toISOString();\n\tlet inserted = 0;\n\n\tfor (const row of rows) {\n\t\tconst rowId = Number(row.id ?? 0);\n\t\tif (!rowId) continue;\n\n\t\tconst metadata =\n\t\t\ttypeof row.metadata_json === \"string\"\n\t\t\t\t? fromJson(row.metadata_json)\n\t\t\t\t: fromJsonNullableLike(row.metadata_json);\n\t\tconst metadataClockDeviceId =\n\t\t\ttypeof metadata.clock_device_id === \"string\" ? metadata.clock_device_id.trim() : \"\";\n\t\tconst originDeviceId =\n\t\t\tmetadataClockDeviceId && metadataClockDeviceId !== \"local\"\n\t\t\t\t? metadataClockDeviceId\n\t\t\t\t: localDeviceId;\n\n\t\tlet importKey = String(row.import_key ?? \"\").trim();\n\t\tif (!importKey) {\n\t\t\tif (!originDeviceId) continue;\n\t\t\timportKey = `legacy:${originDeviceId}:memory_item:${rowId}`;\n\t\t\td.update(schema.memoryItems)\n\t\t\t\t.set({ import_key: importKey })\n\t\t\t\t.where(eq(schema.memoryItems.id, rowId))\n\t\t\t\t.run();\n\t\t}\n\n\t\tconst rev = Number(row.rev ?? 0);\n\t\tconst active = Number(row.active ?? 1);\n\t\tconst deletedAt = String(row.deleted_at ?? \"\").trim();\n\t\tconst opType: \"upsert\" | \"delete\" = deletedAt || active === 0 ? \"delete\" : \"upsert\";\n\t\tconst opId = `backfill:memory_item:${importKey}:${rev}:${opType}`;\n\n\t\tconst exists = d\n\t\t\t.select({ one: sql<number>`1` })\n\t\t\t.from(schema.replicationOps)\n\t\t\t.where(eq(schema.replicationOps.op_id, opId))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (exists) continue;\n\n\t\tconst clockDeviceId = originDeviceId;\n\t\tif (!clockDeviceId) continue;\n\t\tconst payload = buildPayloadFromMemoryRow(row);\n\n\t\td.insert(schema.replicationOps)\n\t\t\t.values({\n\t\t\t\top_id: opId,\n\t\t\t\tentity_type: \"memory_item\",\n\t\t\t\tentity_id: importKey,\n\t\t\t\top_type: opType,\n\t\t\t\tpayload_json: toJson(payload),\n\t\t\t\tclock_rev: rev,\n\t\t\t\tclock_updated_at: String(row.updated_at ?? now),\n\t\t\t\tclock_device_id: clockDeviceId,\n\t\t\t\tdevice_id: clockDeviceId,\n\t\t\t\tcreated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoNothing()\n\t\t\t.run();\n\n\t\tinserted++;\n\t}\n\n\treturn inserted;\n}\n\nfunction fromJsonNullableLike(value: unknown): Record<string, unknown> {\n\tif (typeof value === \"string\") return fromJson(value);\n\tif (value && typeof value === \"object\") return value as Record<string, unknown>;\n\treturn {};\n}\n\nfunction buildPayloadFromMemoryRow(row: MemoryItemRow): MemoryPayload {\n\tconst parseSqliteJson = (val: unknown): unknown => {\n\t\tif (typeof val !== \"string\") return val ?? null;\n\t\ttry {\n\t\t\treturn JSON.parse(val);\n\t\t} catch {\n\t\t\treturn val;\n\t\t}\n\t};\n\tconst metadataParsed = parseSqliteJson(row.metadata_json);\n\tconst metadataJson =\n\t\tmetadataParsed && typeof metadataParsed === \"object\" && !Array.isArray(metadataParsed)\n\t\t\t? (metadataParsed as Record<string, unknown>)\n\t\t\t: {};\n\n\treturn {\n\t\tsession_id: row.session_id ?? null,\n\t\tkind: row.kind ?? null,\n\t\ttitle: row.title ?? null,\n\t\tsubtitle: row.subtitle ?? null,\n\t\tbody_text: row.body_text ?? null,\n\t\tconfidence: row.confidence ?? null,\n\t\ttags_text: row.tags_text ?? null,\n\t\tactive: row.active ?? null,\n\t\tcreated_at: row.created_at ?? null,\n\t\tupdated_at: row.updated_at ?? null,\n\t\tmetadata_json: metadataJson,\n\t\tactor_id: row.actor_id ?? null,\n\t\tactor_display_name: row.actor_display_name ?? null,\n\t\tvisibility: row.visibility ?? null,\n\t\tworkspace_id: row.workspace_id ?? null,\n\t\tworkspace_kind: row.workspace_kind ?? null,\n\t\torigin_device_id: row.origin_device_id ?? null,\n\t\torigin_source: row.origin_source ?? null,\n\t\ttrust_state: row.trust_state ?? null,\n\t\tnarrative: row.narrative ?? null,\n\t\tfacts: parseSqliteJson(row.facts),\n\t\tconcepts: parseSqliteJson(row.concepts),\n\t\tfiles_read: parseSqliteJson(row.files_read),\n\t\tfiles_modified: parseSqliteJson(row.files_modified),\n\t\tuser_prompt_id: row.user_prompt_id ?? null,\n\t\tprompt_number: row.prompt_number ?? null,\n\t\tdeleted_at: row.deleted_at ?? null,\n\t\trev: row.rev ?? 0,\n\t\timport_key: row.import_key ?? null,\n\t};\n}\n\n/**\n * Apply inbound replication ops from a remote peer.\n *\n * Runs all ops in a single transaction. For each op:\n * - Skips if device_id matches localDeviceId (don't re-apply own ops)\n * - Skips if op_id already exists (idempotent)\n * - For upsert: finds existing memory by import_key; if existing has a newer\n * clock, counts as conflict and skips; otherwise INSERT or UPDATE\n * - For delete: soft-deletes (active=0, deleted_at) by import_key\n * - Records the applied op in replication_ops\n */\nexport function applyReplicationOps(\n\tdb: Database,\n\tops: ReplicationOp[],\n\tlocalDeviceId: string,\n): ApplyResult {\n\tconst d = drizzle(db, { schema });\n\tconst result: ApplyResult = { applied: 0, skipped: 0, conflicts: 0, errors: [] };\n\n\tconst applyAll = db.transaction(() => {\n\t\tfor (const op of ops) {\n\t\t\ttry {\n\t\t\t\t// Skip own ops\n\t\t\t\tif (op.device_id === localDeviceId) {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Idempotent: skip if already applied\n\t\t\t\tconst existing = d\n\t\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t\t.from(schema.replicationOps)\n\t\t\t\t\t.where(eq(schema.replicationOps.op_id, op.op_id))\n\t\t\t\t\t.get();\n\t\t\t\tif (existing) {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (op.op_type === \"upsert\") {\n\t\t\t\t\tconst importKey = op.entity_id;\n\t\t\t\t\tconst memRow = d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tid: schema.memoryItems.id,\n\t\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\t\tupdated_at: schema.memoryItems.updated_at,\n\t\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t.get();\n\n\t\t\t\t\tif (memRow) {\n\t\t\t\t\t\tconst existingClockDeviceId = clockDeviceIdFromMetadataJson(memRow.metadata_json);\n\t\t\t\t\t\tconst existingClock = clockTuple(\n\t\t\t\t\t\t\tmemRow.rev ?? 0,\n\t\t\t\t\t\t\tmemRow.updated_at ?? \"\",\n\t\t\t\t\t\t\texistingClockDeviceId,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst opClock = clockTuple(op.clock_rev, op.clock_updated_at, op.clock_device_id);\n\n\t\t\t\t\t\tif (!isNewerClock(opClock, existingClock)) {\n\t\t\t\t\t\t\tresult.conflicts++;\n\t\t\t\t\t\t\t// Still record the op so we don't re-process it\n\t\t\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Update existing row from payload — skip if malformed\n\t\t\t\t\t\tconst payload = parseMemoryPayload(op, result.errors);\n\t\t\t\t\t\tif (!payload) continue;\n\t\t\t\t\t\tconst metaObj = mergePayloadMetadata(payload.metadata_json, op.clock_device_id);\n\n\t\t\t\t\t\td.update(schema.memoryItems)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tkind: sql`COALESCE(${payload.kind}, ${schema.memoryItems.kind})`,\n\t\t\t\t\t\t\t\ttitle: sql`COALESCE(${payload.title}, ${schema.memoryItems.title})`,\n\t\t\t\t\t\t\t\tsubtitle: payload.subtitle,\n\t\t\t\t\t\t\t\tbody_text: sql`COALESCE(${payload.body_text}, ${schema.memoryItems.body_text})`,\n\t\t\t\t\t\t\t\tconfidence: sql`COALESCE(${payload.confidence != null ? Number(payload.confidence) : null}, ${schema.memoryItems.confidence})`,\n\t\t\t\t\t\t\t\ttags_text: sql`COALESCE(${payload.tags_text}, ${schema.memoryItems.tags_text})`,\n\t\t\t\t\t\t\t\tactive: sql`COALESCE(${payload.active != null ? Number(payload.active) : null}, ${schema.memoryItems.active})`,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t\tmetadata_json: toJson(metaObj),\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tdeleted_at: payload.deleted_at,\n\t\t\t\t\t\t\t\tactor_id: sql`COALESCE(${payload.actor_id}, ${schema.memoryItems.actor_id})`,\n\t\t\t\t\t\t\t\tactor_display_name: sql`COALESCE(${payload.actor_display_name}, ${schema.memoryItems.actor_display_name})`,\n\t\t\t\t\t\t\t\tvisibility: sql`COALESCE(${payload.visibility}, ${schema.memoryItems.visibility})`,\n\t\t\t\t\t\t\t\tworkspace_id: sql`COALESCE(${payload.workspace_id}, ${schema.memoryItems.workspace_id})`,\n\t\t\t\t\t\t\t\tworkspace_kind: sql`COALESCE(${payload.workspace_kind}, ${schema.memoryItems.workspace_kind})`,\n\t\t\t\t\t\t\t\torigin_device_id: sql`COALESCE(${payload.origin_device_id}, ${schema.memoryItems.origin_device_id})`,\n\t\t\t\t\t\t\t\torigin_source: sql`COALESCE(${payload.origin_source}, ${schema.memoryItems.origin_source})`,\n\t\t\t\t\t\t\t\ttrust_state: sql`COALESCE(${payload.trust_state}, ${schema.memoryItems.trust_state})`,\n\t\t\t\t\t\t\t\tnarrative: payload.narrative,\n\t\t\t\t\t\t\t\tfacts: toJsonNullable(payload.facts),\n\t\t\t\t\t\t\t\tconcepts: toJsonNullable(payload.concepts),\n\t\t\t\t\t\t\t\tfiles_read: toJsonNullable(payload.files_read),\n\t\t\t\t\t\t\t\tfiles_modified: toJsonNullable(payload.files_modified),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Insert new memory item — skip if malformed\n\t\t\t\t\t\tconst payload = parseMemoryPayload(op, result.errors);\n\t\t\t\t\t\tif (!payload) continue;\n\t\t\t\t\t\tconst sessionId = ensureSessionForReplication(d, null, op.clock_updated_at);\n\t\t\t\t\t\tconst metaObj = mergePayloadMetadata(payload.metadata_json, op.clock_device_id);\n\n\t\t\t\t\t\td.insert(schema.memoryItems)\n\t\t\t\t\t\t\t.values({\n\t\t\t\t\t\t\t\tsession_id: sessionId,\n\t\t\t\t\t\t\t\tkind: payload.kind ?? \"discovery\",\n\t\t\t\t\t\t\t\ttitle: payload.title ?? \"\",\n\t\t\t\t\t\t\t\tsubtitle: payload.subtitle,\n\t\t\t\t\t\t\t\tbody_text: payload.body_text ?? \"\",\n\t\t\t\t\t\t\t\tconfidence: payload.confidence != null ? Number(payload.confidence) : 0.5,\n\t\t\t\t\t\t\t\ttags_text: payload.tags_text ?? \"\",\n\t\t\t\t\t\t\t\tactive: payload.active != null ? Number(payload.active) : 1,\n\t\t\t\t\t\t\t\tcreated_at: payload.created_at ?? op.clock_updated_at,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t\tmetadata_json: toJson(metaObj),\n\t\t\t\t\t\t\t\timport_key: importKey,\n\t\t\t\t\t\t\t\tdeleted_at: payload.deleted_at,\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tactor_id: payload.actor_id,\n\t\t\t\t\t\t\t\tactor_display_name: payload.actor_display_name,\n\t\t\t\t\t\t\t\tvisibility: payload.visibility,\n\t\t\t\t\t\t\t\tworkspace_id: payload.workspace_id,\n\t\t\t\t\t\t\t\tworkspace_kind: payload.workspace_kind,\n\t\t\t\t\t\t\t\torigin_device_id: payload.origin_device_id,\n\t\t\t\t\t\t\t\torigin_source: payload.origin_source,\n\t\t\t\t\t\t\t\ttrust_state: payload.trust_state,\n\t\t\t\t\t\t\t\tnarrative: payload.narrative,\n\t\t\t\t\t\t\t\tfacts: toJsonNullable(payload.facts),\n\t\t\t\t\t\t\t\tconcepts: toJsonNullable(payload.concepts),\n\t\t\t\t\t\t\t\tfiles_read: toJsonNullable(payload.files_read),\n\t\t\t\t\t\t\t\tfiles_modified: toJsonNullable(payload.files_modified),\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t}\n\t\t\t\t} else if (op.op_type === \"delete\") {\n\t\t\t\t\tconst importKey = op.entity_id;\n\t\t\t\t\tconst existingForDelete = d\n\t\t\t\t\t\t.select({\n\t\t\t\t\t\t\tid: schema.memoryItems.id,\n\t\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\t\tupdated_at: schema.memoryItems.updated_at,\n\t\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t\t.where(eq(schema.memoryItems.import_key, importKey))\n\t\t\t\t\t\t.limit(1)\n\t\t\t\t\t\t.get();\n\n\t\t\t\t\tif (existingForDelete) {\n\t\t\t\t\t\t// Clock-compare: only delete if the incoming op is newer\n\t\t\t\t\t\tconst existingClock = clockTuple(\n\t\t\t\t\t\t\texistingForDelete.rev ?? 1,\n\t\t\t\t\t\t\texistingForDelete.updated_at ?? \"\",\n\t\t\t\t\t\t\tclockDeviceIdFromMetadataJson(existingForDelete.metadata_json),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tconst incomingClock = clockTuple(op.clock_rev, op.clock_updated_at, op.clock_device_id);\n\t\t\t\t\t\tif (!isNewerClock(incomingClock, existingClock)) {\n\t\t\t\t\t\t\tresult.conflicts++;\n\t\t\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst now = new Date().toISOString();\n\t\t\t\t\t\td.update(schema.memoryItems)\n\t\t\t\t\t\t\t.set({\n\t\t\t\t\t\t\t\tactive: 0,\n\t\t\t\t\t\t\t\tdeleted_at: sql`COALESCE(${schema.memoryItems.deleted_at}, ${now})`,\n\t\t\t\t\t\t\t\trev: op.clock_rev,\n\t\t\t\t\t\t\t\tupdated_at: op.clock_updated_at,\n\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t.where(eq(schema.memoryItems.id, existingForDelete.id))\n\t\t\t\t\t\t\t.run();\n\t\t\t\t\t}\n\t\t\t\t\t// If import_key not found, record the op as a tombstone for future resolution\n\t\t\t\t} else {\n\t\t\t\t\tresult.skipped++;\n\t\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Record the applied op\n\t\t\t\tinsertReplicationOpRow(d, op);\n\t\t\t\tresult.applied++;\n\t\t\t} catch (err) {\n\t\t\t\tresult.errors.push(`op ${op.op_id}: ${err instanceof Error ? err.message : String(err)}`);\n\t\t\t}\n\t\t}\n\t});\n\n\tapplyAll();\n\treturn result;\n}\n\n/** Insert a replication op row into the replication_ops table (ignore on conflict). */\nfunction insertReplicationOpRow(d: ReturnType<typeof drizzle>, op: ReplicationOp): void {\n\td.insert(schema.replicationOps)\n\t\t.values({\n\t\t\top_id: op.op_id,\n\t\t\tentity_type: op.entity_type,\n\t\t\tentity_id: op.entity_id,\n\t\t\top_type: op.op_type,\n\t\t\tpayload_json: op.payload_json,\n\t\t\tclock_rev: op.clock_rev,\n\t\t\tclock_updated_at: op.clock_updated_at,\n\t\t\tclock_device_id: op.clock_device_id,\n\t\t\tdevice_id: op.device_id,\n\t\t\tcreated_at: op.created_at,\n\t\t})\n\t\t.onConflictDoNothing()\n\t\t.run();\n}\n\n/**\n * Ensure a session row exists for replication inserts.\n * Creates a minimal session if one doesn't exist yet.\n */\nfunction ensureSessionForReplication(\n\td: ReturnType<typeof drizzle>,\n\tsessionId: number | null,\n\tcreatedAt: string,\n): number {\n\tif (sessionId != null) {\n\t\tconst row = d\n\t\t\t.select({ id: schema.sessions.id })\n\t\t\t.from(schema.sessions)\n\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t.get();\n\t\tif (row) return sessionId;\n\t}\n\t// Create a new session for replicated data\n\tconst now = createdAt || new Date().toISOString();\n\tconst rows = d\n\t\t.insert(schema.sessions)\n\t\t.values({\n\t\t\tstarted_at: now,\n\t\t\tuser: \"sync\",\n\t\t\ttool_version: \"sync_replication\",\n\t\t\tmetadata_json: toJson({ source: \"sync\" }),\n\t\t})\n\t\t.returning({ id: schema.sessions.id })\n\t\t.all();\n\tconst id = rows[0]?.id;\n\tif (id == null) throw new Error(\"session insert returned no id\");\n\treturn id;\n}\n","/**\n * MemoryStore — TypeScript port of codemem/store/_store.py (Phase 1 CRUD).\n *\n * During Phase 1, Python owns DDL/schema. This TS runtime reads and writes data\n * but does NOT create or migrate tables. The assertSchemaReady() call verifies\n * the schema was initialized by Python before any operations.\n *\n * Methods ported: get, remember, forget, recent, recentByKinds, stats,\n * updateMemoryVisibility, close.\n *\n * NOT ported yet: pack, usage tracking, vectors, provenance resolution,\n * memory_owned_by_self check.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { statSync } from \"node:fs\";\nimport { and, desc, eq, gt, inArray, isNotNull, lt, lte, or, type SQL, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport {\n\tassertSchemaReady,\n\tbackupOnFirstAccess,\n\tconnect,\n\tDEFAULT_DB_PATH,\n\tensureAdditiveSchemaCompatibility,\n\tfromJson,\n\tisEmbeddingDisabled,\n\tloadSqliteVec,\n\tresolveDbPath,\n\ttableExists,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nimport { buildFilterClauses } from \"./filters.js\";\nimport { readCodememConfigFile } from \"./observer-config.js\";\nimport { buildMemoryPack, buildMemoryPackAsync } from \"./pack.js\";\nimport * as schema from \"./schema.js\";\nimport {\n\ttype ExplainOptions,\n\texplain as explainFn,\n\tsearch as searchFn,\n\ttimeline as timelineFn,\n} from \"./search.js\";\nimport { recordReplicationOp } from \"./sync-replication.js\";\nimport type {\n\tExplainResponse,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tPackResponse,\n\tStoreStats,\n\tTimelineItemResponse,\n} from \"./types.js\";\nimport { storeVectors } from \"./vectors.js\";\n\n// Memory kind validation (mirrors codemem/memory_kinds.py)\n\nconst ALLOWED_MEMORY_KINDS = new Set([\n\t\"discovery\",\n\t\"change\",\n\t\"feature\",\n\t\"bugfix\",\n\t\"refactor\",\n\t\"decision\",\n\t\"exploration\",\n]);\n\n/** Normalize and validate a memory kind. Throws on invalid kinds. */\nfunction validateMemoryKind(kind: string): string {\n\tconst normalized = kind.trim().toLowerCase();\n\tif (!ALLOWED_MEMORY_KINDS.has(normalized)) {\n\t\tthrow new Error(\n\t\t\t`Invalid memory kind \"${kind}\". Allowed: ${[...ALLOWED_MEMORY_KINDS].join(\", \")}`,\n\t\t);\n\t}\n\treturn normalized;\n}\n\n// Helpers\n\n/** ISO 8601 timestamp in UTC. */\nfunction nowIso(): string {\n\treturn new Date().toISOString();\n}\n\nfunction countQuestionPlaceholders(clause: string): number {\n\treturn (clause.match(/\\?/g) ?? []).length;\n}\n\nfunction sqlFromParameterizedClause(clause: string, params: unknown[]): SQL {\n\tconst parts = clause.split(\"?\");\n\tlet acc: SQL = sql.raw(parts[0] ?? \"\");\n\tfor (let i = 1; i < parts.length; i++) {\n\t\tacc = sql`${acc}${params[i - 1]}${sql.raw(parts[i] ?? \"\")}`;\n\t}\n\treturn acc;\n}\n\nfunction buildWhereSql(clauses: string[], params: unknown[]): SQL {\n\tconst sqlClauses: SQL[] = [];\n\tlet cursor = 0;\n\tfor (const clause of clauses) {\n\t\tconst count = countQuestionPlaceholders(clause);\n\t\tconst clauseParams = params.slice(cursor, cursor + count);\n\t\tsqlClauses.push(sqlFromParameterizedClause(clause, clauseParams));\n\t\tcursor += count;\n\t}\n\tif (cursor !== params.length) {\n\t\tthrow new Error(\"filter parameter mismatch while building SQL clauses\");\n\t}\n\tif (sqlClauses.length === 1) return sqlClauses[0] ?? sql`1=1`;\n\tconst combined = and(...sqlClauses);\n\tif (!combined) throw new Error(\"failed to combine filter SQL clauses\");\n\treturn combined;\n}\n\n/** Trim a string value, returning null for empty/non-string. Matches Python's _clean_optional_str. */\nfunction cleanStr(value: unknown): string | null {\n\tif (value == null || typeof value !== \"string\") return null;\n\tconst trimmed = value.trim();\n\treturn trimmed.length > 0 ? trimmed : null;\n}\n\nfunction parseJsonList(value: unknown): string[] {\n\tif (typeof value !== \"string\" || !value.trim()) return [];\n\ttry {\n\t\tconst parsed = JSON.parse(value);\n\t\treturn Array.isArray(parsed)\n\t\t\t? parsed\n\t\t\t\t\t.filter((item): item is string => typeof item === \"string\")\n\t\t\t\t\t.map((item) => item.trim())\n\t\t\t\t\t.filter(Boolean)\n\t\t\t: [];\n\t} catch {\n\t\treturn [];\n\t}\n}\n\nfunction projectBasename(value: string | null | undefined): string {\n\tconst raw = cleanStr(value);\n\tif (!raw) return \"\";\n\tconst parts = raw.replace(/\\\\/g, \"/\").split(\"/\").filter(Boolean);\n\treturn parts[parts.length - 1] ?? raw;\n}\n\nconst LEGACY_SYNC_ACTOR_DISPLAY_NAME = \"Legacy synced peer\";\nconst LEGACY_SHARED_WORKSPACE_ID = \"shared:legacy\";\n\n/**\n * Parse a row's metadata_json string into a plain object.\n * Returns a new MemoryItemResponse with metadata_json as a parsed object.\n */\nfunction parseMetadata(row: MemoryItem): MemoryItemResponse {\n\tconst { metadata_json, ...rest } = row;\n\treturn { ...rest, metadata_json: fromJson(metadata_json) };\n}\n\n// MemoryStore\n\nexport class MemoryStore {\n\treadonly db: Database;\n\treadonly dbPath: string;\n\treadonly deviceId: string;\n\treadonly actorId: string;\n\treadonly actorDisplayName: string;\n\tprivate readonly pendingVectorWrites = new Set<Promise<void>>();\n\n\t/** Lazy Drizzle ORM wrapper — shares the same better-sqlite3 connection. */\n\tprivate _drizzle: ReturnType<typeof drizzle> | null = null;\n\tprivate get d() {\n\t\tif (!this._drizzle) this._drizzle = drizzle(this.db, { schema });\n\t\treturn this._drizzle;\n\t}\n\n\tconstructor(dbPath: string = DEFAULT_DB_PATH) {\n\t\tthis.dbPath = resolveDbPath(dbPath);\n\t\tbackupOnFirstAccess(this.dbPath);\n\t\tthis.db = connect(this.dbPath);\n\t\ttry {\n\t\t\tloadSqliteVec(this.db);\n\t\t\tassertSchemaReady(this.db);\n\t\t\tensureAdditiveSchemaCompatibility(this.db);\n\t\t} catch (err) {\n\t\t\tthis.db.close();\n\t\t\tthrow err;\n\t\t}\n\n\t\t// Resolve device ID: env var → sync_device table → stable \"local\" fallback.\n\t\t// Python uses exactly this order and fallback.\n\t\tconst envDeviceId = process.env.CODEMEM_DEVICE_ID?.trim();\n\t\tif (envDeviceId) {\n\t\t\tthis.deviceId = envDeviceId;\n\t\t} else {\n\t\t\t// Guard: sync_device may not exist in older/minimal schemas\n\t\t\tlet dbDeviceId: string | undefined;\n\t\t\ttry {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select({ device_id: schema.syncDevice.device_id })\n\t\t\t\t\t.from(schema.syncDevice)\n\t\t\t\t\t.limit(1)\n\t\t\t\t\t.get();\n\t\t\t\tdbDeviceId = row?.device_id;\n\t\t\t} catch {\n\t\t\t\t// Table doesn't exist — fall through to stable default\n\t\t\t}\n\t\t\tthis.deviceId = dbDeviceId ?? \"local\";\n\t\t}\n\n\t\t// Resolve actor identity — matches Python load_config() precedence:\n\t\t// config file, then env override, then local fallbacks.\n\t\tconst config = readCodememConfigFile();\n\t\tconst configActorId = Object.hasOwn(process.env, \"CODEMEM_ACTOR_ID\")\n\t\t\t? cleanStr(process.env.CODEMEM_ACTOR_ID)\n\t\t\t: (cleanStr(config.actor_id) ?? null);\n\t\tthis.actorId = configActorId || `local:${this.deviceId}`;\n\n\t\tconst configDisplayName = Object.hasOwn(process.env, \"CODEMEM_ACTOR_DISPLAY_NAME\")\n\t\t\t? cleanStr(process.env.CODEMEM_ACTOR_DISPLAY_NAME)\n\t\t\t: (cleanStr(config.actor_display_name) ?? null);\n\t\tthis.actorDisplayName =\n\t\t\tconfigDisplayName || process.env.USER?.trim() || process.env.USERNAME?.trim() || this.actorId;\n\t}\n\n\tprivate enqueueVectorWrite(memoryId: number, title: string, bodyText: string): void {\n\t\tif (this.db.inTransaction) return;\n\t\tlet op: Promise<void> | null = null;\n\t\top = storeVectors(this.db, memoryId, title, bodyText)\n\t\t\t.catch(() => {\n\t\t\t\t// Non-fatal — keep memory writes resilient when embeddings are unavailable\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tif (op) this.pendingVectorWrites.delete(op);\n\t\t\t});\n\t\tthis.pendingVectorWrites.add(op);\n\t}\n\n\tasync flushPendingVectorWrites(): Promise<void> {\n\t\tif (this.pendingVectorWrites.size === 0) return;\n\t\tawait Promise.allSettled([...this.pendingVectorWrites]);\n\t}\n\n\t// get\n\n\t/**\n\t * Fetch a single memory item by ID.\n\t * Returns null if not found (does not filter by active status).\n\t */\n\tget(memoryId: number): MemoryItemResponse | null {\n\t\tconst row = this.d\n\t\t\t.select()\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t.get() as MemoryItem | undefined;\n\t\tif (!row) return null;\n\t\treturn parseMetadata(row);\n\t}\n\n\t// startSession / endSession\n\n\t/**\n\t * Create a new session row. Returns the session ID.\n\t * Matches Python's store.start_session().\n\t */\n\tstartSession(opts: {\n\t\tcwd?: string;\n\t\tproject?: string | null;\n\t\tgitRemote?: string | null;\n\t\tgitBranch?: string | null;\n\t\tuser?: string;\n\t\ttoolVersion?: string;\n\t\tmetadata?: Record<string, unknown>;\n\t}): number {\n\t\tconst now = nowIso();\n\t\tconst rows = this.d\n\t\t\t.insert(schema.sessions)\n\t\t\t.values({\n\t\t\t\tstarted_at: now,\n\t\t\t\tcwd: opts.cwd ?? process.cwd(),\n\t\t\t\tproject: opts.project ?? null,\n\t\t\t\tgit_remote: opts.gitRemote ?? null,\n\t\t\t\tgit_branch: opts.gitBranch ?? null,\n\t\t\t\tuser: opts.user ?? process.env.USER ?? \"unknown\",\n\t\t\t\ttool_version: opts.toolVersion ?? \"manual\",\n\t\t\t\tmetadata_json: toJson(opts.metadata ?? {}),\n\t\t\t})\n\t\t\t.returning({ id: schema.sessions.id })\n\t\t\t.all();\n\t\tconst id = rows[0]?.id;\n\t\tif (id == null) throw new Error(\"session insert returned no id\");\n\t\treturn id;\n\t}\n\n\t/**\n\t * End a session by recording ended_at.\n\t * FIX: merges incoming metadata with existing instead of replacing.\n\t * No-op if session doesn't exist.\n\t */\n\tendSession(sessionId: number, metadata?: Record<string, unknown>): void {\n\t\tconst now = nowIso();\n\t\tif (metadata) {\n\t\t\t// Read existing metadata and merge — prevents clobbering earlier fields\n\t\t\tconst existing = this.d\n\t\t\t\t.select({ metadata_json: schema.sessions.metadata_json })\n\t\t\t\t.from(schema.sessions)\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.get();\n\t\t\tconst merged = { ...fromJson(existing?.metadata_json), ...metadata };\n\t\t\tthis.d\n\t\t\t\t.update(schema.sessions)\n\t\t\t\t.set({ ended_at: now, metadata_json: toJson(merged) })\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.run();\n\t\t} else {\n\t\t\tthis.d\n\t\t\t\t.update(schema.sessions)\n\t\t\t\t.set({ ended_at: now })\n\t\t\t\t.where(eq(schema.sessions.id, sessionId))\n\t\t\t\t.run();\n\t\t}\n\t}\n\n\t// remember\n\n\t/**\n\t * Create a new memory item. Returns the new memory ID.\n\t *\n\t * Validates and normalizes the kind. Resolves provenance fields (actor_id,\n\t * visibility, workspace_id, trust_state) matching Python's\n\t * _resolve_memory_provenance logic.\n\t */\n\tremember(\n\t\tsessionId: number,\n\t\tkind: string,\n\t\ttitle: string,\n\t\tbodyText: string,\n\t\tconfidence = 0.5,\n\t\ttags?: string[],\n\t\tmetadata?: Record<string, unknown>,\n\t): number {\n\t\tconst validKind = validateMemoryKind(kind);\n\t\tconst now = nowIso();\n\t\tconst tagsText = tags ? [...new Set(tags)].sort().join(\" \") : \"\";\n\t\tconst metaPayload = { ...(metadata ?? {}) };\n\n\t\tmetaPayload.clock_device_id ??= this.deviceId;\n\t\tconst importKey = (metaPayload.import_key as string) || randomUUID();\n\t\tmetaPayload.import_key = importKey;\n\n\t\t// Extract dedicated columns from metadata before they get buried in metadata_json\n\t\tconst subtitle = typeof metaPayload.subtitle === \"string\" ? metaPayload.subtitle : null;\n\t\tconst narrative = typeof metaPayload.narrative === \"string\" ? metaPayload.narrative : null;\n\t\tconst facts = Array.isArray(metaPayload.facts) ? metaPayload.facts : null;\n\t\tconst concepts = Array.isArray(metaPayload.concepts) ? metaPayload.concepts : null;\n\t\tconst filesRead = Array.isArray(metaPayload.files_read) ? metaPayload.files_read : null;\n\t\tconst filesModified = Array.isArray(metaPayload.files_modified)\n\t\t\t? metaPayload.files_modified\n\t\t\t: null;\n\t\tconst promptNumber =\n\t\t\ttypeof metaPayload.prompt_number === \"number\" ? metaPayload.prompt_number : null;\n\t\tconst userPromptId =\n\t\t\ttypeof metaPayload.user_prompt_id === \"number\" ? metaPayload.user_prompt_id : null;\n\n\t\t// Resolve provenance fields\n\t\tconst provenance = this.resolveProvenance(metaPayload);\n\n\t\tconst rows = this.d\n\t\t\t.insert(schema.memoryItems)\n\t\t\t.values({\n\t\t\t\tsession_id: sessionId,\n\t\t\t\tkind: validKind,\n\t\t\t\ttitle,\n\t\t\t\tsubtitle,\n\t\t\t\tbody_text: bodyText,\n\t\t\t\tconfidence,\n\t\t\t\ttags_text: tagsText,\n\t\t\t\tactive: 1,\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t\tmetadata_json: toJson(metaPayload),\n\t\t\t\tactor_id: provenance.actor_id,\n\t\t\t\tactor_display_name: provenance.actor_display_name,\n\t\t\t\tvisibility: provenance.visibility,\n\t\t\t\tworkspace_id: provenance.workspace_id,\n\t\t\t\tworkspace_kind: provenance.workspace_kind,\n\t\t\t\torigin_device_id: provenance.origin_device_id,\n\t\t\t\torigin_source: provenance.origin_source,\n\t\t\t\ttrust_state: provenance.trust_state,\n\t\t\t\tnarrative,\n\t\t\t\tfacts: toJsonNullable(facts),\n\t\t\t\tconcepts: toJsonNullable(concepts),\n\t\t\t\tfiles_read: toJsonNullable(filesRead),\n\t\t\t\tfiles_modified: toJsonNullable(filesModified),\n\t\t\t\tprompt_number: promptNumber,\n\t\t\t\tuser_prompt_id: userPromptId,\n\t\t\t\tdeleted_at: null,\n\t\t\t\trev: 1,\n\t\t\t\timport_key: importKey,\n\t\t\t})\n\t\t\t.returning({ id: schema.memoryItems.id })\n\t\t\t.all();\n\n\t\tconst memoryId = rows[0]?.id;\n\t\tif (memoryId == null) throw new Error(\"memory insert returned no id\");\n\n\t\t// Record replication op for sync propagation\n\t\ttry {\n\t\t\trecordReplicationOp(this.db, { memoryId, opType: \"upsert\", deviceId: this.deviceId });\n\t\t} catch {\n\t\t\t// Non-fatal — don't block memory creation\n\t\t}\n\n\t\tthis.enqueueVectorWrite(memoryId, title, bodyText);\n\n\t\treturn memoryId;\n\t}\n\n\t// provenance resolution\n\n\t/**\n\t * Resolve provenance fields for a new memory, matching Python's\n\t * _resolve_memory_provenance. Uses metadata overrides when present,\n\t * falls back to store-level defaults.\n\t */\n\tprivate resolveProvenance(metadata: Record<string, unknown>): {\n\t\tactor_id: string | null;\n\t\tactor_display_name: string | null;\n\t\tvisibility: string;\n\t\tworkspace_id: string;\n\t\tworkspace_kind: string;\n\t\torigin_device_id: string;\n\t\torigin_source: string | null;\n\t\ttrust_state: string;\n\t} {\n\t\tconst clean = (v: unknown): string | null => {\n\t\t\tif (v == null) return null;\n\t\t\tconst s = String(v).trim();\n\t\t\treturn s.length > 0 ? s : null;\n\t\t};\n\n\t\tconst actorId = clean(metadata.actor_id) ?? this.actorId;\n\t\tconst actorDisplayName = clean(metadata.actor_display_name) ?? this.actorDisplayName;\n\n\t\tconst explicitWorkspaceKind = clean(metadata.workspace_kind);\n\t\tconst explicitWorkspaceId = clean(metadata.workspace_id);\n\n\t\t// Visibility defaults to \"shared\" (matches Python behavior)\n\t\tlet visibility = clean(metadata.visibility);\n\t\tif (!visibility || (visibility !== \"private\" && visibility !== \"shared\")) {\n\t\t\tif (explicitWorkspaceKind === \"shared\" || explicitWorkspaceId?.startsWith(\"shared:\")) {\n\t\t\t\tvisibility = \"shared\";\n\t\t\t} else {\n\t\t\t\tvisibility = \"shared\";\n\t\t\t}\n\t\t}\n\n\t\t// Workspace kind derives from visibility\n\t\tlet workspaceKind = explicitWorkspaceKind ?? \"shared\";\n\t\tif (workspaceKind !== \"personal\" && workspaceKind !== \"shared\") {\n\t\t\tworkspaceKind = visibility === \"shared\" ? \"shared\" : \"personal\";\n\t\t} else if (visibility === \"shared\") {\n\t\t\tworkspaceKind = \"shared\";\n\t\t} else if (visibility === \"private\") {\n\t\t\tworkspaceKind = \"personal\";\n\t\t}\n\n\t\t// Workspace ID with fallback — matches Python's _default_workspace_id\n\t\tconst workspaceId =\n\t\t\texplicitWorkspaceId ??\n\t\t\t(workspaceKind === \"personal\" ? `personal:${actorId}` : \"shared:default\");\n\n\t\tconst originDeviceId = clean(metadata.origin_device_id) ?? this.deviceId;\n\t\tconst originSource = clean(metadata.origin_source) ?? clean(metadata.source) ?? null;\n\t\tconst trustState = clean(metadata.trust_state) ?? \"trusted\";\n\n\t\treturn {\n\t\t\tactor_id: actorId,\n\t\t\tactor_display_name: actorDisplayName,\n\t\t\tvisibility,\n\t\t\tworkspace_id: workspaceId,\n\t\t\tworkspace_kind: workspaceKind,\n\t\t\torigin_device_id: originDeviceId,\n\t\t\torigin_source: originSource,\n\t\t\ttrust_state: trustState,\n\t\t};\n\t}\n\n\t// ownership check\n\n\t/**\n\t * Check if a memory item is owned by this actor/device.\n\t * Port of Python's memory_owned_by_self().\n\t *\n\t * Python checks:\n\t * 1. actor_id == self.actor_id → owned\n\t * 2. origin_device_id in claimed_same_actor_peers → owned\n\t * 3. actor_id in legacy sync actor ids → owned\n\t */\n\tmemoryOwnedBySelf(item: MemoryItem | MemoryResult | Record<string, unknown>): boolean {\n\t\tconst rec = item as Record<string, unknown>;\n\t\t// Check top-level columns first (MemoryItem / DB row),\n\t\t// then metadata dict (MemoryResult from search populates provenance there).\n\t\tconst meta = (rec.metadata ?? {}) as Record<string, unknown>;\n\n\t\tconst actorId = cleanStr(rec.actor_id) ?? cleanStr(meta.actor_id);\n\t\tif (actorId === this.actorId) return true;\n\n\t\tconst deviceId = cleanStr(rec.origin_device_id) ?? cleanStr(meta.origin_device_id);\n\t\tif (deviceId === this.deviceId) return true;\n\n\t\tconst claimedPeers = new Set(this.sameActorPeerIds());\n\t\tif (deviceId && claimedPeers.has(deviceId)) return true;\n\n\t\tconst legacyActorIds = new Set(this.claimedSameActorLegacyActorIds());\n\t\tif (actorId && legacyActorIds.has(actorId)) return true;\n\n\t\treturn false;\n\t}\n\n\t// forget\n\n\t/**\n\t * Soft-delete a memory item (set active = 0, record deleted_at).\n\t * Updates metadata_json with clock_device_id for replication tracing.\n\t * No-op if the memory doesn't exist.\n\t */\n\tforget(memoryId: number): void {\n\t\tthis.db\n\t\t\t.transaction(() => {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select({\n\t\t\t\t\t\trev: schema.memoryItems.rev,\n\t\t\t\t\t\tmetadata_json: schema.memoryItems.metadata_json,\n\t\t\t\t\t})\n\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.get();\n\t\t\t\tif (!row) return;\n\n\t\t\t\tconst meta = fromJson(row.metadata_json);\n\t\t\t\tmeta.clock_device_id = this.deviceId;\n\n\t\t\t\tconst now = nowIso();\n\t\t\t\tconst rev = (row.rev ?? 0) + 1;\n\n\t\t\t\tthis.d\n\t\t\t\t\t.update(schema.memoryItems)\n\t\t\t\t\t.set({\n\t\t\t\t\t\tactive: 0,\n\t\t\t\t\t\tdeleted_at: now,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\tmetadata_json: toJson(meta),\n\t\t\t\t\t\trev,\n\t\t\t\t\t})\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.run();\n\n\t\t\t\ttry {\n\t\t\t\t\trecordReplicationOp(this.db, { memoryId, opType: \"delete\", deviceId: this.deviceId });\n\t\t\t\t} catch {\n\t\t\t\t\t// Non-fatal\n\t\t\t\t}\n\t\t\t})\n\t\t\t.immediate();\n\t}\n\n\t// recent\n\n\t/**\n\t * Return recent active memories, newest first.\n\t * Supports optional filters via buildFilterClauses.\n\t */\n\trecent(limit = 10, filters?: MemoryFilters | null, offset = 0): MemoryItemResponse[] {\n\t\tconst baseClauses = [\"memory_items.active = 1\"];\n\t\tconst filterResult = buildFilterClauses(filters);\n\t\tconst allClauses = [...baseClauses, ...filterResult.clauses];\n\t\tconst whereSql = buildWhereSql(allClauses, filterResult.params);\n\n\t\t// Note: joinSessions is set by the project filter (not yet ported).\n\t\t// Once project filtering lands, it will trigger the sessions JOIN.\n\t\tconst fromSql = filterResult.joinSessions\n\t\t\t? sql.raw(\"memory_items JOIN sessions ON sessions.id = memory_items.session_id\")\n\t\t\t: sql.raw(\"memory_items\");\n\n\t\tconst rows = this.d.all<MemoryItem>(\n\t\t\tsql`SELECT memory_items.* FROM ${fromSql}\n\t\t\t\tWHERE ${whereSql}\n\t\t\t\tORDER BY created_at DESC\n\t\t\t\tLIMIT ${limit} OFFSET ${Math.max(offset, 0)}`,\n\t\t);\n\n\t\treturn rows.map((row) => parseMetadata(row));\n\t}\n\n\t// recentByKinds\n\n\t/**\n\t * Return recent active memories filtered to specific kinds, newest first.\n\t */\n\trecentByKinds(\n\t\tkinds: string[],\n\t\tlimit = 10,\n\t\tfilters?: MemoryFilters | null,\n\t\toffset = 0,\n\t): MemoryItemResponse[] {\n\t\tconst kindsList = kinds.filter((k) => k.length > 0);\n\t\tif (kindsList.length === 0) return [];\n\n\t\tconst kindPlaceholders = kindsList.map(() => \"?\").join(\", \");\n\t\tconst baseClauses = [\"memory_items.active = 1\", `memory_items.kind IN (${kindPlaceholders})`];\n\t\tconst filterResult = buildFilterClauses(filters);\n\t\tconst allClauses = [...baseClauses, ...filterResult.clauses];\n\t\tconst params = [...kindsList, ...filterResult.params];\n\t\tconst whereSql = buildWhereSql(allClauses, params);\n\n\t\tconst fromSql = filterResult.joinSessions\n\t\t\t? sql.raw(\"memory_items JOIN sessions ON sessions.id = memory_items.session_id\")\n\t\t\t: sql.raw(\"memory_items\");\n\n\t\tconst rows = this.d.all<MemoryItem>(\n\t\t\tsql`SELECT memory_items.* FROM ${fromSql}\n\t\t\t\tWHERE ${whereSql}\n\t\t\t\tORDER BY created_at DESC\n\t\t\t\tLIMIT ${limit} OFFSET ${Math.max(offset, 0)}`,\n\t\t);\n\n\t\treturn rows.map((row) => parseMetadata(row));\n\t}\n\n\t// stats\n\n\t/**\n\t * Return database statistics matching the Python stats() output shape.\n\t */\n\tstats(): StoreStats {\n\t\t// biome-ignore lint/suspicious/noExplicitAny: Drizzle table union type is unwieldy\n\t\tconst countRows = (tbl: any) =>\n\t\t\tthis.d.select({ c: sql<number>`COUNT(*)` }).from(tbl).get()?.c ?? 0;\n\n\t\tconst totalMemories = countRows(schema.memoryItems);\n\t\tconst activeMemories =\n\t\t\tthis.d\n\t\t\t\t.select({ c: sql<number>`COUNT(*)` })\n\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t.where(eq(schema.memoryItems.active, 1))\n\t\t\t\t.get()?.c ?? 0;\n\t\tconst sessions = countRows(schema.sessions);\n\t\tconst artifacts = countRows(schema.artifacts);\n\t\tconst rawEvents = countRows(schema.rawEvents);\n\n\t\tlet vectorCount = 0;\n\t\tif (!isEmbeddingDisabled() && tableExists(this.db, \"memory_vectors\")) {\n\t\t\ttry {\n\t\t\t\tconst row = this.d.get<{ c: number | null }>(sql`SELECT COUNT(*) AS c FROM memory_vectors`);\n\t\t\t\tvectorCount = row?.c ?? 0;\n\t\t\t} catch {\n\t\t\t\tvectorCount = 0;\n\t\t\t}\n\t\t}\n\t\tconst vectorCoverage = activeMemories > 0 ? Math.min(1, vectorCount / activeMemories) : 0;\n\n\t\tconst tagsFilled =\n\t\t\tthis.d\n\t\t\t\t.select({ c: sql<number>`COUNT(*)` })\n\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t.where(and(eq(schema.memoryItems.active, 1), sql`TRIM(tags_text) != ''`))\n\t\t\t\t.get()?.c ?? 0;\n\t\tconst tagsCoverage = activeMemories > 0 ? Math.min(1, tagsFilled / activeMemories) : 0;\n\n\t\tlet sizeBytes = 0;\n\t\ttry {\n\t\t\tsizeBytes = statSync(this.dbPath).size;\n\t\t} catch {\n\t\t\t// File may not exist yet or be inaccessible\n\t\t}\n\n\t\t// Usage stats\n\t\tconst usageRows = this.d\n\t\t\t.select({\n\t\t\t\tevent: schema.usageEvents.event,\n\t\t\t\tcount: sql<number>`COUNT(*)`,\n\t\t\t\ttokens_read: sql<number | null>`SUM(tokens_read)`,\n\t\t\t\ttokens_written: sql<number | null>`SUM(tokens_written)`,\n\t\t\t\ttokens_saved: sql<number | null>`SUM(COALESCE(tokens_saved, 0))`,\n\t\t\t})\n\t\t\t.from(schema.usageEvents)\n\t\t\t.groupBy(schema.usageEvents.event)\n\t\t\t.orderBy(desc(sql`COUNT(*)`))\n\t\t\t.all();\n\n\t\tconst usageEvents = usageRows.map((r) => ({\n\t\t\tevent: r.event,\n\t\t\tcount: r.count,\n\t\t\ttokens_read: r.tokens_read ?? 0,\n\t\t\ttokens_written: r.tokens_written ?? 0,\n\t\t\ttokens_saved: r.tokens_saved ?? 0,\n\t\t}));\n\n\t\tconst totalEvents = usageEvents.reduce((s, e) => s + e.count, 0);\n\t\tconst totalTokensRead = usageEvents.reduce((s, e) => s + e.tokens_read, 0);\n\t\tconst totalTokensWritten = usageEvents.reduce((s, e) => s + e.tokens_written, 0);\n\t\tconst totalTokensSaved = usageEvents.reduce((s, e) => s + e.tokens_saved, 0);\n\n\t\treturn {\n\t\t\tidentity: {\n\t\t\t\tdevice_id: this.deviceId,\n\t\t\t\tactor_id: this.actorId,\n\t\t\t\tactor_display_name: this.actorDisplayName,\n\t\t\t},\n\t\t\tdatabase: {\n\t\t\t\tpath: this.dbPath,\n\t\t\t\tsize_bytes: sizeBytes,\n\t\t\t\tsessions,\n\t\t\t\tmemory_items: totalMemories,\n\t\t\t\tactive_memory_items: activeMemories,\n\t\t\t\tartifacts,\n\t\t\t\tvector_rows: vectorCount,\n\t\t\t\tvector_coverage: vectorCoverage,\n\t\t\t\ttags_filled: tagsFilled,\n\t\t\t\ttags_coverage: tagsCoverage,\n\t\t\t\traw_events: rawEvents,\n\t\t\t},\n\t\t\tusage: {\n\t\t\t\tevents: usageEvents,\n\t\t\t\ttotals: {\n\t\t\t\t\tevents: totalEvents,\n\t\t\t\t\ttokens_read: totalTokensRead,\n\t\t\t\t\ttokens_written: totalTokensWritten,\n\t\t\t\t\ttokens_saved: totalTokensSaved,\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\t// updateMemoryVisibility\n\n\t/**\n\t * Update the visibility of an active memory item.\n\t * Throws if visibility is invalid, memory not found, memory is inactive,\n\t * or memory is not owned by this device/actor.\n\t */\n\tupdateMemoryVisibility(memoryId: number, visibility: string): MemoryItemResponse {\n\t\tconst cleaned = visibility.trim();\n\t\tif (cleaned !== \"private\" && cleaned !== \"shared\") {\n\t\t\tthrow new Error(\"visibility must be private or shared\");\n\t\t}\n\n\t\treturn this.db\n\t\t\t.transaction(() => {\n\t\t\t\tconst row = this.d\n\t\t\t\t\t.select()\n\t\t\t\t\t.from(schema.memoryItems)\n\t\t\t\t\t.where(and(eq(schema.memoryItems.id, memoryId), eq(schema.memoryItems.active, 1)))\n\t\t\t\t\t.get() as MemoryItem | undefined;\n\t\t\t\tif (!row) {\n\t\t\t\t\tthrow new Error(\"memory not found\");\n\t\t\t\t}\n\n\t\t\t\tif (!this.memoryOwnedBySelf(row)) {\n\t\t\t\t\tthrow new Error(\"memory not owned by this device\");\n\t\t\t\t}\n\n\t\t\t\tconst rowActorId = cleanStr(row.actor_id) ?? this.actorId;\n\t\t\t\tconst workspaceKind = cleaned === \"shared\" ? \"shared\" : \"personal\";\n\t\t\t\tconst workspaceId =\n\t\t\t\t\tcleaned === \"shared\" && row.workspace_id?.startsWith(\"shared:\")\n\t\t\t\t\t\t? row.workspace_id\n\t\t\t\t\t\t: workspaceKind === \"personal\"\n\t\t\t\t\t\t\t? `personal:${rowActorId}`\n\t\t\t\t\t\t\t: \"shared:default\";\n\n\t\t\t\tconst meta = fromJson(row.metadata_json);\n\t\t\t\tmeta.visibility = cleaned;\n\t\t\t\tmeta.workspace_kind = workspaceKind;\n\t\t\t\tmeta.workspace_id = workspaceId;\n\t\t\t\tmeta.clock_device_id = this.deviceId;\n\n\t\t\t\tconst now = nowIso();\n\t\t\t\tconst rev = (row.rev ?? 0) + 1;\n\n\t\t\t\tthis.d\n\t\t\t\t\t.update(schema.memoryItems)\n\t\t\t\t\t.set({\n\t\t\t\t\t\tvisibility: cleaned,\n\t\t\t\t\t\tworkspace_kind: workspaceKind,\n\t\t\t\t\t\tworkspace_id: workspaceId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t\tmetadata_json: toJson(meta),\n\t\t\t\t\t\trev,\n\t\t\t\t\t})\n\t\t\t\t\t.where(eq(schema.memoryItems.id, memoryId))\n\t\t\t\t\t.run();\n\n\t\t\t\ttry {\n\t\t\t\t\trecordReplicationOp(this.db, {\n\t\t\t\t\t\tmemoryId,\n\t\t\t\t\t\topType: \"upsert\",\n\t\t\t\t\t\tdeviceId: this.deviceId,\n\t\t\t\t\t});\n\t\t\t\t} catch {\n\t\t\t\t\t// Non-fatal\n\t\t\t\t}\n\n\t\t\t\tconst updated = this.get(memoryId);\n\t\t\t\tif (!updated) {\n\t\t\t\t\tthrow new Error(\"memory not found after update\");\n\t\t\t\t}\n\t\t\t\treturn updated;\n\t\t\t})\n\t\t\t.immediate();\n\t}\n\n\t// search\n\n\t/**\n\t * Full-text search for memories using FTS5.\n\t *\n\t * Delegates to search.ts to keep the search logic decoupled.\n\t * Results are ranked by BM25 score, recency, and kind bonus.\n\t */\n\tsearch(query: string, limit = 10, filters?: MemoryFilters): MemoryResult[] {\n\t\treturn searchFn(this, query, limit, filters);\n\t}\n\n\t// timeline\n\n\t/**\n\t * Return a chronological window of memories around an anchor.\n\t *\n\t * Finds an anchor by memoryId or query, then fetches neighbors\n\t * in the same session. Delegates to search.ts.\n\t */\n\ttimeline(\n\t\tquery?: string | null,\n\t\tmemoryId?: number | null,\n\t\tdepthBefore = 3,\n\t\tdepthAfter = 3,\n\t\tfilters?: MemoryFilters | null,\n\t): TimelineItemResponse[] {\n\t\treturn timelineFn(this, query, memoryId, depthBefore, depthAfter, filters);\n\t}\n\n\t// explain\n\n\t/**\n\t * Explain search results with scoring breakdown.\n\t *\n\t * Returns detailed scoring components for each result, merging\n\t * query-based and ID-based lookups. Delegates to search.ts.\n\t */\n\texplain(\n\t\tquery?: string | null,\n\t\tids?: unknown[] | null,\n\t\tlimit = 10,\n\t\tfilters?: MemoryFilters | null,\n\t\toptions?: ExplainOptions,\n\t): ExplainResponse {\n\t\treturn explainFn(this, query, ids, limit, filters, options);\n\t}\n\n\t// buildMemoryPack\n\n\t/**\n\t * Build a formatted memory pack from search results.\n\t *\n\t * Categorizes memories into summary/timeline/observations sections,\n\t * with optional token budgeting. Delegates to pack.ts.\n\t */\n\tbuildMemoryPack(\n\t\tcontext: string,\n\t\tlimit?: number,\n\t\ttokenBudget?: number | null,\n\t\tfilters?: MemoryFilters,\n\t): PackResponse {\n\t\treturn buildMemoryPack(this, context, limit, tokenBudget ?? null, filters);\n\t}\n\n\t/**\n\t * Build a memory pack with semantic candidate merging.\n\t *\n\t * Async version that runs vector KNN search via sqlite-vec and merges\n\t * semantic candidates with FTS results. Falls back to FTS-only when\n\t * embeddings are disabled or unavailable.\n\t */\n\tasync buildMemoryPackAsync(\n\t\tcontext: string,\n\t\tlimit?: number,\n\t\ttokenBudget?: number | null,\n\t\tfilters?: MemoryFilters,\n\t): Promise<PackResponse> {\n\t\treturn buildMemoryPackAsync(this, context, limit, tokenBudget ?? null, filters);\n\t}\n\n\t// Raw event helpers\n\n\t/**\n\t * Normalize source/streamId to match Python's _normalize_stream_identity().\n\t * Trims whitespace, lowercases source, defaults to \"opencode\".\n\t */\n\tprivate normalizeStreamIdentity(source: string, streamId: string): [string, string] {\n\t\tconst s = source.trim().toLowerCase() || \"opencode\";\n\t\tconst sid = streamId.trim();\n\t\tif (!sid) throw new Error(\"stream_id is required\");\n\t\treturn [s, sid];\n\t}\n\n\t// Raw event query methods (ports from codemem/store/raw_events.py)\n\n\t/**\n\t * Find sessions that have unflushed events and have been idle long enough.\n\t * Port of raw_event_sessions_pending_idle_flush().\n\t */\n\trawEventSessionsPendingIdleFlush(\n\t\tidleBeforeTsWallMs: number,\n\t\tlimit = 25,\n\t): { source: string; streamId: string }[] {\n\t\tconst maxEvents = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventSessions.source,\n\t\t\t\tstream_id: schema.rawEventSessions.stream_id,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, schema.rawEventSessions.source),\n\t\t\t\t\teq(maxEvents.stream_id, schema.rawEventSessions.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNotNull(schema.rawEventSessions.last_seen_ts_wall_ms),\n\t\t\t\t\tlte(schema.rawEventSessions.last_seen_ts_wall_ms, idleBeforeTsWallMs),\n\t\t\t\t\tgt(maxEvents.max_seq, schema.rawEventSessions.last_flushed_event_seq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.rawEventSessions.last_seen_ts_wall_ms)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\treturn rows\n\t\t\t.filter((row) => row.stream_id)\n\t\t\t.map((row) => ({\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstreamId: String(row.stream_id ?? \"\"),\n\t\t\t}));\n\t}\n\n\t/**\n\t * Find sessions that have pending/failed flush batches with unflushed events.\n\t * Port of raw_event_sessions_with_pending_queue().\n\t */\n\trawEventSessionsWithPendingQueue(limit = 25): { source: string; streamId: string }[] {\n\t\tconst pendingBatches = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEventFlushBatches.source,\n\t\t\t\tstream_id: schema.rawEventFlushBatches.stream_id,\n\t\t\t\toldest_pending_update: sql<string>`MIN(${schema.rawEventFlushBatches.updated_at})`.as(\n\t\t\t\t\t\"oldest_pending_update\",\n\t\t\t\t),\n\t\t\t})\n\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t.where(inArray(schema.rawEventFlushBatches.status, [\"pending\", \"failed\", \"started\", \"error\"]))\n\t\t\t.groupBy(schema.rawEventFlushBatches.source, schema.rawEventFlushBatches.stream_id)\n\t\t\t.as(\"pending_batches\");\n\n\t\tconst maxEvents = this.d\n\t\t\t.select({\n\t\t\t\tsource: schema.rawEvents.source,\n\t\t\t\tstream_id: schema.rawEvents.stream_id,\n\t\t\t\tmax_seq: sql<number>`MAX(${schema.rawEvents.event_seq})`.as(\"max_seq\"),\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.groupBy(schema.rawEvents.source, schema.rawEvents.stream_id)\n\t\t\t.as(\"max_events\");\n\n\t\tconst rows = this.d\n\t\t\t.select({ source: pendingBatches.source, stream_id: pendingBatches.stream_id })\n\t\t\t.from(pendingBatches)\n\t\t\t.innerJoin(\n\t\t\t\tmaxEvents,\n\t\t\t\tand(\n\t\t\t\t\teq(maxEvents.source, pendingBatches.source),\n\t\t\t\t\teq(maxEvents.stream_id, pendingBatches.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.leftJoin(\n\t\t\t\tschema.rawEventSessions,\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventSessions.source, pendingBatches.source),\n\t\t\t\t\teq(schema.rawEventSessions.stream_id, pendingBatches.stream_id),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.where(\n\t\t\t\tgt(maxEvents.max_seq, sql`COALESCE(${schema.rawEventSessions.last_flushed_event_seq}, -1)`),\n\t\t\t)\n\t\t\t.orderBy(sql`${pendingBatches.oldest_pending_update}`)\n\t\t\t.limit(limit)\n\t\t\t.all();\n\n\t\treturn rows\n\t\t\t.filter((row) => row.stream_id)\n\t\t\t.map((row) => ({\n\t\t\t\tsource: String(row.source ?? \"opencode\"),\n\t\t\t\tstreamId: String(row.stream_id ?? \"\"),\n\t\t\t}));\n\t}\n\n\t/**\n\t * Delete raw events older than max_age_ms. Returns count of deleted raw_events rows.\n\t * Port of purge_raw_events() + purge_raw_events_before().\n\t */\n\tpurgeRawEvents(maxAgeMs: number): number {\n\t\tif (maxAgeMs <= 0) return 0;\n\t\tconst nowMs = Date.now();\n\t\tconst cutoffTsWallMs = nowMs - maxAgeMs;\n\t\tconst cutoffIso = new Date(cutoffTsWallMs).toISOString();\n\n\t\treturn this.db.transaction(() => {\n\t\t\tthis.d\n\t\t\t\t.delete(schema.rawEventIngestSamples)\n\t\t\t\t.where(sql`${schema.rawEventIngestSamples.created_at} < ${cutoffIso}`)\n\t\t\t\t.run();\n\t\t\tconst result = this.d\n\t\t\t\t.delete(schema.rawEvents)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\tisNotNull(schema.rawEvents.ts_wall_ms),\n\t\t\t\t\t\tlt(schema.rawEvents.ts_wall_ms, cutoffTsWallMs),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.run();\n\t\t\treturn Number(result.changes ?? 0);\n\t\t})();\n\t}\n\n\t/**\n\t * Mark stuck flush batches (started/running/pending/claimed) as failed.\n\t * Port of mark_stuck_raw_event_batches_as_error().\n\t */\n\tmarkStuckRawEventBatchesAsError(olderThanIso: string, limit = 100): number {\n\t\tconst now = new Date().toISOString();\n\t\tconst result = this.db\n\t\t\t.prepare(\n\t\t\t\t`WITH candidates AS (\n SELECT id\n FROM raw_event_flush_batches\n WHERE status IN ('started', 'running', ?, ?) AND updated_at < ?\n ORDER BY updated_at\n LIMIT ?\n )\n UPDATE raw_event_flush_batches\n SET status = ?,\n updated_at = ?,\n error_message = 'Flush retry timed out.',\n error_type = 'RawEventBatchStuck',\n observer_provider = NULL,\n observer_model = NULL,\n observer_runtime = NULL\n WHERE id IN (SELECT id FROM candidates)`,\n\t\t\t)\n\t\t\t.run(\n\t\t\t\t\"pending\", // RAW_EVENT_QUEUE_PENDING\n\t\t\t\t\"claimed\", // RAW_EVENT_QUEUE_CLAIMED\n\t\t\t\tolderThanIso,\n\t\t\t\tlimit,\n\t\t\t\t\"failed\", // RAW_EVENT_QUEUE_FAILED\n\t\t\t\tnow,\n\t\t\t);\n\n\t\treturn result.changes;\n\t}\n\n\t// Raw event per-session methods (ports for flush pipeline)\n\n\t/**\n\t * Get session metadata (cwd, project, started_at, etc.) for a raw event stream.\n\t * Port of raw_event_session_meta().\n\t */\n\trawEventSessionMeta(opencodeSessionId: string, source = \"opencode\"): Record<string, unknown> {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tcwd: schema.rawEventSessions.cwd,\n\t\t\t\tproject: schema.rawEventSessions.project,\n\t\t\t\tstarted_at: schema.rawEventSessions.started_at,\n\t\t\t\tlast_seen_ts_wall_ms: schema.rawEventSessions.last_seen_ts_wall_ms,\n\t\t\t\tlast_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq,\n\t\t\t})\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.where(and(eq(schema.rawEventSessions.source, s), eq(schema.rawEventSessions.stream_id, sid)))\n\t\t\t.get();\n\t\tif (!row) return {};\n\t\treturn {\n\t\t\tcwd: row.cwd,\n\t\t\tproject: row.project,\n\t\t\tstarted_at: row.started_at,\n\t\t\tlast_seen_ts_wall_ms: row.last_seen_ts_wall_ms,\n\t\t\tlast_flushed_event_seq: row.last_flushed_event_seq,\n\t\t};\n\t}\n\n\t/**\n\t * Get the last flushed event_seq for a session. Returns -1 if no state.\n\t * Port of raw_event_flush_state().\n\t */\n\trawEventFlushState(opencodeSessionId: string, source = \"opencode\"): number {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst row = this.d\n\t\t\t.select({ last_flushed_event_seq: schema.rawEventSessions.last_flushed_event_seq })\n\t\t\t.from(schema.rawEventSessions)\n\t\t\t.where(and(eq(schema.rawEventSessions.source, s), eq(schema.rawEventSessions.stream_id, sid)))\n\t\t\t.get();\n\t\tif (!row) return -1;\n\t\treturn Number(row.last_flushed_event_seq);\n\t}\n\n\t/**\n\t * Get raw events after a given event_seq, ordered by event_seq ASC.\n\t * Returns enriched event objects with type, timestamps, event_seq, event_id.\n\t * Port of raw_events_since_by_seq().\n\t */\n\trawEventsSinceBySeq(\n\t\topencodeSessionId: string,\n\t\tsource = \"opencode\",\n\t\tafterEventSeq = -1,\n\t\tlimit?: number | null,\n\t): Record<string, unknown>[] {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst baseQuery = this.d\n\t\t\t.select({\n\t\t\t\tevent_seq: schema.rawEvents.event_seq,\n\t\t\t\tevent_type: schema.rawEvents.event_type,\n\t\t\t\tts_wall_ms: schema.rawEvents.ts_wall_ms,\n\t\t\t\tts_mono_ms: schema.rawEvents.ts_mono_ms,\n\t\t\t\tpayload_json: schema.rawEvents.payload_json,\n\t\t\t\tevent_id: schema.rawEvents.event_id,\n\t\t\t})\n\t\t\t.from(schema.rawEvents)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEvents.source, s),\n\t\t\t\t\teq(schema.rawEvents.stream_id, sid),\n\t\t\t\t\tgt(schema.rawEvents.event_seq, afterEventSeq),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.rawEvents.event_seq);\n\n\t\tconst rows = limit != null && limit > 0 ? baseQuery.limit(limit).all() : baseQuery.all();\n\n\t\treturn rows.map((row) => {\n\t\t\tconst payload = fromJson(row.payload_json) as Record<string, unknown>;\n\t\t\t// Use || (not ??) to match Python's `or` semantics — empty string falls through\n\t\t\tpayload.type = payload.type || row.event_type;\n\t\t\tpayload.timestamp_wall_ms = row.ts_wall_ms;\n\t\t\tpayload.timestamp_mono_ms = row.ts_mono_ms;\n\t\t\tpayload.event_seq = row.event_seq;\n\t\t\tpayload.event_id = row.event_id;\n\t\t\treturn payload;\n\t\t});\n\t}\n\n\t/**\n\t * Get or create a flush batch record. Returns [batchId, status].\n\t * Port of get_or_create_raw_event_flush_batch().\n\t */\n\tgetOrCreateRawEventFlushBatch(\n\t\topencodeSessionId: string,\n\t\tsource: string,\n\t\tstartEventSeq: number,\n\t\tendEventSeq: number,\n\t\textractorVersion: string,\n\t): { batchId: number; status: string } {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst now = new Date().toISOString();\n\n\t\t// Atomic UPSERT to avoid SELECT+INSERT races. We intentionally do NOT\n\t\t// heartbeat claimed/running batches: their updated_at stays unchanged so\n\t\t// stuck-batch recovery can still age them out.\n\t\tconst t = schema.rawEventFlushBatches;\n\t\tconst row = this.d\n\t\t\t.insert(t)\n\t\t\t.values({\n\t\t\t\tsource: s,\n\t\t\t\tstream_id: sid,\n\t\t\t\topencode_session_id: sid,\n\t\t\t\tstart_event_seq: startEventSeq,\n\t\t\t\tend_event_seq: endEventSeq,\n\t\t\t\textractor_version: extractorVersion,\n\t\t\t\tstatus: \"pending\",\n\t\t\t\tcreated_at: now,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [t.source, t.stream_id, t.start_event_seq, t.end_event_seq, t.extractor_version],\n\t\t\t\tset: {\n\t\t\t\t\tupdated_at: sql`CASE\n\t\t\t\t\t\tWHEN ${t.status} IN ('claimed', 'running')\n\t\t\t\t\t\tTHEN ${t.updated_at}\n\t\t\t\t\t\tELSE excluded.updated_at\n\t\t\t\t\tEND`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.returning({ id: t.id, status: t.status })\n\t\t\t.get();\n\n\t\tif (!row) throw new Error(\"Failed to create flush batch\");\n\t\t// Canonicalize legacy DB statuses to match Python's _RAW_EVENT_QUEUE_DB_TO_CANONICAL\n\t\tconst rawStatus = String(row.status);\n\t\tconst canonicalStatus =\n\t\t\trawStatus === \"started\"\n\t\t\t\t? \"pending\"\n\t\t\t\t: rawStatus === \"running\"\n\t\t\t\t\t? \"claimed\"\n\t\t\t\t\t: rawStatus === \"error\"\n\t\t\t\t\t\t? \"failed\"\n\t\t\t\t\t\t: rawStatus;\n\t\treturn { batchId: Number(row.id), status: canonicalStatus };\n\t}\n\n\t/**\n\t * Attempt to claim a flush batch for processing.\n\t * Returns true if successfully claimed, false if already claimed/completed.\n\t * Port of claim_raw_event_flush_batch().\n\t */\n\tclaimRawEventFlushBatch(batchId: number): boolean {\n\t\tconst now = new Date().toISOString();\n\t\tconst row = this.d\n\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t.set({\n\t\t\t\tstatus: \"claimed\",\n\t\t\t\tupdated_at: now,\n\t\t\t\tattempt_count: sql`${schema.rawEventFlushBatches.attempt_count} + 1`,\n\t\t\t})\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.rawEventFlushBatches.id, batchId),\n\t\t\t\t\tinArray(schema.rawEventFlushBatches.status, [\"pending\", \"failed\", \"started\", \"error\"]),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.returning({ id: schema.rawEventFlushBatches.id })\n\t\t\t.get();\n\t\treturn row != null;\n\t}\n\n\t/**\n\t * Update the status of a flush batch.\n\t * Port of update_raw_event_flush_batch_status().\n\t */\n\tupdateRawEventFlushBatchStatus(batchId: number, status: string): void {\n\t\tconst now = new Date().toISOString();\n\t\tif (status === \"failed\") {\n\t\t\t// Preserve existing error details when marking as failed\n\t\t\tthis.d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({ status, updated_at: now })\n\t\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t\t.run();\n\t\t} else {\n\t\t\t// Clear error details for non-failure statuses\n\t\t\tthis.d\n\t\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t\t.set({\n\t\t\t\t\tstatus,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t\terror_message: null,\n\t\t\t\t\terror_type: null,\n\t\t\t\t\tobserver_provider: null,\n\t\t\t\t\tobserver_model: null,\n\t\t\t\t\tobserver_runtime: null,\n\t\t\t\t})\n\t\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t\t.run();\n\t\t}\n\t}\n\n\t/**\n\t * Record a flush batch failure with error details.\n\t * Port of record_raw_event_flush_batch_failure().\n\t */\n\trecordRawEventFlushBatchFailure(\n\t\tbatchId: number,\n\t\topts: {\n\t\t\tmessage: string;\n\t\t\terrorType: string;\n\t\t\tobserverProvider?: string | null;\n\t\t\tobserverModel?: string | null;\n\t\t\tobserverRuntime?: string | null;\n\t\t},\n\t): void {\n\t\tconst now = new Date().toISOString();\n\t\tthis.d\n\t\t\t.update(schema.rawEventFlushBatches)\n\t\t\t.set({\n\t\t\t\tstatus: \"failed\",\n\t\t\t\tupdated_at: now,\n\t\t\t\terror_message: opts.message,\n\t\t\t\terror_type: opts.errorType,\n\t\t\t\tobserver_provider: opts.observerProvider ?? null,\n\t\t\t\tobserver_model: opts.observerModel ?? null,\n\t\t\t\tobserver_runtime: opts.observerRuntime ?? null,\n\t\t\t})\n\t\t\t.where(eq(schema.rawEventFlushBatches.id, batchId))\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Update the last flushed event_seq for a session.\n\t * Port of update_raw_event_flush_state().\n\t */\n\tupdateRawEventFlushState(\n\t\topencodeSessionId: string,\n\t\tlastFlushed: number,\n\t\tsource = \"opencode\",\n\t): void {\n\t\tconst [s, sid] = this.normalizeStreamIdentity(source, opencodeSessionId);\n\t\tconst now = new Date().toISOString();\n\t\tthis.d\n\t\t\t.insert(schema.rawEventSessions)\n\t\t\t.values({\n\t\t\t\topencode_session_id: sid,\n\t\t\t\tsource: s,\n\t\t\t\tstream_id: sid,\n\t\t\t\tlast_flushed_event_seq: lastFlushed,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [schema.rawEventSessions.source, schema.rawEventSessions.stream_id],\n\t\t\t\tset: {\n\t\t\t\t\topencode_session_id: sql`excluded.opencode_session_id`,\n\t\t\t\t\tlast_flushed_event_seq: sql`excluded.last_flushed_event_seq`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t// Raw event ingestion methods (ports for POST /api/raw-events)\n\n\t/**\n\t * Update ingest stats counters (sample + running totals).\n\t * Port of _update_raw_event_ingest_stats().\n\t */\n\tprivate updateRawEventIngestStats(\n\t\tinserted: number,\n\t\tskippedInvalid: number,\n\t\tskippedDuplicate: number,\n\t\tskippedConflict: number,\n\t): void {\n\t\tconst now = nowIso();\n\t\tconst skippedEvents = skippedInvalid + skippedDuplicate + skippedConflict;\n\t\tthis.d\n\t\t\t.insert(schema.rawEventIngestSamples)\n\t\t\t.values({\n\t\t\t\tcreated_at: now,\n\t\t\t\tinserted_events: inserted,\n\t\t\t\tskipped_invalid: skippedInvalid,\n\t\t\t\tskipped_duplicate: skippedDuplicate,\n\t\t\t\tskipped_conflict: skippedConflict,\n\t\t\t})\n\t\t\t.run();\n\t\tthis.d\n\t\t\t.insert(schema.rawEventIngestStats)\n\t\t\t.values({\n\t\t\t\tid: 1,\n\t\t\t\tinserted_events: inserted,\n\t\t\t\tskipped_events: skippedEvents,\n\t\t\t\tskipped_invalid: skippedInvalid,\n\t\t\t\tskipped_duplicate: skippedDuplicate,\n\t\t\t\tskipped_conflict: skippedConflict,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: schema.rawEventIngestStats.id,\n\t\t\t\tset: {\n\t\t\t\t\tinserted_events: sql`${schema.rawEventIngestStats.inserted_events} + excluded.inserted_events`,\n\t\t\t\t\tskipped_events: sql`${schema.rawEventIngestStats.skipped_events} + excluded.skipped_events`,\n\t\t\t\t\tskipped_invalid: sql`${schema.rawEventIngestStats.skipped_invalid} + excluded.skipped_invalid`,\n\t\t\t\t\tskipped_duplicate: sql`${schema.rawEventIngestStats.skipped_duplicate} + excluded.skipped_duplicate`,\n\t\t\t\t\tskipped_conflict: sql`${schema.rawEventIngestStats.skipped_conflict} + excluded.skipped_conflict`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Record a single raw event. Returns true if inserted, false if duplicate.\n\t * Port of record_raw_event().\n\t */\n\trecordRawEvent(opts: {\n\t\topencodeSessionId: string;\n\t\tsource?: string;\n\t\teventId: string;\n\t\teventType: string;\n\t\tpayload: Record<string, unknown>;\n\t\ttsWallMs?: number | null;\n\t\ttsMonoMs?: number | null;\n\t}): boolean {\n\t\tif (!opts.opencodeSessionId.trim()) throw new Error(\"opencode_session_id is required\");\n\t\tif (!opts.eventId.trim()) throw new Error(\"event_id is required\");\n\t\tif (!opts.eventType.trim()) throw new Error(\"event_type is required\");\n\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\n\t\t\topts.source ?? \"opencode\",\n\t\t\topts.opencodeSessionId,\n\t\t);\n\n\t\treturn this.db.transaction(() => {\n\t\t\tconst now = nowIso();\n\n\t\t\t// Check for duplicate\n\t\t\tconst existing = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEvents.source, source),\n\t\t\t\t\t\teq(schema.rawEvents.stream_id, streamId),\n\t\t\t\t\t\teq(schema.rawEvents.event_id, opts.eventId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (existing != null) {\n\t\t\t\tthis.updateRawEventIngestStats(0, 0, 1, 0);\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\t// Ensure session row exists\n\t\t\tconst sessionRow = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (sessionRow == null) {\n\t\t\t\tthis.d\n\t\t\t\t\t.insert(schema.rawEventSessions)\n\t\t\t\t\t.values({\n\t\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\n\t\t\t// Allocate event_seq\n\t\t\tconst seqRow = this.d\n\t\t\t\t.update(schema.rawEventSessions)\n\t\t\t\t.set({\n\t\t\t\t\tlast_received_event_seq: sql`${schema.rawEventSessions.last_received_event_seq} + 1`,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.returning({\n\t\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\t})\n\t\t\t\t.get();\n\t\t\tif (!seqRow) throw new Error(\"Failed to allocate raw event seq\");\n\t\t\tconst eventSeq = Number(seqRow.last_received_event_seq);\n\n\t\t\tthis.d\n\t\t\t\t.insert(schema.rawEvents)\n\t\t\t\t.values({\n\t\t\t\t\tsource,\n\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\tevent_id: opts.eventId,\n\t\t\t\t\tevent_seq: eventSeq,\n\t\t\t\t\tevent_type: opts.eventType,\n\t\t\t\t\tts_wall_ms: opts.tsWallMs ?? null,\n\t\t\t\t\tts_mono_ms: opts.tsMonoMs ?? null,\n\t\t\t\t\tpayload_json: toJson(opts.payload),\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t\tthis.updateRawEventIngestStats(1, 0, 0, 0);\n\t\t\treturn true;\n\t\t})();\n\t}\n\n\t/**\n\t * Record a batch of raw events for a single session. Returns { inserted, skipped }.\n\t * Port of record_raw_events_batch().\n\t */\n\trecordRawEventsBatch(\n\t\topencodeSessionId: string,\n\t\tevents: Record<string, unknown>[],\n\t): { inserted: number; skipped: number } {\n\t\tif (!opencodeSessionId.trim()) throw new Error(\"opencode_session_id is required\");\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\"opencode\", opencodeSessionId);\n\n\t\treturn this.db.transaction(() => {\n\t\t\tconst now = nowIso();\n\n\t\t\t// Ensure session row exists\n\t\t\tconst sessionRow = this.d\n\t\t\t\t.select({ one: sql<number>`1` })\n\t\t\t\t.from(schema.rawEventSessions)\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.get();\n\t\t\tif (sessionRow == null) {\n\t\t\t\tthis.d\n\t\t\t\t\t.insert(schema.rawEventSessions)\n\t\t\t\t\t.values({\n\t\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\t\tsource,\n\t\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\t\tupdated_at: now,\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\n\t\t\t// Normalize and validate events\n\t\t\tlet skippedInvalid = 0;\n\t\t\tlet skippedDuplicate = 0;\n\t\t\tlet skippedConflict = 0;\n\n\t\t\tinterface NormalizedEvent {\n\t\t\t\teventId: string;\n\t\t\t\teventType: string;\n\t\t\t\tpayload: Record<string, unknown>;\n\t\t\t\ttsWallMs: unknown;\n\t\t\t\ttsMonoMs: unknown;\n\t\t\t}\n\t\t\tconst normalized: NormalizedEvent[] = [];\n\t\t\tconst seenIds = new Set<string>();\n\n\t\t\tfor (const event of events) {\n\t\t\t\tconst eventId = String(event.event_id ?? \"\");\n\t\t\t\tconst eventType = String(event.event_type ?? \"\");\n\t\t\t\tlet payload = event.payload;\n\t\t\t\tif (payload == null || typeof payload !== \"object\" || Array.isArray(payload)) {\n\t\t\t\t\tpayload = {};\n\t\t\t\t}\n\t\t\t\tconst tsWallMs = event.ts_wall_ms;\n\t\t\t\tconst tsMonoMs = event.ts_mono_ms;\n\n\t\t\t\tif (!eventId || !eventType) {\n\t\t\t\t\tskippedInvalid++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (seenIds.has(eventId)) {\n\t\t\t\t\tskippedDuplicate++;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenIds.add(eventId);\n\t\t\t\tnormalized.push({\n\t\t\t\t\teventId,\n\t\t\t\t\teventType,\n\t\t\t\t\tpayload: payload as Record<string, unknown>,\n\t\t\t\t\ttsWallMs,\n\t\t\t\t\ttsMonoMs,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (normalized.length === 0) {\n\t\t\t\tthis.updateRawEventIngestStats(0, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\t\treturn { inserted: 0, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t\t}\n\n\t\t\t// Check for existing event_ids in chunks\n\t\t\tconst existingIds = new Set<string>();\n\t\t\tconst chunkSize = 500;\n\t\t\tfor (let i = 0; i < normalized.length; i += chunkSize) {\n\t\t\t\tconst chunk = normalized.slice(i, i + chunkSize);\n\t\t\t\tconst chunkEventIds = chunk.map((e) => e.eventId);\n\t\t\t\tconst rows = this.d\n\t\t\t\t\t.select({ event_id: schema.rawEvents.event_id })\n\t\t\t\t\t.from(schema.rawEvents)\n\t\t\t\t\t.where(\n\t\t\t\t\t\tand(\n\t\t\t\t\t\t\teq(schema.rawEvents.source, source),\n\t\t\t\t\t\t\teq(schema.rawEvents.stream_id, streamId),\n\t\t\t\t\t\t\tinArray(schema.rawEvents.event_id, chunkEventIds),\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t\t.all();\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tif (row.event_id) existingIds.add(row.event_id);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst newEvents = normalized.filter((e) => !existingIds.has(e.eventId));\n\t\t\tskippedDuplicate += normalized.length - newEvents.length;\n\n\t\t\tif (newEvents.length === 0) {\n\t\t\t\tthis.updateRawEventIngestStats(0, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\t\treturn { inserted: 0, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t\t}\n\n\t\t\t// Allocate seq range\n\t\t\tconst seqRow = this.d\n\t\t\t\t.update(schema.rawEventSessions)\n\t\t\t\t.set({\n\t\t\t\t\tlast_received_event_seq: sql`${schema.rawEventSessions.last_received_event_seq} + ${newEvents.length}`,\n\t\t\t\t\tupdated_at: now,\n\t\t\t\t})\n\t\t\t\t.where(\n\t\t\t\t\tand(\n\t\t\t\t\t\teq(schema.rawEventSessions.source, source),\n\t\t\t\t\t\teq(schema.rawEventSessions.stream_id, streamId),\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\t.returning({\n\t\t\t\t\tlast_received_event_seq: schema.rawEventSessions.last_received_event_seq,\n\t\t\t\t})\n\t\t\t\t.get();\n\t\t\tif (!seqRow) throw new Error(\"Failed to allocate raw event seq\");\n\t\t\tconst endSeq = Number(seqRow.last_received_event_seq);\n\t\t\tconst startSeq = endSeq - newEvents.length + 1;\n\n\t\t\tconst insertRows = newEvents.map((event, offset) => {\n\t\t\t\tconst tsWallMs = typeof event.tsWallMs === \"number\" ? event.tsWallMs : null;\n\t\t\t\tconst tsMonoMs = typeof event.tsMonoMs === \"number\" ? event.tsMonoMs : null;\n\t\t\t\treturn {\n\t\t\t\t\tsource,\n\t\t\t\t\tstream_id: streamId,\n\t\t\t\t\topencode_session_id: streamId,\n\t\t\t\t\tevent_id: event.eventId,\n\t\t\t\t\tevent_seq: startSeq + offset,\n\t\t\t\t\tevent_type: event.eventType,\n\t\t\t\t\tts_wall_ms: tsWallMs,\n\t\t\t\t\tts_mono_ms: tsMonoMs,\n\t\t\t\t\tpayload_json: toJson(event.payload),\n\t\t\t\t\tcreated_at: now,\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tconst result = this.d.insert(schema.rawEvents).values(insertRows).onConflictDoNothing().run();\n\t\t\tconst inserted = Number(result.changes ?? 0);\n\t\t\tskippedConflict += newEvents.length - inserted;\n\n\t\t\tthis.updateRawEventIngestStats(inserted, skippedInvalid, skippedDuplicate, skippedConflict);\n\t\t\treturn { inserted, skipped: skippedInvalid + skippedDuplicate + skippedConflict };\n\t\t})();\n\t}\n\n\t/**\n\t * UPSERT session metadata (cwd, project, started_at, last_seen_ts_wall_ms).\n\t * Port of update_raw_event_session_meta().\n\t */\n\tupdateRawEventSessionMeta(opts: {\n\t\topencodeSessionId: string;\n\t\tsource?: string;\n\t\tcwd?: string | null;\n\t\tproject?: string | null;\n\t\tstartedAt?: string | null;\n\t\tlastSeenTsWallMs?: number | null;\n\t}): void {\n\t\tconst [source, streamId] = this.normalizeStreamIdentity(\n\t\t\topts.source ?? \"opencode\",\n\t\t\topts.opencodeSessionId,\n\t\t);\n\t\tconst now = nowIso();\n\t\tconst t = schema.rawEventSessions;\n\t\tthis.d\n\t\t\t.insert(t)\n\t\t\t.values({\n\t\t\t\topencode_session_id: streamId,\n\t\t\t\tsource,\n\t\t\t\tstream_id: streamId,\n\t\t\t\tcwd: opts.cwd ?? null,\n\t\t\t\tproject: opts.project ?? null,\n\t\t\t\tstarted_at: opts.startedAt ?? null,\n\t\t\t\tlast_seen_ts_wall_ms: opts.lastSeenTsWallMs ?? null,\n\t\t\t\tupdated_at: now,\n\t\t\t})\n\t\t\t.onConflictDoUpdate({\n\t\t\t\ttarget: [t.source, t.stream_id],\n\t\t\t\tset: {\n\t\t\t\t\topencode_session_id: sql`excluded.opencode_session_id`,\n\t\t\t\t\tcwd: sql`COALESCE(excluded.cwd, ${t.cwd})`,\n\t\t\t\t\tproject: sql`COALESCE(excluded.project, ${t.project})`,\n\t\t\t\t\tstarted_at: sql`COALESCE(excluded.started_at, ${t.started_at})`,\n\t\t\t\t\tlast_seen_ts_wall_ms: sql`CASE\n\t\t\t\t\t\tWHEN excluded.last_seen_ts_wall_ms IS NULL THEN ${t.last_seen_ts_wall_ms}\n\t\t\t\t\t\tWHEN ${t.last_seen_ts_wall_ms} IS NULL THEN excluded.last_seen_ts_wall_ms\n\t\t\t\t\t\tWHEN excluded.last_seen_ts_wall_ms > ${t.last_seen_ts_wall_ms} THEN excluded.last_seen_ts_wall_ms\n\t\t\t\t\t\tELSE ${t.last_seen_ts_wall_ms}\n\t\t\t\t\tEND`,\n\t\t\t\t\tupdated_at: sql`excluded.updated_at`,\n\t\t\t\t},\n\t\t\t})\n\t\t\t.run();\n\t}\n\n\t/**\n\t * Get totals of pending (unflushed) raw events.\n\t * Port of raw_event_backlog_totals().\n\t */\n\trawEventBacklogTotals(): { pending: number; sessions: number } {\n\t\tconst row = this.d.get<{ sessions: number | null; pending: number | null }>(sql`\n\t\t\tWITH max_events AS (\n\t\t\t\tSELECT source, stream_id, MAX(event_seq) AS max_seq\n\t\t\t\tFROM raw_events\n\t\t\t\tGROUP BY source, stream_id\n\t\t\t)\n\t\t\tSELECT\n\t\t\t\tCOUNT(1) AS sessions,\n\t\t\t\tSUM(e.max_seq - s.last_flushed_event_seq) AS pending\n\t\t\tFROM raw_event_sessions s\n\t\t\tJOIN max_events e ON e.source = s.source AND e.stream_id = s.stream_id\n\t\t\tWHERE e.max_seq > s.last_flushed_event_seq\n\t\t`);\n\t\tif (!row) return { sessions: 0, pending: 0 };\n\t\treturn {\n\t\t\tsessions: Number(row.sessions ?? 0),\n\t\t\tpending: Number(row.pending ?? 0),\n\t\t};\n\t}\n\n\t/**\n\t * Get the latest failed flush batch, or null if none.\n\t * Port of latest_raw_event_flush_failure().\n\t */\n\tlatestRawEventFlushFailure(source?: string | null): Record<string, unknown> | null {\n\t\tconst conditions = [inArray(schema.rawEventFlushBatches.status, [\"error\", \"failed\"])];\n\t\tif (source != null) {\n\t\t\tconditions.push(\n\t\t\t\teq(schema.rawEventFlushBatches.source, source.trim().toLowerCase() || \"opencode\"),\n\t\t\t);\n\t\t}\n\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tid: schema.rawEventFlushBatches.id,\n\t\t\t\tsource: schema.rawEventFlushBatches.source,\n\t\t\t\tstream_id: schema.rawEventFlushBatches.stream_id,\n\t\t\t\topencode_session_id: schema.rawEventFlushBatches.opencode_session_id,\n\t\t\t\tstart_event_seq: schema.rawEventFlushBatches.start_event_seq,\n\t\t\t\tend_event_seq: schema.rawEventFlushBatches.end_event_seq,\n\t\t\t\textractor_version: schema.rawEventFlushBatches.extractor_version,\n\t\t\t\tstatus: schema.rawEventFlushBatches.status,\n\t\t\t\tupdated_at: schema.rawEventFlushBatches.updated_at,\n\t\t\t\tattempt_count: schema.rawEventFlushBatches.attempt_count,\n\t\t\t\terror_message: schema.rawEventFlushBatches.error_message,\n\t\t\t\terror_type: schema.rawEventFlushBatches.error_type,\n\t\t\t\tobserver_provider: schema.rawEventFlushBatches.observer_provider,\n\t\t\t\tobserver_model: schema.rawEventFlushBatches.observer_model,\n\t\t\t\tobserver_runtime: schema.rawEventFlushBatches.observer_runtime,\n\t\t\t})\n\t\t\t.from(schema.rawEventFlushBatches)\n\t\t\t.where(and(...conditions))\n\t\t\t.orderBy(desc(schema.rawEventFlushBatches.updated_at))\n\t\t\t.limit(1)\n\t\t\t.get();\n\t\tif (!row) return null;\n\t\treturn { ...row, status: \"error\" };\n\t}\n\n\tgetSyncDaemonState(): Record<string, unknown> | null {\n\t\tconst row = this.d\n\t\t\t.select({\n\t\t\t\tlast_error: schema.syncDaemonState.last_error,\n\t\t\t\tlast_traceback: schema.syncDaemonState.last_traceback,\n\t\t\t\tlast_error_at: schema.syncDaemonState.last_error_at,\n\t\t\t\tlast_ok_at: schema.syncDaemonState.last_ok_at,\n\t\t\t})\n\t\t\t.from(schema.syncDaemonState)\n\t\t\t.where(eq(schema.syncDaemonState.id, 1))\n\t\t\t.get();\n\t\treturn row ? { ...row } : null;\n\t}\n\n\tsameActorPeerIds(): string[] {\n\t\tif (!tableExists(this.db, \"sync_peers\")) return [];\n\t\tconst rows = this.d\n\t\t\t.select({ peer_device_id: schema.syncPeers.peer_device_id })\n\t\t\t.from(schema.syncPeers)\n\t\t\t.where(\n\t\t\t\tor(\n\t\t\t\t\teq(schema.syncPeers.claimed_local_actor, 1),\n\t\t\t\t\teq(schema.syncPeers.actor_id, this.actorId),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(schema.syncPeers.peer_device_id)\n\t\t\t.all();\n\t\treturn rows.map((row) => String(row.peer_device_id ?? \"\").trim()).filter(Boolean);\n\t}\n\n\tclaimedSameActorLegacyActorIds(): string[] {\n\t\treturn this.sameActorPeerIds().map((peerId) => `legacy-sync:${peerId}`);\n\t}\n\n\tclaimableLegacyDeviceIds(): Record<string, unknown>[] {\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\torigin_device_id: schema.memoryItems.origin_device_id,\n\t\t\t\tmemory_count: sql<number>`COUNT(*)`,\n\t\t\t\tlast_seen_at: sql<string | null>`MAX(${schema.memoryItems.created_at})`,\n\t\t\t})\n\t\t\t.from(schema.memoryItems)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\tisNotNull(schema.memoryItems.origin_device_id),\n\t\t\t\t\tsql`TRIM(${schema.memoryItems.origin_device_id}) != ''`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} != 'unknown'`,\n\t\t\t\t\tsql`(\n\t\t\t\t\t\t(${schema.memoryItems.actor_id} IS NULL OR TRIM(${schema.memoryItems.actor_id}) = '' OR ${schema.memoryItems.actor_id} LIKE 'legacy-sync:%')\n\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t${schema.memoryItems.actor_id} IS NULL\n\t\t\t\t\t\t\tOR TRIM(${schema.memoryItems.actor_id}) = ''\n\t\t\t\t\t\t\tOR ${schema.memoryItems.actor_id} LIKE 'legacy-sync:%'\n\t\t\t\t\t\t\tOR ${schema.memoryItems.actor_display_name} = ${LEGACY_SYNC_ACTOR_DISPLAY_NAME}\n\t\t\t\t\t\t\tOR ${schema.memoryItems.workspace_id} = ${LEGACY_SHARED_WORKSPACE_ID}\n\t\t\t\t\t\t\tOR ${schema.memoryItems.trust_state} = 'legacy_unknown'\n\t\t\t\t\t\t)\n\t\t\t\t\t)`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} != ${this.deviceId}`,\n\t\t\t\t\tsql`${schema.memoryItems.origin_device_id} NOT IN (\n\t\t\t\t\t\tSELECT ${schema.syncPeers.peer_device_id}\n\t\t\t\t\t\tFROM ${schema.syncPeers}\n\t\t\t\t\t\tWHERE ${schema.syncPeers.peer_device_id} IS NOT NULL\n\t\t\t\t\t)`,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.groupBy(schema.memoryItems.origin_device_id)\n\t\t\t.orderBy(\n\t\t\t\tdesc(sql`MAX(${schema.memoryItems.created_at})`),\n\t\t\t\tschema.memoryItems.origin_device_id,\n\t\t\t)\n\t\t\t.all();\n\t\treturn rows\n\t\t\t.filter((row) => cleanStr(row.origin_device_id))\n\t\t\t.map((row) => ({\n\t\t\t\torigin_device_id: String(row.origin_device_id),\n\t\t\t\tmemory_count: Number(row.memory_count ?? 0),\n\t\t\t\tlast_seen_at: row.last_seen_at ?? null,\n\t\t\t}));\n\t}\n\n\tprivate effectiveSyncProjectFilters(peerDeviceId?: string | null): {\n\t\tinclude: string[];\n\t\texclude: string[];\n\t} {\n\t\tconst config = readCodememConfigFile();\n\t\tlet include = parseJsonList(JSON.stringify(config.sync_projects_include ?? []));\n\t\tlet exclude = parseJsonList(JSON.stringify(config.sync_projects_exclude ?? []));\n\t\tif (peerDeviceId) {\n\t\t\tconst row = this.d\n\t\t\t\t.select({\n\t\t\t\t\tprojects_include_json: schema.syncPeers.projects_include_json,\n\t\t\t\t\tprojects_exclude_json: schema.syncPeers.projects_exclude_json,\n\t\t\t\t})\n\t\t\t\t.from(schema.syncPeers)\n\t\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t\t.limit(1)\n\t\t\t\t.get();\n\t\t\tif (row) {\n\t\t\t\tif (row.projects_include_json != null) include = parseJsonList(row.projects_include_json);\n\t\t\t\tif (row.projects_exclude_json != null) exclude = parseJsonList(row.projects_exclude_json);\n\t\t\t}\n\t\t}\n\t\treturn { include, exclude };\n\t}\n\n\tprivate syncProjectAllowed(project: string | null, peerDeviceId?: string | null): boolean {\n\t\tconst { include, exclude } = this.effectiveSyncProjectFilters(peerDeviceId);\n\t\tconst name = cleanStr(project);\n\t\tconst basename = projectBasename(name);\n\t\tif (include.length > 0 && !include.some((item) => item === name || item === basename))\n\t\t\treturn false;\n\t\tif (exclude.some((item) => item === name || item === basename)) return false;\n\t\treturn true;\n\t}\n\n\tsharingReviewSummary(project?: string | null): Record<string, unknown>[] {\n\t\tconst selectedProject = cleanStr(project);\n\t\tconst claimedPeers = this.sameActorPeerIds();\n\t\tconst legacyActorIds = this.claimedSameActorLegacyActorIds();\n\t\tconst ownershipConditions = [eq(schema.memoryItems.actor_id, this.actorId)];\n\t\tif (claimedPeers.length > 0) {\n\t\t\townershipConditions.push(inArray(schema.memoryItems.origin_device_id, claimedPeers));\n\t\t}\n\t\tif (legacyActorIds.length > 0) {\n\t\t\townershipConditions.push(inArray(schema.memoryItems.actor_id, legacyActorIds));\n\t\t}\n\t\tconst ownershipWhere =\n\t\t\townershipConditions.length === 1 ? ownershipConditions[0] : or(...ownershipConditions);\n\t\tconst rows = this.d\n\t\t\t.select({\n\t\t\t\tpeer_device_id: schema.syncPeers.peer_device_id,\n\t\t\t\tname: schema.syncPeers.name,\n\t\t\t\tactor_id: schema.syncPeers.actor_id,\n\t\t\t\tactor_display_name: schema.actors.display_name,\n\t\t\t\tproject: schema.sessions.project,\n\t\t\t\tvisibility: schema.memoryItems.visibility,\n\t\t\t\ttotal: sql<number>`COUNT(*)`,\n\t\t\t})\n\t\t\t.from(schema.syncPeers)\n\t\t\t.leftJoin(schema.actors, eq(schema.actors.actor_id, schema.syncPeers.actor_id))\n\t\t\t.innerJoin(schema.memoryItems, sql`1 = 1`)\n\t\t\t.innerJoin(schema.sessions, eq(schema.sessions.id, schema.memoryItems.session_id))\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(schema.memoryItems.active, 1),\n\t\t\t\t\tisNotNull(schema.syncPeers.actor_id),\n\t\t\t\t\tsql`TRIM(${schema.syncPeers.actor_id}) != ''`,\n\t\t\t\t\tsql`${schema.syncPeers.actor_id} != ${this.actorId}`,\n\t\t\t\t\townershipWhere,\n\t\t\t\t),\n\t\t\t)\n\t\t\t.groupBy(\n\t\t\t\tschema.syncPeers.peer_device_id,\n\t\t\t\tschema.syncPeers.name,\n\t\t\t\tschema.syncPeers.actor_id,\n\t\t\t\tschema.actors.display_name,\n\t\t\t\tschema.sessions.project,\n\t\t\t\tschema.memoryItems.visibility,\n\t\t\t)\n\t\t\t.orderBy(schema.syncPeers.name, schema.syncPeers.peer_device_id)\n\t\t\t.all();\n\t\tconst byPeer = new Map<string, Record<string, unknown>>();\n\t\tfor (const row of rows) {\n\t\t\tconst peerDeviceId = String(row.peer_device_id ?? \"\").trim();\n\t\t\tconst actorId = String(row.actor_id ?? \"\").trim();\n\t\t\tif (!peerDeviceId || !actorId || actorId === this.actorId) continue;\n\t\t\tconst projectName = cleanStr(row.project);\n\t\t\tif (selectedProject) {\n\t\t\t\tconst selectedBase = projectBasename(selectedProject);\n\t\t\t\tconst projectBase = projectBasename(projectName);\n\t\t\t\tif (projectName !== selectedProject && projectBase !== selectedBase) continue;\n\t\t\t}\n\t\t\tif (!this.syncProjectAllowed(projectName, peerDeviceId)) continue;\n\t\t\tconst current = byPeer.get(peerDeviceId) ?? {\n\t\t\t\tpeer_device_id: peerDeviceId,\n\t\t\t\tpeer_name: cleanStr(row.name) ?? peerDeviceId,\n\t\t\t\tactor_id: actorId,\n\t\t\t\tactor_display_name: cleanStr(row.actor_display_name) ?? actorId,\n\t\t\t\tproject: selectedProject,\n\t\t\t\tscope_label: selectedProject ?? \"All allowed projects\",\n\t\t\t\tshareable_count: 0,\n\t\t\t\tprivate_count: 0,\n\t\t\t};\n\t\t\tconst total = Number(row.total ?? 0);\n\t\t\tif (String(row.visibility ?? \"\") === \"private\") {\n\t\t\t\tcurrent.private_count = Number(current.private_count ?? 0) + total;\n\t\t\t} else {\n\t\t\t\tcurrent.shareable_count = Number(current.shareable_count ?? 0) + total;\n\t\t\t}\n\t\t\tbyPeer.set(peerDeviceId, current);\n\t\t}\n\t\tconst results = [...byPeer.values()].map((item) => ({\n\t\t\t...item,\n\t\t\ttotal_count: Number(item.shareable_count ?? 0) + Number(item.private_count ?? 0),\n\t\t}));\n\t\treturn results.sort(\n\t\t\t(a: Record<string, unknown>, b: Record<string, unknown>) =>\n\t\t\t\tString(a.peer_name ?? \"\").localeCompare(String(b.peer_name ?? \"\")) ||\n\t\t\t\tString(a.peer_device_id ?? \"\").localeCompare(String(b.peer_device_id ?? \"\")),\n\t\t);\n\t}\n\n\t// close\n\n\t/** Close the database connection. */\n\tclose(): void {\n\t\tthis.db.close();\n\t}\n}\n","/**\n * Peer discovery and address management for the codemem sync system.\n *\n * Handles address normalization, deduplication, peer address storage,\n * and mDNS stubs. Ported from codemem/sync/discovery.py.\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport { eq, sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport { mergeAddresses, normalizeAddress } from \"./address-utils.js\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\n\n// Re-export for consumers that import from sync-discovery\nexport { addressDedupeKey, mergeAddresses, normalizeAddress } from \"./address-utils.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const DEFAULT_SERVICE_TYPE = \"_codemem._tcp.local.\";\n\n// ---------------------------------------------------------------------------\n// Address selection\n// ---------------------------------------------------------------------------\n\n/**\n * Select addresses to dial, preferring mDNS-discovered addresses.\n *\n * mDNS addresses come first, then stored addresses (deduplicated).\n */\nexport function selectDialAddresses(options: { stored: string[]; mdns: string[] }): string[] {\n\tif (options.mdns.length === 0) {\n\t\treturn mergeAddresses(options.stored, []);\n\t}\n\treturn mergeAddresses(options.mdns, options.stored);\n}\n\n// ---------------------------------------------------------------------------\n// Peer address storage (DB)\n// ---------------------------------------------------------------------------\n\n/**\n * Load stored addresses for a peer from the sync_peers table.\n */\nexport function loadPeerAddresses(db: Database, peerDeviceId: string): string[] {\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ addresses_json: schema.syncPeers.addresses_json })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\tif (!row?.addresses_json) return [];\n\ttry {\n\t\tconst raw = JSON.parse(row.addresses_json);\n\t\tif (!Array.isArray(raw)) return [];\n\t\treturn raw.filter((item): item is string => typeof item === \"string\");\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Update stored addresses for a peer, merging with existing.\n *\n * Creates the sync_peers row if it doesn't exist (upsert).\n * Returns the merged address list.\n */\nexport function updatePeerAddresses(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddresses: string[],\n\toptions?: {\n\t\tname?: string;\n\t\tpinnedFingerprint?: string;\n\t\tpublicKey?: string;\n\t},\n): string[] {\n\tconst merged = mergeAddresses(loadPeerAddresses(db, peerDeviceId), addresses);\n\tconst now = new Date().toISOString();\n\tconst addressesJson = JSON.stringify(merged);\n\n\t// Atomic UPSERT — avoids TOCTOU race with concurrent sync workers\n\tconst d = drizzle(db, { schema });\n\td.insert(schema.syncPeers)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tname: options?.name ?? null,\n\t\t\tpinned_fingerprint: options?.pinnedFingerprint ?? null,\n\t\t\tpublic_key: options?.publicKey ?? null,\n\t\t\taddresses_json: addressesJson,\n\t\t\tcreated_at: now,\n\t\t\tlast_seen_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncPeers.peer_device_id,\n\t\t\tset: {\n\t\t\t\tname: sql`COALESCE(excluded.name, ${schema.syncPeers.name})`,\n\t\t\t\tpinned_fingerprint: sql`COALESCE(excluded.pinned_fingerprint, ${schema.syncPeers.pinned_fingerprint})`,\n\t\t\t\tpublic_key: sql`COALESCE(excluded.public_key, ${schema.syncPeers.public_key})`,\n\t\t\t\taddresses_json: sql`excluded.addresses_json`,\n\t\t\t\tlast_seen_at: sql`excluded.last_seen_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n\n\treturn merged;\n}\n\n/**\n * Record a sync attempt in the sync_attempts table and update peer status.\n */\nexport function recordSyncAttempt(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: {\n\t\tok: boolean;\n\t\topsIn?: number;\n\t\topsOut?: number;\n\t\terror?: string;\n\t},\n): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncAttempts)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tstarted_at: now,\n\t\t\tfinished_at: now,\n\t\t\tok: options.ok ? 1 : 0,\n\t\t\tops_in: options.opsIn ?? 0,\n\t\t\tops_out: options.opsOut ?? 0,\n\t\t\terror: options.error ?? null,\n\t\t})\n\t\t.run();\n\n\tif (options.ok) {\n\t\td.update(schema.syncPeers)\n\t\t\t.set({ last_sync_at: now, last_error: null })\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t} else {\n\t\td.update(schema.syncPeers)\n\t\t\t.set({ last_error: options.error ?? null })\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t}\n}\n\n/**\n * Record a successful sync and promote the working address to the front.\n *\n * Returns the reordered address list.\n */\nexport function recordPeerSuccess(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddress: string | null,\n): string[] {\n\tconst addresses = loadPeerAddresses(db, peerDeviceId);\n\tconst normalized = normalizeAddress(address ?? \"\");\n\tlet ordered = addresses;\n\tif (normalized) {\n\t\tconst remaining = addresses.filter((item) => normalizeAddress(item) !== normalized);\n\t\tordered = [normalized, ...remaining];\n\t\tconst d = drizzle(db, { schema });\n\t\td.update(schema.syncPeers)\n\t\t\t.set({\n\t\t\t\taddresses_json: JSON.stringify(ordered),\n\t\t\t\tlast_sync_at: new Date().toISOString(),\n\t\t\t\tlast_error: null,\n\t\t\t})\n\t\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t\t.run();\n\t}\n\treturn ordered;\n}\n\n// ---------------------------------------------------------------------------\n// Project filters\n// ---------------------------------------------------------------------------\n\n/**\n * Set per-peer project include/exclude filters.\n *\n * Pass null for both to clear the override (inherit global config).\n */\nexport function setPeerProjectFilter(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: {\n\t\tinclude: string[] | null;\n\t\texclude: string[] | null;\n\t},\n): void {\n\tconst includeJson = options.include === null ? null : JSON.stringify(options.include);\n\tconst excludeJson = options.exclude === null ? null : JSON.stringify(options.exclude);\n\tconst d = drizzle(db, { schema });\n\td.update(schema.syncPeers)\n\t\t.set({ projects_include_json: includeJson, projects_exclude_json: excludeJson })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n/**\n * Set the claimed_local_actor flag for a peer.\n */\nexport function setPeerLocalActorClaim(db: Database, peerDeviceId: string, claimed: boolean): void {\n\tconst d = drizzle(db, { schema });\n\td.update(schema.syncPeers)\n\t\t.set({ claimed_local_actor: claimed ? 1 : 0 })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n// ---------------------------------------------------------------------------\n// mDNS stubs (Phase 1)\n// ---------------------------------------------------------------------------\n\nexport interface MdnsEntry {\n\tname?: string;\n\thost?: string;\n\tport?: number;\n\taddress?: Uint8Array | string;\n\tproperties?: Record<string, string | Uint8Array>;\n}\n\nfunction commandAvailable(command: string): boolean {\n\ttry {\n\t\texecFileSync(\"which\", [command], { stdio: \"pipe\" });\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction runDnsSd(args: string[], timeoutMs = 1200): string {\n\ttry {\n\t\treturn execFileSync(\"dns-sd\", args, {\n\t\t\tencoding: \"utf8\",\n\t\t\tstdio: [\"ignore\", \"pipe\", \"pipe\"],\n\t\t\ttimeout: timeoutMs,\n\t\t});\n\t} catch (err) {\n\t\tif (err && typeof err === \"object\") {\n\t\t\tconst e = err as { stdout?: string | Buffer; stderr?: string | Buffer };\n\t\t\tconst out = [e.stdout, e.stderr]\n\t\t\t\t.map((part) => {\n\t\t\t\t\tif (typeof part === \"string\") return part;\n\t\t\t\t\tif (part instanceof Buffer) return part.toString(\"utf8\");\n\t\t\t\t\treturn \"\";\n\t\t\t\t})\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\"\\n\");\n\t\t\treturn out;\n\t\t}\n\t\treturn \"\";\n\t}\n}\n\nfunction discoverServiceNamesDnsSd(): string[] {\n\tconst output = runDnsSd([\"-B\", \"_codemem._tcp\", \"local.\"], 1200);\n\tif (!output) return [];\n\tconst names = new Set<string>();\n\tfor (const line of output.split(/\\r?\\n/)) {\n\t\tif (!line.includes(\"Add\")) continue;\n\t\tlet name = \"\";\n\t\tconst columnMatch = line.match(/\\bAdd\\b.*\\slocal\\.\\s+_codemem\\._tcp\\.\\s+(.+)$/);\n\t\tif (columnMatch) {\n\t\t\tname = String(columnMatch[1] ?? \"\").trim();\n\t\t} else {\n\t\t\tconst legacyMatch = line.match(/\\sAdd\\s+\\S+\\s+\\S+\\s+\\S+\\s+(.+)\\._codemem\\._tcp\\./);\n\t\t\tif (legacyMatch) name = String(legacyMatch[1] ?? \"\").trim();\n\t\t}\n\t\tif (name) names.add(name);\n\t}\n\treturn [...names];\n}\n\nfunction resolveServiceDnsSd(name: string): MdnsEntry | null {\n\tconst output = runDnsSd([\"-L\", name, \"_codemem._tcp\", \"local.\"], 1200);\n\tif (!output) return null;\n\n\tconst hostPortMatch = output.match(/can be reached at\\s+([^:\\s]+)\\.?:(\\d+)/i);\n\tconst host = hostPortMatch?.[1] ? String(hostPortMatch[1]).trim() : \"\";\n\tconst port = hostPortMatch?.[2] ? Number.parseInt(String(hostPortMatch[2]), 10) : 0;\n\n\tconst txtDeviceIdMatch = output.match(/device_id=([^\\s\"',]+)/i);\n\tconst deviceId = txtDeviceIdMatch?.[1] ? String(txtDeviceIdMatch[1]).trim() : \"\";\n\n\tif (!host || !port || Number.isNaN(port)) return null;\n\tconst properties: Record<string, string> = {};\n\tif (deviceId) properties.device_id = deviceId;\n\n\treturn {\n\t\tname,\n\t\thost,\n\t\tport,\n\t\tproperties,\n\t};\n}\n\n/**\n * Check if mDNS discovery is enabled via the CODEMEM_SYNC_MDNS env var.\n */\nexport function mdnsEnabled(): boolean {\n\tconst value = process.env.CODEMEM_SYNC_MDNS;\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\";\n}\n\n/**\n * Advertise this device via mDNS.\n *\n * Phase 1 stub: logs the intent and returns a no-op closer.\n * Real implementation will use dns-sd (macOS) or bonjour-service (Linux).\n */\nexport function advertiseMdns(_deviceId: string, _port: number): { close(): void } {\n\tif (!mdnsEnabled()) return { close() {} };\n\tif (process.platform !== \"darwin\") return { close() {} };\n\tif (!commandAvailable(\"dns-sd\")) return { close() {} };\n\n\tconst serviceName = `codemem-${_deviceId.slice(0, 12)}`;\n\tconst child = spawn(\n\t\t\"dns-sd\",\n\t\t[\"-R\", serviceName, \"_codemem._tcp\", \"local.\", String(_port), `device_id=${_deviceId}`],\n\t\t{\n\t\t\tstdio: \"ignore\",\n\t\t\tdetached: false,\n\t\t},\n\t);\n\n\treturn {\n\t\tclose() {\n\t\t\tif (!child.killed) {\n\t\t\t\ttry {\n\t\t\t\t\tchild.kill(\"SIGTERM\");\n\t\t\t\t} catch {\n\t\t\t\t\t// best effort\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t};\n}\n\n/**\n * Discover peers via mDNS.\n *\n * Phase 1 stub: returns an empty array.\n * Real implementation will use dns-sd (macOS) or bonjour-service (Linux).\n */\nexport function discoverPeersViaMdns(): MdnsEntry[] {\n\tif (!mdnsEnabled()) return [];\n\tif (process.platform !== \"darwin\") return [];\n\tif (!commandAvailable(\"dns-sd\")) return [];\n\n\tconst names = discoverServiceNamesDnsSd();\n\tif (names.length === 0) return [];\n\n\tconst entries: MdnsEntry[] = [];\n\tfor (const name of names) {\n\t\tconst resolved = resolveServiceDnsSd(name);\n\t\tif (resolved) entries.push(resolved);\n\t}\n\treturn entries;\n}\n\n/**\n * Extract addresses for a specific peer from mDNS discovery entries.\n */\nexport function mdnsAddressesForPeer(peerDeviceId: string, entries: MdnsEntry[]): string[] {\n\tconst addresses: string[] = [];\n\tfor (const entry of entries) {\n\t\tconst props = entry.properties ?? {};\n\t\tlet deviceId: string | Uint8Array | undefined =\n\t\t\tprops.device_id ??\n\t\t\t((props as Record<string, unknown>).device_id as string | Uint8Array | undefined);\n\t\tif (deviceId == null) continue;\n\t\tif (deviceId instanceof Uint8Array) {\n\t\t\tdeviceId = new TextDecoder().decode(deviceId);\n\t\t}\n\t\tif (deviceId !== peerDeviceId) continue;\n\n\t\tconst port = entry.port ?? 0;\n\t\tif (!port) continue;\n\n\t\t// Prefer entry.address (resolved IP) over entry.host (service name).\n\t\t// mDNS responses often carry a service-name host (e.g. _codemem._tcp.local)\n\t\t// that isn't directly dialable, but include the usable IP in address.\n\t\tconst address = (entry as Record<string, unknown>).address as string | undefined;\n\t\tconst host = entry.host ?? \"\";\n\t\tconst dialHost = address && !address.includes(\".local\") ? address : host;\n\t\tif (dialHost && !dialHost.includes(\"_tcp.local\") && !dialHost.includes(\"_udp.local\")) {\n\t\t\taddresses.push(`${dialHost}:${port}`);\n\t\t}\n\t}\n\treturn addresses;\n}\n","/**\n * Sync pass orchestrator: coordinates a single sync exchange with a peer device.\n *\n * Handles the pull→apply→push cycle for replication ops, with address\n * fallback, fingerprint verification, and cursor tracking.\n * Ported from codemem/sync/sync_pass.py.\n */\n\nimport { and, desc, eq, gt, or } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { buildAuthHeaders } from \"./sync-auth.js\";\nimport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nimport { ensureDeviceIdentity } from \"./sync-identity.js\";\nimport {\n\tapplyReplicationOps,\n\tbackfillReplicationOps,\n\tchunkOpsBySize,\n\textractReplicationOps,\n\tfilterReplicationOpsForSync,\n\tfilterReplicationOpsForSyncWithStatus,\n\tgetReplicationCursor,\n\tmigrateLegacyImportKeys,\n\tsetReplicationCursor,\n} from \"./sync-replication.js\";\nimport type { ReplicationOp } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Default max body size for sync POST requests (1 MiB). */\nconst MAX_SYNC_BODY_BYTES = 1_048_576;\n\n/** Default op fetch/push limit per round. */\nconst DEFAULT_LIMIT = 200;\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SyncResult {\n\tok: boolean;\n\terror?: string;\n\taddress?: string;\n\topsIn: number;\n\topsOut: number;\n\taddressErrors: Array<{ address: string; error: string }>;\n}\n\nexport interface SyncPassOptions {\n\tlimit?: number;\n\tkeysDir?: string;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Check if a cursor candidate advances beyond the current position.\n *\n * Cursors are `timestamp|op_id` strings that sort lexicographically.\n */\nexport function cursorAdvances(current: string | null, candidate: string | null): boolean {\n\tif (!candidate) return false;\n\tif (!candidate.includes(\"|\")) return false;\n\tif (!current) return true;\n\treturn candidate > current;\n}\n\n/**\n * Extract an error detail string from a JSON response payload.\n */\nfunction errorDetail(payload: Record<string, unknown> | null): string | null {\n\tif (!payload) return null;\n\tconst error = payload.error;\n\tconst reason = payload.reason;\n\tif (typeof error === \"string\" && typeof reason === \"string\") {\n\t\treturn `${error}:${reason}`;\n\t}\n\tif (typeof error === \"string\") return error;\n\treturn null;\n}\n\n/**\n * Summarize address errors into a single error string.\n */\nfunction summarizeAddressErrors(\n\taddressErrors: Array<{ address: string; error: string }>,\n): string | null {\n\tif (addressErrors.length === 0) return null;\n\tconst parts = addressErrors.map((item) => `${item.address}: ${item.error}`);\n\treturn `all addresses failed | ${parts.join(\" || \")}`;\n}\n\n/**\n * Record a sync attempt in the sync_attempts table.\n */\nfunction recordSyncAttempt(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions: { ok: boolean; opsIn?: number; opsOut?: number; error?: string },\n): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncAttempts)\n\t\t.values({\n\t\t\tpeer_device_id: peerDeviceId,\n\t\t\tstarted_at: now,\n\t\t\tfinished_at: now,\n\t\t\tok: options.ok ? 1 : 0,\n\t\t\tops_in: options.opsIn ?? 0,\n\t\t\tops_out: options.opsOut ?? 0,\n\t\t\terror: options.error ?? null,\n\t\t})\n\t\t.run();\n}\n\n/**\n * Update last_sync_at and clear last_error on successful sync.\n */\nfunction recordPeerSuccess(db: Database, peerDeviceId: string): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.update(schema.syncPeers)\n\t\t.set({ last_sync_at: now, last_seen_at: now, last_error: null })\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.run();\n}\n\n/**\n * Load outbound replication ops for the local device since a cursor.\n *\n * Returns `[ops, nextCursor]`. The cursor is `created_at|op_id`.\n */\nfunction loadLocalOpsSince(\n\tdb: Database,\n\tcursor: string | null,\n\tdeviceId: string,\n\tlimit: number,\n): [ReplicationOp[], string | null] {\n\tconst d = drizzle(db, { schema });\n\tconst { replicationOps: ops } = schema;\n\tlet rows: ReplicationOp[];\n\tif (cursor) {\n\t\tconst sepIdx = cursor.indexOf(\"|\");\n\t\tconst cursorTs = sepIdx >= 0 ? cursor.slice(0, sepIdx) : cursor;\n\t\tconst cursorOpId = sepIdx >= 0 ? cursor.slice(sepIdx + 1) : \"\";\n\t\trows = d\n\t\t\t.select()\n\t\t\t.from(ops)\n\t\t\t.where(\n\t\t\t\tand(\n\t\t\t\t\teq(ops.device_id, deviceId),\n\t\t\t\t\tor(\n\t\t\t\t\t\tgt(ops.created_at, cursorTs),\n\t\t\t\t\t\tand(eq(ops.created_at, cursorTs), gt(ops.op_id, cursorOpId)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\t\t\t.orderBy(ops.created_at, ops.op_id)\n\t\t\t.limit(limit)\n\t\t\t.all() as ReplicationOp[];\n\t} else {\n\t\trows = d\n\t\t\t.select()\n\t\t\t.from(ops)\n\t\t\t.where(eq(ops.device_id, deviceId))\n\t\t\t.orderBy(ops.created_at, ops.op_id)\n\t\t\t.limit(limit)\n\t\t\t.all() as ReplicationOp[];\n\t}\n\n\tif (rows.length === 0) return [[], null];\n\tconst last = rows.at(-1);\n\tif (!last) return [[], null];\n\tconst nextCursor = `${last.created_at}|${last.op_id}`;\n\treturn [rows, nextCursor];\n}\n\n/**\n * Compute a cursor string from a timestamp and op_id.\n * Currently unused — will be needed when real apply logic advances the cursor.\n */\nexport function computeCursor(createdAt: string, opId: string): string {\n\treturn `${createdAt}|${opId}`;\n}\n\n// ---------------------------------------------------------------------------\n// Push ops\n// ---------------------------------------------------------------------------\n\n/**\n * Push ops to a peer endpoint with auth headers.\n *\n * On 413 (too large), splits the batch in half and retries recursively.\n */\nconst MAX_PUSH_SPLIT_DEPTH = 8;\n\nasync function pushOps(\n\tpostUrl: string,\n\tdeviceId: string,\n\tops: ReplicationOp[],\n\tkeysDir?: string,\n\tdepth = 0,\n): Promise<void> {\n\tif (ops.length === 0) return;\n\n\tconst body = { ops };\n\tconst bodyBytes = Buffer.from(JSON.stringify(body), \"utf-8\");\n\tconst headers = buildAuthHeaders({\n\t\tdeviceId,\n\t\tmethod: \"POST\",\n\t\turl: postUrl,\n\t\tbodyBytes,\n\t\tkeysDir,\n\t});\n\tconst [status, payload] = await requestJson(\"POST\", postUrl, {\n\t\theaders,\n\t\tbody,\n\t\tbodyBytes,\n\t});\n\n\tif (status === 200 && payload != null) return;\n\n\tconst detail = errorDetail(payload);\n\tif (\n\t\tstatus === 413 &&\n\t\tops.length > 1 &&\n\t\tdepth < MAX_PUSH_SPLIT_DEPTH &&\n\t\t(detail === \"payload_too_large\" || detail === \"too_many_ops\")\n\t) {\n\t\tconst mid = Math.floor(ops.length / 2);\n\t\tawait pushOps(postUrl, deviceId, ops.slice(0, mid), keysDir, depth + 1);\n\t\tawait pushOps(postUrl, deviceId, ops.slice(mid), keysDir, depth + 1);\n\t\treturn;\n\t}\n\n\tconst suffix = detail ? ` (${status}: ${detail})` : ` (${status})`;\n\tthrow new Error(`peer ops push failed${suffix}`);\n}\n\n// ---------------------------------------------------------------------------\n// Preflight\n// ---------------------------------------------------------------------------\n\n/** Run migration + replication backfill preflight. */\nexport function syncPassPreflight(db: Database): void {\n\tmigrateLegacyImportKeys(db, 2000);\n\tbackfillReplicationOps(db, 200);\n}\n\n// ---------------------------------------------------------------------------\n// Main sync loop\n// ---------------------------------------------------------------------------\n\n/**\n * Execute a single sync exchange with a peer across the given addresses.\n *\n * Tries each address in order. On first success, returns immediately.\n * On all failures, collects address-level errors and returns them.\n */\nexport async function syncOnce(\n\tdb: Database,\n\tpeerDeviceId: string,\n\taddresses: string[],\n\toptions?: SyncPassOptions,\n): Promise<SyncResult> {\n\tconst limit = options?.limit ?? DEFAULT_LIMIT;\n\tconst keysDir = options?.keysDir;\n\n\t// Look up pinned fingerprint\n\tconst d = drizzle(db, { schema });\n\tconst pinRow = d\n\t\t.select({ pinned_fingerprint: schema.syncPeers.pinned_fingerprint })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\tconst pinnedFingerprint = pinRow?.pinned_fingerprint ?? \"\";\n\tif (!pinnedFingerprint) {\n\t\treturn { ok: false, error: \"peer not pinned\", opsIn: 0, opsOut: 0, addressErrors: [] };\n\t}\n\n\t// Read cursors\n\tlet [lastApplied, lastAcked] = getReplicationCursor(db, peerDeviceId);\n\n\t// Ensure local device identity\n\tlet deviceId: string;\n\ttry {\n\t\t[deviceId] = ensureDeviceIdentity(db, { keysDir });\n\t} catch (err: unknown) {\n\t\tconst detail = err instanceof Error ? err.message.trim() || err.constructor.name : \"unknown\";\n\t\tconst error = `device identity unavailable: ${detail}`;\n\t\trecordSyncAttempt(db, peerDeviceId, { ok: false, error });\n\t\treturn { ok: false, error, opsIn: 0, opsOut: 0, addressErrors: [] };\n\t}\n\n\tconst addressErrors: Array<{ address: string; error: string }> = [];\n\tlet attemptedAny = false;\n\n\tfor (const address of addresses) {\n\t\tconst baseUrl = buildBaseUrl(address);\n\t\tif (!baseUrl) continue;\n\t\tattemptedAny = true;\n\n\t\ttry {\n\t\t\t// -- 1. Verify peer identity via /v1/status --\n\t\t\tconst statusUrl = `${baseUrl}/v1/status`;\n\t\t\tconst statusHeaders = buildAuthHeaders({\n\t\t\t\tdeviceId,\n\t\t\t\tmethod: \"GET\",\n\t\t\t\turl: statusUrl,\n\t\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\t\tkeysDir,\n\t\t\t});\n\t\t\tconst [statusCode, statusPayload] = await requestJson(\"GET\", statusUrl, {\n\t\t\t\theaders: statusHeaders,\n\t\t\t});\n\t\t\tif (statusCode !== 200 || !statusPayload) {\n\t\t\t\tconst detail = errorDetail(statusPayload);\n\t\t\t\tconst suffix = detail ? ` (${statusCode}: ${detail})` : ` (${statusCode})`;\n\t\t\t\tthrow new Error(`peer status failed${suffix}`);\n\t\t\t}\n\t\t\tif (statusPayload.fingerprint !== pinnedFingerprint) {\n\t\t\t\tthrow new Error(\"peer fingerprint mismatch\");\n\t\t\t}\n\n\t\t\t// -- 2. Pull ops from peer --\n\t\t\tconst query = new URLSearchParams({\n\t\t\t\tsince: lastApplied ?? \"\",\n\t\t\t\tlimit: String(limit),\n\t\t\t}).toString();\n\t\t\tconst getUrl = `${baseUrl}/v1/ops?${query}`;\n\t\t\tconst getHeaders = buildAuthHeaders({\n\t\t\t\tdeviceId,\n\t\t\t\tmethod: \"GET\",\n\t\t\t\turl: getUrl,\n\t\t\t\tbodyBytes: Buffer.alloc(0),\n\t\t\t\tkeysDir,\n\t\t\t});\n\t\t\tconst [getStatus, getPayload] = await requestJson(\"GET\", getUrl, {\n\t\t\t\theaders: getHeaders,\n\t\t\t});\n\t\t\tif (getStatus !== 200 || getPayload == null) {\n\t\t\t\tconst detail = errorDetail(getPayload);\n\t\t\t\tconst suffix = detail ? ` (${getStatus}: ${detail})` : ` (${getStatus})`;\n\t\t\t\tthrow new Error(`peer ops fetch failed${suffix}`);\n\t\t\t}\n\t\t\tif (!Array.isArray(getPayload.ops)) {\n\t\t\t\tthrow new Error(\"invalid ops response\");\n\t\t\t}\n\t\t\tconst ops = extractReplicationOps(getPayload);\n\t\t\tconst [inboundOps, inboundCursor] = filterReplicationOpsForSyncWithStatus(\n\t\t\t\tdb,\n\t\t\t\tops,\n\t\t\t\tpeerDeviceId,\n\t\t\t);\n\n\t\t\t// -- 3. Apply incoming ops to local entities --\n\t\t\tconst applied = applyReplicationOps(db, inboundOps, deviceId);\n\n\t\t\tconst inboundCursorCandidate =\n\t\t\t\tinboundCursor ||\n\t\t\t\t(typeof getPayload.next_cursor === \"string\" ? getPayload.next_cursor.trim() : \"\");\n\t\t\tif (cursorAdvances(lastApplied, inboundCursorCandidate)) {\n\t\t\t\tsetReplicationCursor(db, peerDeviceId, { lastApplied: inboundCursorCandidate });\n\t\t\t\tlastApplied = inboundCursorCandidate;\n\t\t\t}\n\n\t\t\t// -- 5. Push local ops to peer --\n\t\t\tconst [outboundWindow, outboundCursor] = loadLocalOpsSince(db, lastAcked, deviceId, limit);\n\t\t\tconst [outboundOps, filteredOutboundCursor] = filterReplicationOpsForSync(\n\t\t\t\tdb,\n\t\t\t\toutboundWindow,\n\t\t\t\tpeerDeviceId,\n\t\t\t);\n\t\t\tconst postUrl = `${baseUrl}/v1/ops`;\n\t\t\tif (outboundOps.length > 0) {\n\t\t\t\tconst batches = chunkOpsBySize(outboundOps, MAX_SYNC_BODY_BYTES);\n\t\t\t\tfor (const batch of batches) {\n\t\t\t\t\tawait pushOps(postUrl, deviceId, batch, keysDir);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst ackCursor = filteredOutboundCursor ?? outboundCursor;\n\t\t\tif (ackCursor && cursorAdvances(lastAcked, ackCursor)) {\n\t\t\t\tsetReplicationCursor(db, peerDeviceId, { lastAcked: ackCursor });\n\t\t\t\tlastAcked = ackCursor;\n\t\t\t}\n\n\t\t\t// -- 6. Record success --\n\t\t\trecordPeerSuccess(db, peerDeviceId);\n\t\t\trecordSyncAttempt(db, peerDeviceId, {\n\t\t\t\tok: true,\n\t\t\t\topsIn: applied.applied,\n\t\t\t\topsOut: outboundOps.length,\n\t\t\t});\n\t\t\treturn {\n\t\t\t\tok: true,\n\t\t\t\taddress: baseUrl,\n\t\t\t\topsIn: applied.applied,\n\t\t\t\topsOut: outboundOps.length,\n\t\t\t\taddressErrors: [],\n\t\t\t};\n\t\t} catch (err: unknown) {\n\t\t\tconst detail = err instanceof Error ? err.message.trim() || err.constructor.name : \"unknown\";\n\t\t\taddressErrors.push({ address: baseUrl, error: detail });\n\t\t}\n\t}\n\n\t// All addresses failed\n\tlet error = summarizeAddressErrors(addressErrors);\n\tif (!attemptedAny) {\n\t\terror = \"no dialable peer addresses\";\n\t}\n\tif (!error) {\n\t\terror = \"sync failed without diagnostic detail\";\n\t}\n\trecordSyncAttempt(db, peerDeviceId, { ok: false, error });\n\treturn { ok: false, error, opsIn: 0, opsOut: 0, addressErrors };\n}\n\n// ---------------------------------------------------------------------------\n// High-level entry point\n// ---------------------------------------------------------------------------\n\n/**\n * Run a sync pass with a peer, resolving addresses from the database.\n *\n * Loads peer addresses from the sync_peers table and delegates to syncOnce.\n */\nexport async function runSyncPass(\n\tdb: Database,\n\tpeerDeviceId: string,\n\toptions?: SyncPassOptions,\n): Promise<SyncResult> {\n\t// Load stored addresses for this peer\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ addresses_json: schema.syncPeers.addresses_json })\n\t\t.from(schema.syncPeers)\n\t\t.where(eq(schema.syncPeers.peer_device_id, peerDeviceId))\n\t\t.get();\n\n\tlet addresses: string[] = [];\n\tif (row?.addresses_json) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(row.addresses_json);\n\t\t\tif (Array.isArray(parsed)) {\n\t\t\t\taddresses = parsed.filter((a): a is string => typeof a === \"string\");\n\t\t\t}\n\t\t} catch {\n\t\t\t// Malformed JSON — proceed with empty addresses\n\t\t}\n\t}\n\n\treturn syncOnce(db, peerDeviceId, addresses, options);\n}\n\n// ---------------------------------------------------------------------------\n// Connectivity backoff (for daemon use)\n// ---------------------------------------------------------------------------\n\n/** Connectivity error patterns indicating peer is offline. */\nconst CONNECTIVITY_ERROR_PATTERNS = [\n\t\"no route to host\",\n\t\"connection refused\",\n\t\"network is unreachable\",\n\t\"timed out\",\n\t\"name or service not known\",\n\t\"nodename nor servname provided\",\n\t\"errno 65\",\n\t\"errno 61\",\n\t\"errno 60\",\n\t\"errno 111\",\n] as const;\n\nconst PEER_BACKOFF_BASE_S = 120;\nconst PEER_BACKOFF_MAX_S = 1800;\n\n/** Check if an error string indicates a network connectivity failure. */\nexport function isConnectivityError(error: string | null): boolean {\n\tif (!error) return false;\n\tconst lower = error.toLowerCase();\n\treturn CONNECTIVITY_ERROR_PATTERNS.some((pattern) => lower.includes(pattern));\n}\n\n/** Count consecutive recent failures that are connectivity errors. */\nexport function consecutiveConnectivityFailures(\n\tdb: Database,\n\tpeerDeviceId: string,\n\tlimit = 10,\n): number {\n\tconst d = drizzle(db, { schema });\n\tconst rows = d\n\t\t.select({ ok: schema.syncAttempts.ok, error: schema.syncAttempts.error })\n\t\t.from(schema.syncAttempts)\n\t\t.where(eq(schema.syncAttempts.peer_device_id, peerDeviceId))\n\t\t.orderBy(desc(schema.syncAttempts.started_at))\n\t\t.limit(limit)\n\t\t.all();\n\n\tlet count = 0;\n\tfor (const row of rows) {\n\t\tif (row.ok) break;\n\t\tif (isConnectivityError(row.error)) {\n\t\t\tcount++;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\treturn count;\n}\n\n/** Calculate backoff duration with jitter to avoid thundering herd. */\nexport function peerBackoffSeconds(consecutiveFailures: number): number {\n\tif (consecutiveFailures <= 1) return 0;\n\tconst exponent = Math.min(consecutiveFailures - 1, 8);\n\tconst base = Math.min(PEER_BACKOFF_BASE_S * 2 ** (exponent - 1), PEER_BACKOFF_MAX_S);\n\t// Add 50% jitter: base * [0.5, 1.0)\n\treturn base * (0.5 + Math.random() * 0.5);\n}\n\n/** Check if a peer should be skipped due to repeated connectivity failures. */\nexport function shouldSkipOfflinePeer(db: Database, peerDeviceId: string): boolean {\n\tconst failures = consecutiveConnectivityFailures(db, peerDeviceId);\n\tif (failures < 2) return false;\n\tconst backoffS = peerBackoffSeconds(failures);\n\tif (backoffS <= 0) return false;\n\n\tconst d = drizzle(db, { schema });\n\tconst row = d\n\t\t.select({ started_at: schema.syncAttempts.started_at })\n\t\t.from(schema.syncAttempts)\n\t\t.where(eq(schema.syncAttempts.peer_device_id, peerDeviceId))\n\t\t.orderBy(desc(schema.syncAttempts.started_at))\n\t\t.limit(1)\n\t\t.get();\n\n\tif (!row?.started_at) return false;\n\ttry {\n\t\tconst lastAttempt = new Date(row.started_at).getTime();\n\t\tconst now = Date.now();\n\t\tconst elapsedS = (now - lastAttempt) / 1000;\n\t\treturn elapsedS < backoffS;\n\t} catch {\n\t\treturn false;\n\t}\n}\n","/**\n * Sync daemon: periodic background sync with all configured peers.\n *\n * Uses AbortSignal for cancellation and setInterval for periodic ticks.\n * Ported from codemem/sync/daemon.py — HTTP server portion is deferred\n * to the viewer-server Hono routes.\n */\n\nimport { sql } from \"drizzle-orm\";\nimport { drizzle } from \"drizzle-orm/better-sqlite3\";\nimport type { Database } from \"./db.js\";\nimport { connect as connectDb, resolveDbPath } from \"./db.js\";\nimport * as schema from \"./schema.js\";\nimport { advertiseMdns, mdnsEnabled } from \"./sync-discovery.js\";\nimport { ensureDeviceIdentity } from \"./sync-identity.js\";\nimport { runSyncPass, shouldSkipOfflinePeer, syncPassPreflight } from \"./sync-pass.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface SyncDaemonOptions {\n\thost?: string;\n\tport?: number;\n\tintervalS?: number;\n\tdbPath?: string;\n\tkeysDir?: string;\n\tsignal?: AbortSignal;\n}\n\nexport interface SyncTickResult {\n\tok: boolean;\n\tskipped?: boolean;\n\treason?: string;\n\terror?: string;\n\topsIn?: number;\n\topsOut?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Daemon state helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Record a successful daemon tick in sync_daemon_state.\n */\nexport function setSyncDaemonOk(db: Database): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncDaemonState)\n\t\t.values({ id: 1, last_ok_at: now })\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncDaemonState.id,\n\t\t\tset: { last_ok_at: sql`excluded.last_ok_at` },\n\t\t})\n\t\t.run();\n}\n\n/**\n * Record a daemon tick error in sync_daemon_state.\n */\nexport function setSyncDaemonError(db: Database, error: string, traceback?: string): void {\n\tconst d = drizzle(db, { schema });\n\tconst now = new Date().toISOString();\n\td.insert(schema.syncDaemonState)\n\t\t.values({\n\t\t\tid: 1,\n\t\t\tlast_error: error,\n\t\t\tlast_traceback: traceback ?? null,\n\t\t\tlast_error_at: now,\n\t\t})\n\t\t.onConflictDoUpdate({\n\t\t\ttarget: schema.syncDaemonState.id,\n\t\t\tset: {\n\t\t\t\tlast_error: sql`excluded.last_error`,\n\t\t\t\tlast_traceback: sql`excluded.last_traceback`,\n\t\t\t\tlast_error_at: sql`excluded.last_error_at`,\n\t\t\t},\n\t\t})\n\t\t.run();\n}\n\n// ---------------------------------------------------------------------------\n// Sync tick\n// ---------------------------------------------------------------------------\n\n/**\n * Run one sync tick: iterate over all enabled peers and sync each.\n *\n * Returns per-peer results. Peers in backoff are skipped.\n */\nexport async function syncDaemonTick(db: Database, keysDir?: string): Promise<SyncTickResult[]> {\n\tconst d = drizzle(db, { schema });\n\tconst rows = d\n\t\t.select({ peer_device_id: schema.syncPeers.peer_device_id })\n\t\t.from(schema.syncPeers)\n\t\t.all();\n\n\t// Skip heavy preflight work when there are no peers configured.\n\t// This keeps startup and idle daemon ticks responsive on large local stores.\n\tif (rows.length === 0) {\n\t\treturn [];\n\t}\n\n\tsyncPassPreflight(db);\n\n\tconst results: SyncTickResult[] = [];\n\tfor (const row of rows) {\n\t\tconst peerDeviceId = row.peer_device_id;\n\n\t\tif (shouldSkipOfflinePeer(db, peerDeviceId)) {\n\t\t\tresults.push({ ok: false, skipped: true, reason: \"peer offline (backoff)\" });\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst result = await runSyncPass(db, peerDeviceId, { keysDir });\n\t\tresults.push({\n\t\t\tok: result.ok,\n\t\t\terror: result.error,\n\t\t\topsIn: result.opsIn,\n\t\t\topsOut: result.opsOut,\n\t\t});\n\t}\n\n\treturn results;\n}\n\n// ---------------------------------------------------------------------------\n// Daemon loop\n// ---------------------------------------------------------------------------\n\n/**\n * Run the sync daemon loop.\n *\n * 1. Ensures device identity\n * 2. Starts mDNS advertising (if enabled)\n * 3. Runs an initial sync tick\n * 4. Sets up an interval timer for periodic sync\n * 5. Waits for abort signal to stop\n * 6. Cleans up on exit\n */\nexport async function runSyncDaemon(options?: SyncDaemonOptions): Promise<void> {\n\tconst intervalS = options?.intervalS ?? 120;\n\tconst dbPath = resolveDbPath(options?.dbPath);\n\tconst keysDir = options?.keysDir;\n\tconst signal = options?.signal;\n\n\t// Ensure device identity\n\tconst db = connectDb(dbPath);\n\tlet mdnsHandle: { close(): void } | null = null;\n\ttry {\n\t\tconst [deviceId] = ensureDeviceIdentity(db, { keysDir });\n\n\t\t// Start mDNS advertising if enabled\n\t\tif (mdnsEnabled() && options?.port) {\n\t\t\tmdnsHandle = advertiseMdns(deviceId, options.port);\n\t\t}\n\t} finally {\n\t\tdb.close();\n\t}\n\n\t// Check cancellation before startup tick\n\tif (signal?.aborted) {\n\t\tmdnsHandle?.close();\n\t\treturn;\n\t}\n\n\t// Run initial tick\n\tawait runTickOnce(dbPath, keysDir);\n\n\t// If aborted during initial tick, clean up\n\tif (signal?.aborted) {\n\t\tmdnsHandle?.close();\n\t\treturn;\n\t}\n\n\t// Set up periodic ticks — serialized to avoid overlapping sync passes\n\treturn new Promise<void>((resolve) => {\n\t\tlet tickRunning = false;\n\t\tconst timer = setInterval(() => {\n\t\t\tif (tickRunning) return; // Skip if previous tick still running\n\t\t\ttickRunning = true;\n\t\t\trunTickOnce(dbPath, keysDir).finally(() => {\n\t\t\t\ttickRunning = false;\n\t\t\t});\n\t\t}, intervalS * 1000);\n\n\t\tconst cleanup = () => {\n\t\t\tclearInterval(timer);\n\t\t\tmdnsHandle?.close();\n\t\t\tresolve();\n\t\t};\n\n\t\tif (signal) {\n\t\t\tif (signal.aborted) {\n\t\t\t\tcleanup();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsignal.addEventListener(\"abort\", cleanup, { once: true });\n\t\t}\n\t});\n}\n\n/**\n * Run a single tick, opening and closing a DB connection.\n *\n * Errors are caught and recorded in sync_daemon_state.\n */\nasync function runTickOnce(dbPath: string, keysDir?: string): Promise<void> {\n\tconst db = connectDb(dbPath);\n\ttry {\n\t\tawait syncDaemonTick(db, keysDir);\n\t\tsetSyncDaemonOk(db);\n\t} catch (err: unknown) {\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconst stack = err instanceof Error ? (err.stack ?? \"\") : \"\";\n\t\tsetSyncDaemonError(db, message, stack);\n\t} finally {\n\t\tdb.close();\n\t}\n}\n","/**\n * AUTO-GENERATED by scripts/generate-test-schema.ts\n *\n * Do not edit manually. Regenerate with:\n * pnpm --filter @codemem/core run generate:test-schema\n */\n\nexport const TEST_SCHEMA_BASE_DDL =\n\t\"CREATE TABLE IF NOT EXISTS `sessions` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`started_at` text NOT NULL,\\n\\t`ended_at` text,\\n\\t`cwd` text,\\n\\t`project` text,\\n\\t`git_remote` text,\\n\\t`git_branch` text,\\n\\t`user` text,\\n\\t`tool_version` text,\\n\\t`metadata_json` text,\\n\\t`import_key` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `artifacts` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer NOT NULL,\\n\\t`kind` text NOT NULL,\\n\\t`path` text,\\n\\t`content_text` text,\\n\\t`content_hash` text,\\n\\t`created_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_artifacts_session_kind` ON `artifacts` (`session_id`,`kind`);\\nCREATE TABLE IF NOT EXISTS `memory_items` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer NOT NULL,\\n\\t`kind` text NOT NULL,\\n\\t`title` text NOT NULL,\\n\\t`subtitle` text,\\n\\t`body_text` text NOT NULL,\\n\\t`confidence` real DEFAULT 0.5,\\n\\t`tags_text` text DEFAULT '',\\n\\t`active` integer DEFAULT 1,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\t`actor_id` text,\\n\\t`actor_display_name` text,\\n\\t`visibility` text,\\n\\t`workspace_id` text,\\n\\t`workspace_kind` text,\\n\\t`origin_device_id` text,\\n\\t`origin_source` text,\\n\\t`trust_state` text,\\n\\t`facts` text,\\n\\t`narrative` text,\\n\\t`concepts` text,\\n\\t`files_read` text,\\n\\t`files_modified` text,\\n\\t`user_prompt_id` integer,\\n\\t`prompt_number` integer,\\n\\t`deleted_at` text,\\n\\t`rev` integer DEFAULT 0,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_memory_items_active_created` ON `memory_items` (`active`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_memory_items_session` ON `memory_items` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `usage_events` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`event` text NOT NULL,\\n\\t`tokens_read` integer DEFAULT 0,\\n\\t`tokens_written` integer DEFAULT 0,\\n\\t`tokens_saved` integer DEFAULT 0,\\n\\t`created_at` text NOT NULL,\\n\\t`metadata_json` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE set null\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_usage_events_event_created` ON `usage_events` (`event`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_usage_events_session` ON `usage_events` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `raw_events` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`event_id` text,\\n\\t`event_seq` integer NOT NULL,\\n\\t`event_type` text NOT NULL,\\n\\t`ts_wall_ms` integer,\\n\\t`ts_mono_ms` real,\\n\\t`payload_json` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_raw_events_source_stream_seq` ON `raw_events` (`source`,`stream_id`,`event_seq`);\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_raw_events_source_stream_event_id` ON `raw_events` (`source`,`stream_id`,`event_id`);\\nCREATE INDEX IF NOT EXISTS `idx_raw_events_session_seq` ON `raw_events` (`opencode_session_id`,`event_seq`);\\nCREATE INDEX IF NOT EXISTS `idx_raw_events_created` ON `raw_events` (`created_at`);\\nCREATE TABLE IF NOT EXISTS `raw_event_sessions` (\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`cwd` text,\\n\\t`project` text,\\n\\t`started_at` text,\\n\\t`last_seen_ts_wall_ms` integer,\\n\\t`last_received_event_seq` integer DEFAULT -1 NOT NULL,\\n\\t`last_flushed_event_seq` integer DEFAULT -1 NOT NULL,\\n\\t`updated_at` text NOT NULL,\\n\\tPRIMARY KEY(`source`, `stream_id`)\\n);\\n\\nCREATE TABLE IF NOT EXISTS `opencode_sessions` (\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`session_id` integer,\\n\\t`created_at` text NOT NULL,\\n\\tPRIMARY KEY(`source`, `stream_id`),\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_opencode_sessions_session` ON `opencode_sessions` (`session_id`);\\nCREATE TABLE IF NOT EXISTS `raw_event_flush_batches` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`source` text DEFAULT 'opencode' NOT NULL,\\n\\t`stream_id` text DEFAULT '' NOT NULL,\\n\\t`opencode_session_id` text NOT NULL,\\n\\t`start_event_seq` integer NOT NULL,\\n\\t`end_event_seq` integer NOT NULL,\\n\\t`extractor_version` text NOT NULL,\\n\\t`status` text NOT NULL,\\n\\t`error_message` text,\\n\\t`error_type` text,\\n\\t`observer_provider` text,\\n\\t`observer_model` text,\\n\\t`observer_runtime` text,\\n\\t`attempt_count` integer DEFAULT 0 NOT NULL,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE UNIQUE INDEX IF NOT EXISTS `idx_flush_batches_source_stream_seq_ver` ON `raw_event_flush_batches` (`source`,`stream_id`,`start_event_seq`,`end_event_seq`,`extractor_version`);\\nCREATE INDEX IF NOT EXISTS `idx_flush_batches_session_created` ON `raw_event_flush_batches` (`opencode_session_id`,`created_at`);\\nCREATE INDEX IF NOT EXISTS `idx_flush_batches_status_updated` ON `raw_event_flush_batches` (`status`,`updated_at`);\\nCREATE TABLE IF NOT EXISTS `user_prompts` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`project` text,\\n\\t`prompt_text` text NOT NULL,\\n\\t`prompt_number` integer,\\n\\t`created_at` text NOT NULL,\\n\\t`created_at_epoch` integer NOT NULL,\\n\\t`metadata_json` text,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_session` ON `user_prompts` (`session_id`);\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_project` ON `user_prompts` (`project`);\\nCREATE INDEX IF NOT EXISTS `idx_user_prompts_epoch` ON `user_prompts` (`created_at_epoch`);\\nCREATE TABLE IF NOT EXISTS `session_summaries` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`session_id` integer,\\n\\t`project` text,\\n\\t`request` text,\\n\\t`investigated` text,\\n\\t`learned` text,\\n\\t`completed` text,\\n\\t`next_steps` text,\\n\\t`notes` text,\\n\\t`files_read` text,\\n\\t`files_edited` text,\\n\\t`prompt_number` integer,\\n\\t`created_at` text NOT NULL,\\n\\t`created_at_epoch` integer NOT NULL,\\n\\t`metadata_json` text,\\n\\t`import_key` text,\\n\\tFOREIGN KEY (`session_id`) REFERENCES `sessions`(`id`) ON UPDATE no action ON DELETE cascade\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_session` ON `session_summaries` (`session_id`);\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_project` ON `session_summaries` (`project`);\\nCREATE INDEX IF NOT EXISTS `idx_session_summaries_epoch` ON `session_summaries` (`created_at_epoch`);\\nCREATE TABLE IF NOT EXISTS `replication_ops` (\\n\\t`op_id` text PRIMARY KEY NOT NULL,\\n\\t`entity_type` text NOT NULL,\\n\\t`entity_id` text NOT NULL,\\n\\t`op_type` text NOT NULL,\\n\\t`payload_json` text,\\n\\t`clock_rev` integer NOT NULL,\\n\\t`clock_updated_at` text NOT NULL,\\n\\t`clock_device_id` text NOT NULL,\\n\\t`device_id` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_replication_ops_created` ON `replication_ops` (`created_at`,`op_id`);\\nCREATE INDEX IF NOT EXISTS `idx_replication_ops_entity` ON `replication_ops` (`entity_type`,`entity_id`);\\nCREATE TABLE IF NOT EXISTS `replication_cursors` (\\n\\t`peer_device_id` text PRIMARY KEY NOT NULL,\\n\\t`last_applied_cursor` text,\\n\\t`last_acked_cursor` text,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_peers` (\\n\\t`peer_device_id` text PRIMARY KEY NOT NULL,\\n\\t`name` text,\\n\\t`pinned_fingerprint` text,\\n\\t`public_key` text,\\n\\t`addresses_json` text,\\n\\t`claimed_local_actor` integer DEFAULT 0 NOT NULL,\\n\\t`actor_id` text,\\n\\t`projects_include_json` text,\\n\\t`projects_exclude_json` text,\\n\\t`created_at` text NOT NULL,\\n\\t`last_seen_at` text,\\n\\t`last_sync_at` text,\\n\\t`last_error` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_nonces` (\\n\\t`nonce` text PRIMARY KEY NOT NULL,\\n\\t`device_id` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_device` (\\n\\t`device_id` text PRIMARY KEY NOT NULL,\\n\\t`public_key` text NOT NULL,\\n\\t`fingerprint` text NOT NULL,\\n\\t`created_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `sync_attempts` (\\n\\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\\n\\t`peer_device_id` text NOT NULL,\\n\\t`started_at` text NOT NULL,\\n\\t`finished_at` text,\\n\\t`ok` integer NOT NULL,\\n\\t`ops_in` integer NOT NULL,\\n\\t`ops_out` integer NOT NULL,\\n\\t`error` text\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_sync_attempts_peer_started` ON `sync_attempts` (`peer_device_id`,`started_at`);\\nCREATE TABLE IF NOT EXISTS `sync_daemon_state` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`last_error` text,\\n\\t`last_traceback` text,\\n\\t`last_error_at` text,\\n\\t`last_ok_at` text\\n);\\n\\nCREATE TABLE IF NOT EXISTS `raw_event_ingest_samples` (\\n\\t`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,\\n\\t`created_at` text NOT NULL,\\n\\t`inserted_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_invalid` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_duplicate` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_conflict` integer DEFAULT 0 NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `raw_event_ingest_stats` (\\n\\t`id` integer PRIMARY KEY NOT NULL,\\n\\t`inserted_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_events` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_invalid` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_duplicate` integer DEFAULT 0 NOT NULL,\\n\\t`skipped_conflict` integer DEFAULT 0 NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE TABLE IF NOT EXISTS `actors` (\\n\\t`actor_id` text PRIMARY KEY NOT NULL,\\n\\t`display_name` text NOT NULL,\\n\\t`is_local` integer DEFAULT 0 NOT NULL,\\n\\t`status` text DEFAULT 'active' NOT NULL,\\n\\t`merged_into_actor_id` text,\\n\\t`created_at` text NOT NULL,\\n\\t`updated_at` text NOT NULL\\n);\\n\\nCREATE INDEX IF NOT EXISTS `idx_actors_is_local` ON `actors` (`is_local`);\\nCREATE INDEX IF NOT EXISTS `idx_actors_status` ON `actors` (`status`);\";\n","/**\n * Test utilities for the codemem TS backend.\n */\n\nimport type { Database } from \"./db.js\";\nimport { SCHEMA_VERSION } from \"./db.js\";\nimport { TEST_SCHEMA_BASE_DDL } from \"./test-schema.generated.js\";\n\nconst TEST_SCHEMA_AUX_DDL = `\nCREATE INDEX IF NOT EXISTS idx_sync_peers_actor_id ON sync_peers(actor_id);\n\nCREATE VIRTUAL TABLE IF NOT EXISTS memory_fts USING fts5(\n\ttitle, body_text, tags_text,\n\tcontent='memory_items',\n\tcontent_rowid='id'\n);\n\nCREATE TRIGGER IF NOT EXISTS memory_items_ai AFTER INSERT ON memory_items BEGIN\n\tINSERT INTO memory_fts(rowid, title, body_text, tags_text)\n\tVALUES (new.id, new.title, new.body_text, new.tags_text);\nEND;\n\nDROP TRIGGER IF EXISTS memory_items_au;\nCREATE TRIGGER memory_items_au AFTER UPDATE ON memory_items BEGIN\n\tINSERT INTO memory_fts(memory_fts, rowid, title, body_text, tags_text)\n\tVALUES('delete', old.id, old.title, old.body_text, old.tags_text);\n\tINSERT INTO memory_fts(rowid, title, body_text, tags_text)\n\tVALUES (new.id, new.title, new.body_text, new.tags_text);\nEND;\n\nDROP TRIGGER IF EXISTS memory_items_ad;\nCREATE TRIGGER memory_items_ad AFTER DELETE ON memory_items BEGIN\n\tINSERT INTO memory_fts(memory_fts, rowid, title, body_text, tags_text)\n\tVALUES('delete', old.id, old.title, old.body_text, old.tags_text);\nEND;\n`;\n\n/**\n * Create the full schema for test databases.\n */\nexport function initTestSchema(db: Database): void {\n\tdb.exec(TEST_SCHEMA_BASE_DDL);\n\tdb.exec(TEST_SCHEMA_AUX_DDL);\n\tdb.pragma(`user_version = ${SCHEMA_VERSION}`);\n}\n\n/**\n * Insert a minimal test session and return its ID.\n */\nexport function insertTestSession(db: Database): number {\n\tconst now = new Date().toISOString();\n\tconst info = db\n\t\t.prepare(\n\t\t\t\"INSERT INTO sessions(started_at, cwd, project, user, tool_version) VALUES (?, ?, ?, ?, ?)\",\n\t\t)\n\t\t.run(now, \"/tmp/test\", \"test-project\", \"test-user\", \"test\");\n\treturn Number(info.lastInsertRowid);\n}\n\n// Re-export for test convenience\nexport { MemoryStore } from \"./store.js\";\n","/**\n * @codemem/core — store, embeddings, and shared types.\n *\n * This package owns the SQLite store, embedding worker interface,\n * and type definitions shared across the codemem TS backend.\n */\n\nexport const VERSION = \"0.20.4\";\n\nexport * as Api from \"./api-types.js\";\nexport type { ClaudeHookAdapterEvent, ClaudeHookRawEventEnvelope } from \"./claude-hooks.js\";\nexport {\n\tbuildIngestPayloadFromHook,\n\tbuildRawEventEnvelopeFromHook,\n\tMAPPABLE_CLAUDE_HOOK_EVENTS,\n\tmapClaudeHookPayload,\n\tnormalizeProjectLabel,\n\tresolveHookProject,\n} from \"./claude-hooks.js\";\nexport {\n\tcoordinatorCreateInviteAction,\n\tcoordinatorImportInviteAction,\n\tcoordinatorListJoinRequestsAction,\n\tcoordinatorReviewJoinRequestAction,\n} from \"./coordinator-actions.js\";\nexport { createCoordinatorApp } from \"./coordinator-api.js\";\nexport type { InvitePayload } from \"./coordinator-invites.js\";\nexport {\n\tdecodeInvitePayload,\n\tencodeInvitePayload,\n\textractInvitePayload,\n\tinviteLink,\n} from \"./coordinator-invites.js\";\nexport {\n\tcoordinatorEnabled,\n\tcoordinatorStatusSnapshot,\n\tlistCoordinatorJoinRequests,\n\tlookupCoordinatorPeers,\n\treadCoordinatorSyncConfig,\n\tregisterCoordinatorPresence,\n} from \"./coordinator-runtime.js\";\nexport {\n\tCoordinatorStore,\n\tconnectCoordinator,\n\tDEFAULT_COORDINATOR_DB_PATH,\n} from \"./coordinator-store.js\";\nexport type { Database } from \"./db.js\";\nexport {\n\tassertSchemaReady,\n\tbackupOnFirstAccess,\n\tconnect,\n\tDEFAULT_DB_PATH,\n\tfromJson,\n\tfromJsonStrict,\n\tgetSchemaVersion,\n\tisEmbeddingDisabled,\n\tloadSqliteVec,\n\tMIN_COMPATIBLE_SCHEMA,\n\tmigrateLegacyDbPath,\n\tresolveDbPath,\n\tSCHEMA_VERSION,\n\ttableExists,\n\ttoJson,\n\ttoJsonNullable,\n} from \"./db.js\";\nexport type { EmbeddingClient } from \"./embeddings.js\";\nexport {\n\t_resetEmbeddingClient,\n\tchunkText,\n\tembedTexts,\n\tgetEmbeddingClient,\n\thashText,\n\tserializeFloat32,\n} from \"./embeddings.js\";\nexport type { ExportOptions, ExportPayload, ImportOptions, ImportResult } from \"./export-import.js\";\nexport {\n\tbuildImportKey,\n\texportMemories,\n\timportMemories,\n\tmergeSummaryMetadata,\n\treadImportPayload,\n} from \"./export-import.js\";\nexport { buildFilterClauses, buildFilterClausesWithContext } from \"./filters.js\";\n// Ingest pipeline\nexport {\n\tbudgetToolEvents,\n\teventToToolEvent,\n\textractAdapterEvent,\n\textractToolEvents,\n\tisInternalMemoryTool,\n\tLOW_SIGNAL_TOOLS,\n\tnormalizeToolName,\n\tprojectAdapterToolEvent,\n} from \"./ingest-events.js\";\nexport { isLowSignalObservation, normalizeObservation } from \"./ingest-filters.js\";\nexport type { IngestOptions } from \"./ingest-pipeline.js\";\nexport { cleanOrphanSessions, ingest, main as ingestMain } from \"./ingest-pipeline.js\";\nexport { buildObserverPrompt } from \"./ingest-prompts.js\";\nexport {\n\tisSensitiveFieldName,\n\tsanitizePayload,\n\tsanitizeToolOutput,\n\tstripPrivate,\n\tstripPrivateObj,\n} from \"./ingest-sanitize.js\";\nexport {\n\tbuildTranscript,\n\tderiveRequest,\n\textractAssistantMessages,\n\textractAssistantUsage,\n\textractPrompts,\n\tfirstSentence,\n\tisTrivialRequest,\n\tnormalizeAdapterEvents,\n\tnormalizeRequestText,\n\tTRIVIAL_REQUESTS,\n} from \"./ingest-transcript.js\";\nexport type {\n\tIngestPayload,\n\tObserverContext,\n\tParsedObservation,\n\tParsedOutput,\n\tParsedSummary,\n\tSessionContext,\n\tToolEvent,\n} from \"./ingest-types.js\";\nexport { hasMeaningfulObservation, parseObserverResponse } from \"./ingest-xml-parser.js\";\nexport { parsePositiveMemoryId, parseStrictInteger } from \"./integers.js\";\nexport type {\n\tBackfillTagsTextOptions,\n\tBackfillTagsTextResult,\n\tDeactivateLowSignalMemoriesOptions,\n\tDeactivateLowSignalResult,\n\tGateResult,\n\tRawEventStatusItem,\n\tRawEventStatusResult,\n\tReliabilityMetrics,\n} from \"./maintenance.js\";\nexport {\n\tbackfillTagsText,\n\tdeactivateLowSignalMemories,\n\tdeactivateLowSignalObservations,\n\tgetRawEventStatus,\n\tgetReliabilityMetrics,\n\tinitDatabase,\n\trawEventsGate,\n\tretryRawEventFailures,\n\tvacuumDatabase,\n} from \"./maintenance.js\";\nexport type { ObserverAuthMaterial } from \"./observer-auth.js\";\nexport {\n\tbuildCodexHeaders,\n\textractOAuthAccess,\n\textractOAuthAccountId,\n\textractOAuthExpires,\n\textractProviderApiKey,\n\tloadOpenCodeOAuthCache,\n\tObserverAuthAdapter,\n\tprobeAvailableCredentials,\n\treadAuthFile,\n\tredactText,\n\trenderObserverHeaders,\n\tresolveOAuthProvider,\n\trunAuthCommand,\n} from \"./observer-auth.js\";\nexport type { ObserverConfig, ObserverResponse, ObserverStatus } from \"./observer-client.js\";\nexport { loadObserverConfig, ObserverAuthError, ObserverClient } from \"./observer-client.js\";\nexport {\n\tCODEMEM_CONFIG_ENV_OVERRIDES,\n\tgetCodememConfigPath,\n\tgetCodememEnvOverrides,\n\tgetOpenCodeProviderConfig,\n\tgetProviderApiKey,\n\tgetProviderBaseUrl,\n\tgetProviderHeaders,\n\tgetProviderOptions,\n\tlistConfiguredOpenCodeProviders,\n\tlistCustomProviders,\n\tlistObserverProviderOptions,\n\tloadOpenCodeConfig,\n\treadCodememConfigFile,\n\tresolveBuiltInProviderDefaultModel,\n\tresolveBuiltInProviderFromModel,\n\tresolveBuiltInProviderModel,\n\tresolveCustomProviderDefaultModel,\n\tresolveCustomProviderFromModel,\n\tresolveCustomProviderModel,\n\tresolvePlaceholder,\n\tstripJsonComments,\n\tstripTrailingCommas,\n\twriteCodememConfigFile,\n} from \"./observer-config.js\";\nexport { buildMemoryPack, buildMemoryPackAsync, estimateTokens } from \"./pack.js\";\nexport {\n\tprojectBasename,\n\tprojectClause,\n\tprojectColumnClause,\n\tprojectMatchesFilter,\n\tresolveProject,\n} from \"./project.js\";\nexport type { FlushRawEventsOptions } from \"./raw-event-flush.js\";\nexport { buildSessionContext, flushRawEvents } from \"./raw-event-flush.js\";\nexport { RawEventSweeper } from \"./raw-event-sweeper.js\";\nexport * as schema from \"./schema.js\";\nexport type { StoreHandle } from \"./search.js\";\nexport {\n\tdedupeOrderedIds,\n\texpandQuery,\n\texplain,\n\tkindBonus,\n\trecencyScore,\n\trerankResults,\n\tsearch,\n\ttimeline,\n} from \"./search.js\";\nexport { MemoryStore } from \"./store.js\";\nexport type {\n\tBuildAuthHeadersOptions,\n\tSignRequestOptions,\n\tVerifySignatureOptions,\n} from \"./sync-auth.js\";\nexport {\n\tbuildAuthHeaders,\n\tbuildCanonicalRequest,\n\tcleanupNonces,\n\tDEFAULT_TIME_WINDOW_S,\n\trecordNonce,\n\tSIGNATURE_VERSION,\n\tsignRequest,\n\tverifySignature,\n} from \"./sync-auth.js\";\nexport type { SyncDaemonOptions, SyncTickResult } from \"./sync-daemon.js\";\nexport {\n\trunSyncDaemon,\n\tsetSyncDaemonError,\n\tsetSyncDaemonOk,\n\tsyncDaemonTick,\n} from \"./sync-daemon.js\";\nexport type { MdnsEntry } from \"./sync-discovery.js\";\nexport {\n\taddressDedupeKey,\n\tadvertiseMdns,\n\tDEFAULT_SERVICE_TYPE,\n\tdiscoverPeersViaMdns,\n\tloadPeerAddresses,\n\tmdnsAddressesForPeer,\n\tmdnsEnabled,\n\tmergeAddresses,\n\tnormalizeAddress,\n\trecordPeerSuccess,\n\trecordSyncAttempt,\n\tselectDialAddresses,\n\tsetPeerLocalActorClaim,\n\tsetPeerProjectFilter,\n\tupdatePeerAddresses,\n} from \"./sync-discovery.js\";\nexport type { RequestJsonOptions } from \"./sync-http-client.js\";\nexport { buildBaseUrl, requestJson } from \"./sync-http-client.js\";\nexport type { EnsureDeviceIdentityOptions } from \"./sync-identity.js\";\nexport {\n\tensureDeviceIdentity,\n\tfingerprintPublicKey,\n\tgenerateKeypair,\n\tloadPrivateKey,\n\tloadPrivateKeyKeychain,\n\tloadPublicKey,\n\tresolveKeyPaths,\n\tstorePrivateKeyKeychain,\n\tvalidateExistingKeypair,\n} from \"./sync-identity.js\";\nexport type { SyncPassOptions, SyncResult } from \"./sync-pass.js\";\nexport {\n\tconsecutiveConnectivityFailures,\n\tcursorAdvances,\n\tisConnectivityError,\n\tpeerBackoffSeconds,\n\trunSyncPass,\n\tshouldSkipOfflinePeer,\n\tsyncOnce,\n\tsyncPassPreflight,\n} from \"./sync-pass.js\";\nexport type { ApplyResult } from \"./sync-replication.js\";\nexport {\n\tapplyReplicationOps,\n\tbackfillReplicationOps,\n\tchunkOpsBySize,\n\tclockTuple,\n\textractReplicationOps,\n\tfilterReplicationOpsForSync,\n\tfilterReplicationOpsForSyncWithStatus,\n\tgetReplicationCursor,\n\tisNewerClock,\n\tloadReplicationOpsSince,\n\tmigrateLegacyImportKeys,\n\trecordReplicationOp,\n\tsetReplicationCursor,\n} from \"./sync-replication.js\";\nexport { deriveTags, fileTags, normalizeTag } from \"./tags.js\";\n// Test utilities (exported for consumer packages like viewer-server)\nexport { initTestSchema, insertTestSession } from \"./test-utils.js\";\nexport type {\n\tActor,\n\tArtifact,\n\tExplainError,\n\tExplainItem,\n\tExplainResponse,\n\tExplainScoreComponents,\n\tMemoryFilters,\n\tMemoryItem,\n\tMemoryItemResponse,\n\tMemoryResult,\n\tOpenCodeSession,\n\tPackItem,\n\tPackResponse,\n\tRawEvent,\n\tRawEventFlushBatch,\n\tRawEventIngestSample,\n\tRawEventIngestStats,\n\tRawEventSession,\n\tReplicationClock,\n\tReplicationCursor,\n\tReplicationOp,\n\tSession,\n\tSessionSummary,\n\tStoreStats,\n\tSyncAttempt,\n\tSyncDaemonState,\n\tSyncDevice,\n\tSyncNonce,\n\tSyncPeer,\n\tTimelineItemResponse,\n\tUsageEvent,\n\tUserPrompt,\n} from \"./types.js\";\nexport type {\n\tBackfillVectorsOptions,\n\tBackfillVectorsResult,\n\tSemanticSearchResult,\n} from \"./vectors.js\";\nexport { backfillVectors, semanticSearch, storeVectors } from \"./vectors.js\";\n"],"x_google_ignoreList":[13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"mappings":"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"}